动画自定义 UITableViewCell 时的计时问题
Timing issue when animating custom UITableViewCell
我的应用程序架构有问题...
我有一个 UITableView
装满了自定义 UITableViewCells
。
当然,我使用了出列,所以只生成了 8-10 个单元格实例。
继续我的问题...我已将主题功能添加到我的应用程序中。
当用户长时间触摸主 UINavigationBar
时,会在应用程序范围内发布一条通知,通知每个 viewController 更新他们的 UI。
当 viewController 托管带有自定义单元格的 tableView 收到通知时,它会调用 tableView.reloadData()
效果很好,因为我已经实现了func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
在 willDisplay
中,我在我的自定义 tableViewCell 上调用了一个方法,该方法使用 UIViewAnimation
对单元格进行适当的颜色更改。
看起来像这样:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if let newsFeedItemCell = cell as? NewsFeedItemCell {
if (self.nightModeEnabled) {
newsFeedItemCell.transitionToNightMode()
} else {
newsFeedItemCell.transitionFromNightMode()
}
}
}
在自定义单元格实现中,这些方法如下所示:
func transitionToNightMode() {
UIView.animate(withDuration: 1, animations: {
self.backgroundColor = UIColor.black
self.titleTextView.backgroundColor = UIColor.black
self.titleTextView.textColor = UIColor.white
})
}
func transitionFromNightMode() {
UIView.animate(withDuration: 1, animations: {
self.backgroundColor = UIColor.white
self.titleTextView.backgroundColor = UIColor.white
self.titleTextView.textColor = UIColor.black
})
}
这很好用...这是问题所在:
在滚动 tableView 时,任何不在屏幕上的单元格 在滚动到屏幕上时,它们的颜色 update/animation 代码称为 ,这会导致不和谐的用户体验。
我当然理解为什么会这样,因为 willDisplay
仅在单元格显示时被调用。
我想不出避免这种情况的优雅方法。
我很高兴屏幕上 的单元格 正在为用户体验带来愉悦的动画效果,但是,对于屏幕外的单元格,我宁愿它们 跳过 动画。
可能的解决方案(虽然不雅):
保留对 cellForRow
创建的 8-10 个单元格中的每一个的引用,并检查它们是否在屏幕外,是否立即设置了它们的状态。
但是,我不喜欢保留对每个单元格的引用的想法。
有什么想法吗?
我会尝试以下方法。
我不会在 transition(To/From)NightMode
中使用 UIView.animate
,而是创建一个可以执行相同动画的 UIViewPropertyAnimator
对象。我会保留对该对象的引用,然后准备重用,如果动画仍然是 运行,我会简单地完成该动画并重置状态。所以像这样:
fileprivate var transitionAnimator: UIViewPropertyAnimator?
fileprivate func finishAnimatorAnimation() {
// if there is a transitionAnimator, immediately finishes it
if let animator = transitionAnimator {
animator.stopAnimation(false)
animator.finishAnimation(at: .end)
transitionAnimator = nil
}
}
override func prepareForReuse() {
super.prepareForReuse()
finishAnimatorAnimation()
}
func transitionToNightMode() {
transitionAnimator = UIViewPropertyAnimator(duration: 1, curve: .easeInOut, animations: {
self.backgroundColor = UIColor.black
self.titleTextView.backgroundColor = UIColor.black
self.titleTextView.textColor = UIColor.white
})
transitionAnimator?.startAnimation()
}
func transitionFromNightMode() {
transitionAnimator = UIViewPropertyAnimator(duration: 1, curve: .easeInOut, animations: {
self.backgroundColor = UIColor.white
self.titleTextView.backgroundColor = UIColor.white
self.titleTextView.textColor = UIColor.black
})
transitionAnimator?.startAnimation()
}
我不会使用 reloadData
对此进行动画处理,因为您的 tableview 数据模型实际上并没有改变。
相反,我会给 UITableView 一个函数,如下所示:
class myClass : UITableViewController {
....
func transitionToNightMode() {
for visible in visibleCells {
UIView.animate(withDuration: 1, animations: {
visible.backgroundColor = UIColor.black
visible.titleTextView.backgroundColor = UIColor.black
visible.titleTextView.textColor = UIColor.white
})
}
}
}
然后在您的 willDisplay 或 cellForItemAt 中,设置正确的无动画外观。
我的应用程序架构有问题...
我有一个 UITableView
装满了自定义 UITableViewCells
。
当然,我使用了出列,所以只生成了 8-10 个单元格实例。
继续我的问题...我已将主题功能添加到我的应用程序中。
当用户长时间触摸主 UINavigationBar
时,会在应用程序范围内发布一条通知,通知每个 viewController 更新他们的 UI。
当 viewController 托管带有自定义单元格的 tableView 收到通知时,它会调用 tableView.reloadData()
效果很好,因为我已经实现了func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
在 willDisplay
中,我在我的自定义 tableViewCell 上调用了一个方法,该方法使用 UIViewAnimation
对单元格进行适当的颜色更改。
看起来像这样:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if let newsFeedItemCell = cell as? NewsFeedItemCell {
if (self.nightModeEnabled) {
newsFeedItemCell.transitionToNightMode()
} else {
newsFeedItemCell.transitionFromNightMode()
}
}
}
在自定义单元格实现中,这些方法如下所示:
func transitionToNightMode() {
UIView.animate(withDuration: 1, animations: {
self.backgroundColor = UIColor.black
self.titleTextView.backgroundColor = UIColor.black
self.titleTextView.textColor = UIColor.white
})
}
func transitionFromNightMode() {
UIView.animate(withDuration: 1, animations: {
self.backgroundColor = UIColor.white
self.titleTextView.backgroundColor = UIColor.white
self.titleTextView.textColor = UIColor.black
})
}
这很好用...这是问题所在:
在滚动 tableView 时,任何不在屏幕上的单元格 在滚动到屏幕上时,它们的颜色 update/animation 代码称为 ,这会导致不和谐的用户体验。
我当然理解为什么会这样,因为 willDisplay
仅在单元格显示时被调用。
我想不出避免这种情况的优雅方法。
我很高兴屏幕上 的单元格 正在为用户体验带来愉悦的动画效果,但是,对于屏幕外的单元格,我宁愿它们 跳过 动画。
可能的解决方案(虽然不雅):
保留对 cellForRow
创建的 8-10 个单元格中的每一个的引用,并检查它们是否在屏幕外,是否立即设置了它们的状态。
但是,我不喜欢保留对每个单元格的引用的想法。
有什么想法吗?
我会尝试以下方法。
我不会在 transition(To/From)NightMode
中使用 UIView.animate
,而是创建一个可以执行相同动画的 UIViewPropertyAnimator
对象。我会保留对该对象的引用,然后准备重用,如果动画仍然是 运行,我会简单地完成该动画并重置状态。所以像这样:
fileprivate var transitionAnimator: UIViewPropertyAnimator?
fileprivate func finishAnimatorAnimation() {
// if there is a transitionAnimator, immediately finishes it
if let animator = transitionAnimator {
animator.stopAnimation(false)
animator.finishAnimation(at: .end)
transitionAnimator = nil
}
}
override func prepareForReuse() {
super.prepareForReuse()
finishAnimatorAnimation()
}
func transitionToNightMode() {
transitionAnimator = UIViewPropertyAnimator(duration: 1, curve: .easeInOut, animations: {
self.backgroundColor = UIColor.black
self.titleTextView.backgroundColor = UIColor.black
self.titleTextView.textColor = UIColor.white
})
transitionAnimator?.startAnimation()
}
func transitionFromNightMode() {
transitionAnimator = UIViewPropertyAnimator(duration: 1, curve: .easeInOut, animations: {
self.backgroundColor = UIColor.white
self.titleTextView.backgroundColor = UIColor.white
self.titleTextView.textColor = UIColor.black
})
transitionAnimator?.startAnimation()
}
我不会使用 reloadData
对此进行动画处理,因为您的 tableview 数据模型实际上并没有改变。
相反,我会给 UITableView 一个函数,如下所示:
class myClass : UITableViewController {
....
func transitionToNightMode() {
for visible in visibleCells {
UIView.animate(withDuration: 1, animations: {
visible.backgroundColor = UIColor.black
visible.titleTextView.backgroundColor = UIColor.black
visible.titleTextView.textColor = UIColor.white
})
}
}
}
然后在您的 willDisplay 或 cellForItemAt 中,设置正确的无动画外观。