Ios 设置自定义UITableViewCell动画时的计时问题
我的应用程序架构有问题 我有一个UITableView,其中填充了自定义UITableViewCells。 当然,我使用了出列,所以只生成了8-10个单元实例 继续我的问题。。。我已经在我的应用程序中添加了一个主题功能。 当用户长时间触摸主UI时,会在应用程序范围内发布通知,通知每个viewController更新其UI 承载带有自定义单元格的tableView的viewController收到通知时,将调用tableView.reloadData 这很好,因为我已经实现了func tableView utableview:UITableView,willDisplay cell:UITableViewCell,forRowAt indexPath:indexPath 在willDisplay中,我调用自定义tableViewCell上的一个方法,该方法使用UIViewAnimation对单元格进行适当的颜色更改 看起来是这样的:Ios 设置自定义UITableViewCell动画时的计时问题,ios,swift,uitableview,uiviewanimation,Ios,Swift,Uitableview,Uiviewanimation,我的应用程序架构有问题 我有一个UITableView,其中填充了自定义UITableViewCells。 当然,我使用了出列,所以只生成了8-10个单元实例 继续我的问题。。。我已经在我的应用程序中添加了一个主题功能。 当用户长时间触摸主UI时,会在应用程序范围内发布通知,通知每个viewController更新其UI 承载带有自定义单元格的tableView的viewController收到通知时,将调用tableView.reloadData 这很好,因为我已经实现了func tableV
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
})
}
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()
}
在自定义单元实现中,这些方法如下所示:
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
})
}
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()
}
这个很好用。。。问题是:
滚动tableView时,任何不在屏幕上的单元格在滚动到屏幕上时都会调用颜色更新/动画代码,这会导致不和谐的用户体验
我当然理解为什么会发生这种情况,因为willDisplay只被称为cells display
我想不出一个优雅的方法来避免这种情况
我很高兴屏幕上的细胞正在为用户体验制作动画,然而,对于屏幕外的细胞,我宁愿他们跳过动画
可能的解决方案,尽管不美观:
保留对cellForRow创建的8-10个单元格的引用,并检查它们是否在屏幕外,是否立即设置了状态
但是,我不喜欢保留每个单元格的引用
有什么想法吗?我想试试下面的方法 在transitionTo/FromNightMode中,我不使用UIView.animate,而是创建一个UIViewPropertyAnimator对象,该对象将执行相同的动画。我将保留对该对象的引用,然后准备重用,如果动画仍在运行,我只需完成该动画并重置状态。比如说:
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
})
}
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()
}
我会尝试以下方法 在transitionTo/FromNightMode中,我不使用UIView.animate,而是创建一个UIViewPropertyAnimator对象,该对象将执行相同的动画。我将保留对该对象的引用,然后准备重用,如果动画仍在运行,我只需完成该动画并重置状态。比如说:
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
})
}
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()
}
我不会使用重载数据来设置动画,因为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中,设置正确的外观而不设置动画。我不会使用重载数据来设置动画,因为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中,设置正确的外观而不使用动画。令人愉快的解决方案。打电话反对可见细胞可以吗?只有在更改数据时才能调用reload,对吗?@Woodstock,谢谢!是的,这是对可见单元格的调用。与任何其他UIView一样,可见单元格也是层次结构的一部分。有时我会变得懒惰,在我真的不需要的时候使用重载,但它通常用于数据模型中的更改,而不仅仅是用于视觉外观。这是一个令人愉快的解决方案。打电话反对可见细胞可以吗?只有在更改数据时才能调用reload,对吗?@Woodstock,谢谢!是的,这是对可见单元格的调用。与任何其他UIView一样,可见单元格也是层次结构的一部分。有时我会变得懒惰,在我真的不需要的时候使用重载,但它通常用于数据模型中的更改,而不仅仅是用于视觉外观。