UICollectionViewCell - 内容不会与单元格的 contentView 一起动画
UICollectionViewCell - contents do not animate alongside cell's contentView
问题看起来像这样:http://i.imgur.com/5iaAiGQ.mp4
(红色是cell.contentView的一种颜色)
代码如下:https://github.com/nezhyborets/UICollectionViewContentsAnimationProblem
当前状态:
UICollectionViewCell 的 contentView 的内容不会随着 contentView 框架的变化而变化。它立即获得尺寸,没有动画。
做任务时遇到的其他问题:
直到我在 UICollectionViewCell 子类中执行此操作之前,contentView 也没有随着单元格的帧更改而设置动画:
override func awakeFromNib() {
super.awakeFromNib()
//Because contentView won't animate along with cell
contentView.frame = bounds
contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
}
其他说明:
下面是cell size动画涉及的代码
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.selectedIndex = indexPath.row
collectionView.performBatchUpdates({
collectionView.reloadData()
}, completion: nil)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let isSelected = self.selectedIndex == indexPath.row
let someSize : CGFloat = 90 //doesn't matter
let sizeK : CGFloat = isSelected ? 0.9 : 0.65
let size = CGSize(width: someSize * sizeK, height: someSize * sizeK)
return size
}
我在使用 collectionView.setCollectionViewLayout(newLayout, animated: true)
时得到相同的结果,而在使用 collectionView.collectionViewLayout.invalidateLayout()
而不是 reloadData() inside batchUpdates
时根本没有动画。
更新
当我在 UICollectionView 的 willDisplayCell
方法中打印 imageView.constraints
时,它会打印空数组。
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
for view in cell.contentView.subviews {
print(view.constraints)
}
//Outputs
//View: <UIImageView: 0x7fe26460e810; frame = (0 0; 50 50); autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x608000037280>>
//View constraints: []
}
您可以将 transform 应用于单元格,但它有一些缺点,例如处理方向更改。
为了增加效果,我在混音中添加了颜色变化和 spring 效果,这两种效果都无法使用重新加载路线实现:
func collectionView(_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath) {
UIView.animate(
withDuration: 0.4,
delay: 0,
usingSpringWithDamping: 0.4,
initialSpringVelocity: 0,
options: UIViewAnimationOptions.beginFromCurrentState,
animations: {
if( self.selectedIndexPath.row != NSNotFound) {
if let c0 =
collectionView.cellForItem(at: self.selectedIndexPath)
{
c0.contentView.layer.transform = CATransform3DIdentity
c0.contentView.backgroundColor = UIColor.lightGray
}
}
self.selectedIndexPath = indexPath
if let c1 = collectionView.cellForItem(at: indexPath)
{
c1.contentView.layer.transform =
CATransform3DMakeScale(1.25, 1.25, 1)
c1.contentView.backgroundColor = UIColor.red
}
},
completion: nil)
}
这是一个棘手的问题,您已经非常接近解决方案了。问题在于,动画布局更改的方法会有所不同,具体取决于您是使用自动布局还是调整蒙版大小或其他方法,并且您当前正在 ProblematicCollectionViewCell class 中混合使用。 (在回答一个单独的问题时会更好地解决其他可用的方法,但请注意,Apple 通常似乎避免在自己的应用程序中对单元格使用自动布局。)
以下是为特定单元格设置动画所需执行的操作:
-
选择或取消选择单元格时,请告诉收集布局对象,细胞大小已更改,并在其可以做到的范围内对这些更改进行动画。最简单的方法是使用 performBatchUpdates
,这将导致从 sizeForItemAt
获取新的尺寸,然后将新的布局属性应用于其自己的动画块中的相关单元格:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.selectedIndex = indexPath.row
collectionView.performBatchUpdates(nil)
}
告诉您的单元格在每次集合视图布局对象更改其布局属性(将在 performBatchUpdates
动画块内发生)时对其子视图进行布局:
// ProblematicCollectionViewCell.swift
override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
super.apply(layoutAttributes)
layoutIfNeeded()
}
如果您想更好地控制动画,可以将对 performBatchUpdates
的调用嵌套在对 UIView.animate
基于块的动画方法之一的调用中。 iOS10 中集合视图单元格的默认动画持续时间为 0.25。
解决方法很简单。首先,在ViewController.collectionView(_,didSelectItemAt:)中,只写这个:
collectionView.performBatchUpdates({
self.selectedIndex = indexPath.row
}, completion: nil)
然后,在 class ProblematicCollectionViewCell 中添加此函数:
override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
super.apply(layoutAttributes)
self.layoutIfNeeded()
}
尽情享受吧!
问题看起来像这样:http://i.imgur.com/5iaAiGQ.mp4 (红色是cell.contentView的一种颜色)
代码如下:https://github.com/nezhyborets/UICollectionViewContentsAnimationProblem
当前状态: UICollectionViewCell 的 contentView 的内容不会随着 contentView 框架的变化而变化。它立即获得尺寸,没有动画。
做任务时遇到的其他问题: 直到我在 UICollectionViewCell 子类中执行此操作之前,contentView 也没有随着单元格的帧更改而设置动画:
override func awakeFromNib() {
super.awakeFromNib()
//Because contentView won't animate along with cell
contentView.frame = bounds
contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
}
其他说明: 下面是cell size动画涉及的代码
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.selectedIndex = indexPath.row
collectionView.performBatchUpdates({
collectionView.reloadData()
}, completion: nil)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let isSelected = self.selectedIndex == indexPath.row
let someSize : CGFloat = 90 //doesn't matter
let sizeK : CGFloat = isSelected ? 0.9 : 0.65
let size = CGSize(width: someSize * sizeK, height: someSize * sizeK)
return size
}
我在使用 collectionView.setCollectionViewLayout(newLayout, animated: true)
时得到相同的结果,而在使用 collectionView.collectionViewLayout.invalidateLayout()
而不是 reloadData() inside batchUpdates
时根本没有动画。
更新
当我在 UICollectionView 的 willDisplayCell
方法中打印 imageView.constraints
时,它会打印空数组。
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
for view in cell.contentView.subviews {
print(view.constraints)
}
//Outputs
//View: <UIImageView: 0x7fe26460e810; frame = (0 0; 50 50); autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x608000037280>>
//View constraints: []
}
您可以将 transform 应用于单元格,但它有一些缺点,例如处理方向更改。
为了增加效果,我在混音中添加了颜色变化和 spring 效果,这两种效果都无法使用重新加载路线实现:
func collectionView(_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath) {
UIView.animate(
withDuration: 0.4,
delay: 0,
usingSpringWithDamping: 0.4,
initialSpringVelocity: 0,
options: UIViewAnimationOptions.beginFromCurrentState,
animations: {
if( self.selectedIndexPath.row != NSNotFound) {
if let c0 =
collectionView.cellForItem(at: self.selectedIndexPath)
{
c0.contentView.layer.transform = CATransform3DIdentity
c0.contentView.backgroundColor = UIColor.lightGray
}
}
self.selectedIndexPath = indexPath
if let c1 = collectionView.cellForItem(at: indexPath)
{
c1.contentView.layer.transform =
CATransform3DMakeScale(1.25, 1.25, 1)
c1.contentView.backgroundColor = UIColor.red
}
},
completion: nil)
}
这是一个棘手的问题,您已经非常接近解决方案了。问题在于,动画布局更改的方法会有所不同,具体取决于您是使用自动布局还是调整蒙版大小或其他方法,并且您当前正在 ProblematicCollectionViewCell class 中混合使用。 (在回答一个单独的问题时会更好地解决其他可用的方法,但请注意,Apple 通常似乎避免在自己的应用程序中对单元格使用自动布局。)
以下是为特定单元格设置动画所需执行的操作:
-
选择或取消选择单元格时,请告诉收集布局对象,细胞大小已更改,并在其可以做到的范围内对这些更改进行动画。最简单的方法是使用
performBatchUpdates
,这将导致从sizeForItemAt
获取新的尺寸,然后将新的布局属性应用于其自己的动画块中的相关单元格:func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { self.selectedIndex = indexPath.row collectionView.performBatchUpdates(nil) }
告诉您的单元格在每次集合视图布局对象更改其布局属性(将在
performBatchUpdates
动画块内发生)时对其子视图进行布局:// ProblematicCollectionViewCell.swift override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) { super.apply(layoutAttributes) layoutIfNeeded() }
如果您想更好地控制动画,可以将对 performBatchUpdates
的调用嵌套在对 UIView.animate
基于块的动画方法之一的调用中。 iOS10 中集合视图单元格的默认动画持续时间为 0.25。
解决方法很简单。首先,在ViewController.collectionView(_,didSelectItemAt:)中,只写这个:
collectionView.performBatchUpdates({
self.selectedIndex = indexPath.row
}, completion: nil)
然后,在 class ProblematicCollectionViewCell 中添加此函数:
override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
super.apply(layoutAttributes)
self.layoutIfNeeded()
}
尽情享受吧!