Swift - 选择 TableView 单元格时的动画 (didSelectRowAtIndexPath)
Swift - Animating When TableView Cell Selected (didSelectRowAtIndexPath)
我一直在尝试在我的 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)
}
}
首先,不要在这里使用dequeueReusableCellWithIdentifier
。它将使用屏幕上不可见的单元格并准备显示它。您想要的是 cellForRowAtIndexPath
给定 indexPath
.
屏幕上已经存在的 returns 单元格
然后我明白你想玩 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
class。例如,使用 selected
属性和 setSelected(animated: Bool)
来直观地处理状态变化。
在 Tanguy 的帮助下,这就是我们的目标。我仍然遇到一些动画问题,因为 table 本身实际上不会为下面的下一层单元格设置动画,但我会改进该代码并使其正常工作。认为这适合现在的目的,因此值得发布以展示解决方案本身。谢谢!!
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)
}
}
您不需要使用 UIView.animate(...)
。 UITableView
默认提供动画更新。更改约束后,您可以通过调用来实现:
tableView.performBatchUpdates(nil, completion: nil)
在创建身高时,请不要忘记将身高 属性 设置为低于 .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)
}
我一直在尝试在我的 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)
}
}
首先,不要在这里使用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
class。例如,使用 selected
属性和 setSelected(animated: Bool)
来直观地处理状态变化。
在 Tanguy 的帮助下,这就是我们的目标。我仍然遇到一些动画问题,因为 table 本身实际上不会为下面的下一层单元格设置动画,但我会改进该代码并使其正常工作。认为这适合现在的目的,因此值得发布以展示解决方案本身。谢谢!!
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)
}
}
您不需要使用 UIView.animate(...)
。 UITableView
默认提供动画更新。更改约束后,您可以通过调用来实现:
tableView.performBatchUpdates(nil, completion: nil)
在创建身高时,请不要忘记将身高 属性 设置为低于 .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)
}