Swift - 选择 TableView 单元格时动画(didSelectRowAtIndexPath)
Posted
技术标签:
【中文标题】Swift - 选择 TableView 单元格时动画(didSelectRowAtIndexPath)【英文标题】:Swift - Animating When TableView Cell Selected (didSelectRowAtIndexPath) 【发布时间】:2016-03-23 13:46:17 【问题描述】:当我的 tableviewcell 被选中时,我一直在尝试为它的内容设置动画,但我遇到了问题。我读过一些你不能在 didSelectRowAtIndexPath 中运行 animatewithduration 的地方,或者至少如果你这样做它不会动画。我下面的代码似乎就是这种情况,视图移动但没有动画。
我已尝试将逻辑合并到 willDisplayCell 以监视选择,但无济于事,因此,如果有人能够为我找到正确的解决方案,我会很高兴,因为尽管进行了大量搜索,但似乎没有答案:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
let currentCellDescriptor = getCellDescriptorForIndexPath(indexPath)
let cell = tableView.dequeueReusableCellWithIdentifier(currentCellDescriptor["cellIdentifier"] as! String, forIndexPath: indexPath) as! CustomCell
let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row]
if cellDescriptors[indexPath.section][indexOfTappedRow]["isExpandable"] as! Bool == true
if cellDescriptors[indexPath.section][indexOfTappedRow]["isExpanded"] as! Bool == false
// INTERESTING BIT: Animates cell contents to Right
if currentCellDescriptor["cellIdentifier"] as! String == "CellHeader"
UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 12, options: .CurveLinear, animations:
cell.headerTitleLeftConstraint.priority = 999
cell.headerTitleRightConstraint.priority = 1
cell.layoutIfNeeded()
, completion: nil)
else if cellDescriptors[indexPath.section][indexOfTappedRow]["isExpanded"] as! Bool == true
// MARK: Animates cell contents back to left
if currentCellDescriptor["cellIdentifier"] as! String == "CellHeader"
UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 12, options: .CurveLinear, animations:
cell.headerTitleLeftConstraint.priority = 1
cell.headerTitleRightConstraint.priority = 999
cell.layoutIfNeeded()
, completion: nil)
【问题讨论】:
你为什么用'let cell = tableView.dequeueReusableCellWithIdentifier(currentCellDescriptor["cellIdentifier"] as!String, forIndexPath: indexPath) as! 'didSelectRowAtIndexPath' 中的 CustomCell' ? Vader 先生,这是因为我有一个包含所有单元格数据的 plist。这样做要容易得多,因为我有多个可折叠级别的单元格。 【参考方案1】:首先,不要在这里使用dequeueReusableCellWithIdentifier
。它将使用屏幕上不可见的单元格并准备显示它。你想要的是cellForRowAtIndexPath
,它返回一个已经在屏幕上给定indexPath
的单元格。
然后我知道您想使用 2 个约束并为布局更改设置动画。为此,动画应该只包含layoutIfNeeded
:
cell.headerTitleLeftConstraint.priority = 999
cell.headerTitleRightConstraint.priority = 1
UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 12, options: .CurveLinear, animations:
cell.layoutIfNeeded()
, completion: nil)
我还建议您将此逻辑从控制器转移到您的 CustomCell
类。例如,使用selected
属性和setSelected(animated: Bool)
来直观地处理状态变化。
【讨论】:
【参考方案2】:在 Tanguy 的帮助下,这就是我们目前的情况。我仍然有一些动画问题,因为表格本身实际上不会为下面的下一级单元格设置动画,但我会改进该代码并使其正常工作。认为这现在适合目的,因此值得发布以展示解决方案本身。谢谢!!
override func setSelected(selected: Bool, animated: Bool)
super.setSelected(selected, animated: animated)
// Adjusts constraints everytime switch is tapped
isLeftAligned = !isLeftAligned
if userHanded == "Right"
UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 12, options: .CurveLinear, animations:
self.leftHandedHeaderConstraint.priority = (self.isLeftAligned) ? 1 : 999
self.rightHandedHeaderConstraint.priority = (self.isLeftAligned) ? 999 : 1
self.layoutIfNeeded()
, completion: nil)
else if userHanded == "Left"
UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 12, options: .CurveLinear, animations:
self.leftHandedHeaderConstraint.priority = (self.isLeftAligned) ? 999 : 1
self.rightHandedHeaderConstraint.priority = (self.isLeftAligned) ? 1 : 999
self.layoutIfNeeded()
, completion: nil)
【讨论】:
P.S.其他人请注意确保将 tableView 设置为 MultipleSelection 以便并非所有相同类型的单元格都动画:)【参考方案3】:您不需要使用UIView.animate(...)
。 UITableView
默认提供动画更新。更改约束后,您可以通过调用来实现:
tableView.performBatchUpdates(nil, completion: nil)
在创建 height 属性时,不要忘记赋予它比.required
更低的优先级。否则 AutoLayout 将在更新时打破约束(您可以在调试器中观察到这一点)。而不是将约束的优先级设置为999
或类似的东西,您应该使用UILayoutPriority
的静态属性以获得更易读的代码。
/// Set this wherever you create your height constraint, e.g. in your custom cell's class.
heightConstraint?.priority = .defaultHigh
didSelectRowAt
的完整代码如下所示:
let expandedHeight: CGFloat = 500
let defaultHeight: CGFloat = 85
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
guard let cell = tableView.cellForRow(at: indexPath) as? YourCustomTableViewCell else return
/// Save the status of your cell in a property of your view model.
if viewModel[indexPath.row].isFullsize
cell.heightConstraint?.constant = defaultHeight
viewModel[indexPath.row].isFullsize = false
else
cell.heightConstraint?.constant = expandedHeight
viewModel[indexPath.row].isFullsize = true
/// Call this for an animated update (since ios 11)
tableView.performBatchUpdates(nil, completion: nil)
/// For the same result you could also use
/// tableView.beginUpdates()
/// tableView.endUpdates()
/// ...but the docs say that this will become deprecated soon
/// Scrolls to your selected cell's top
tableView.scrollToRow(at: indexPath, at: .top, animated: true)
【讨论】:
以上是关于Swift - 选择 TableView 单元格时动画(didSelectRowAtIndexPath)的主要内容,如果未能解决你的问题,请参考以下文章
重用自定义 tableView 单元格时重复显示相同的子视图(Swift 4.1)
当我在按钮操作上从 TableView 中删除单元格时,应用程序崩溃了 - iOS Swift