UICollectionView单元格选中动画实现及animated参数作用问询
刚好对UICollectionView的选中动画和animated参数这块挺熟的,给你拆解一下:
利用Cell的
isSelected状态自动触发动画
这是最简洁的方式,直接在自定义UICollectionViewCell里重写isSelected的didSet,把动画逻辑写在这里——不管是通过代理方法选中,还是调用selectItem(at:)选中,都会自动触发动画:class CustomCollectionViewCell: UICollectionViewCell { override var isSelected: Bool { didSet { UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.5) { // 缩放效果 self.transform = self.isSelected ? CGAffineTransform(scaleX: 0.95, y: 0.95) : .identity // 圆角+背景色变化 self.layer.cornerRadius = self.isSelected ? 12 : 0 self.backgroundColor = self.isSelected ? UIColor.systemBlue.withAlphaComponent(0.1) : .white } } } }通过UICollectionViewDelegate代理方法手动控制
如果需要在选中/取消选中时同步处理其他逻辑(比如更新数据源、联动页面其他UI),可以在代理方法里拿到目标Cell并添加动画:extension YourViewController: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { guard let cell = collectionView.cellForItem(at: indexPath) else { return } // 选中时的弹性动画 UIView.animate(withDuration: 0.2) { cell.transform = CGAffineTransform(scaleX: 0.9, y: 0.9) } completion: { _ in UIView.animate(withDuration: 0.2) { cell.transform = CGAffineTransform(scaleX: 0.95, y: 0.95) } } } func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { guard let cell = collectionView.cellForItem(at: indexPath) else { return } // 取消选中时恢复原状 UIView.animate(withDuration: 0.3) { cell.transform = .identity } } }借助UIButton的高亮状态(Cell内有按钮时适用)
如果你的Cell里有一个占满整个Cell的UIButton,直接用按钮的highlighted状态做动画会更贴合触摸反馈:class ButtonCollectionViewCell: UICollectionViewCell { let mainButton = UIButton(type: .system) weak var delegate: ButtonCellDelegate? override init(frame: CGRect) { super.init(frame: frame) mainButton.frame = bounds mainButton.autoresizingMask = [.flexibleWidth, .flexibleHeight] addSubview(mainButton) mainButton.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside) // 绑定高亮状态的动画 mainButton.configuration?.updateHandler = { config in UIView.animate(withDuration: 0.2) { self.transform = config.isHighlighted ? CGAffineTransform(scaleX: 0.98, y: 0.98) : .identity } } } @objc func buttonTapped(_ sender: UIButton) { delegate?.cellTapped(self) } }
animated参数的作用详解 首先得明确:这个参数控制的是UICollectionView系统内置的布局/数据变更动画,和我们手动给Cell加的自定义选中动画不是一回事,具体分几种核心场景:
数据变更操作(插入/删除/移动/刷新)
当你调用insertItems(at:)、deleteItems(at:)、moveItem(at:to:)、reloadItems(at:)这些方法时,animated参数决定了操作是否带有系统默认过渡动画:- 传
true:系统会使用当前UICollectionViewLayout定义的动画(比如流式布局的淡入、滑入效果)平滑过渡; - 传
false:所有变更瞬间完成,没有任何过渡效果。
- 传
布局切换操作
调用setCollectionViewLayout(_:animated:)时,animated参数控制新旧布局切换的过渡动画:
比如从UICollectionViewFlowLayout切换到自定义布局,传true会让Cell的位置、大小等属性平滑过渡到新布局状态;传false会直接跳转到新布局,没有动画。选中/取消选中操作(
selectItem(at:animated:scrollPosition:))
这里的animated参数控制的是系统默认的选中高亮动画(比如Cell背景色淡入)。如果你已经在自定义Cell里重写了isSelected的动画,这个系统默认动画可能会和自定义动画冲突,此时可以传false,完全使用自己的动画逻辑。
另外你提到要创建UICollectionView子类,允许使用者自定义行为——可以考虑在子类里封装一个selectedAnimation闭包属性,让使用者传入自定义动画逻辑,同时在调用selectItem(at:animated:)时,根据animated参数决定是否触发这个闭包,既保留系统参数的原生作用,又给使用者留出自定义空间。
内容的提问来源于stack exchange,提问作者Hash88

