如何为已添加的UIBezierPath添加反向动画以移除边框?
实现UICollectionViewCell边框动画的反向切换
嘿,这个需求其实很好实现,核心就是追踪单元格的选中状态,然后根据状态切换动画方向,同时还要处理动画结束后的状态保持问题——不然动画跑完会直接回到初始状态,白忙活一场。我给你整理了具体的修改方案:
第一步:添加状态标记属性
先给你的AnimalSelectionCollectionCell加一个布尔属性,用来记录当前边框是否已经完成正向动画(也就是是否处于"已绘制完成"的状态):
class AnimalSelectionCollectionCell: UICollectionViewCell { ... let shapeLayer: CAShapeLayer = CAShapeLayer() // 新增:标记当前边框动画的状态 private var isBorderCompleted = false ... }
第二步:修改动画方法,支持双向切换
把原来的startAnimation()改成一个能切换状态的方法,比如叫toggleBorderAnimation(),根据isBorderCompleted的值来决定动画的方向:
func toggleBorderAnimation() { // 先移除之前的动画,避免重复叠加导致的异常 shapeLayer.removeAnimation(forKey: "myStroke") // 设置边框颜色(如果反向动画需要换颜色,这里可以加判断调整) shapeLayer.strokeColor = AppColors.Text.selectedName.cgColor let animation = CABasicAnimation(keyPath: "strokeEnd") // 根据当前状态设置动画的起始/结束值 if isBorderCompleted { // 反向动画:从完整边框擦除到无 animation.fromValue = 1.0 animation.toValue = 0.0 } else { // 正向动画:从无绘制到完整边框 animation.fromValue = 0.0 animation.toValue = 1.0 } animation.duration = 0.5 // 关键设置:动画结束后保持最终状态,不回到初始值 animation.fillMode = .forwards animation.isRemovedOnCompletion = false shapeLayer.add(animation, forKey: "myStroke") // 切换状态标记,下次调用时触发反向逻辑 isBorderCompleted.toggle() }
第三步:初始化时设置初始状态
在setData()方法里,记得初始化shapeLayer的strokeEnd为0,确保单元格初始状态是没有边框的:
func setData() { ... shapeLayer.lineWidth = 1.0 shapeLayer.fillColor = UIColor.clear.cgColor // 初始化strokeEnd为0,保证初始无边框 shapeLayer.strokeEnd = 0.0 contentView.layer.addSublayer(shapeLayer) }
第四步:处理布局刷新时的状态保持
因为layoutSubviews会在单元格尺寸变化时被调用,重新设置path后要保持当前的strokeEnd值,不然状态会被重置:
override func layoutSubviews() { super.layoutSubviews() let path = UIBezierPath( roundedRect: contentView.bounds.inset(by: UIEdgeInsets(top: 2, left: 2, bottom: 2, right: 2)), byRoundingCorners: .allCorners, cornerRadii: CGSize(width: contentView.bounds.size.width, height: 0.0)) // 先保存当前的strokeEnd状态 let currentStrokeEnd = shapeLayer.strokeEnd shapeLayer.path = path.cgPath // 恢复strokeEnd值,保持当前动画状态 shapeLayer.strokeEnd = currentStrokeEnd }
最后:在UICollectionView选中逻辑里调用
当你处理UICollectionView的选中事件时,比如didSelectItemAt,拿到对应cell后调用toggleBorderAnimation()即可:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { guard let cell = collectionView.cellForItem(at: indexPath) as? AnimalSelectionCollectionCell else { return } cell.toggleBorderAnimation() }
这样修改后,第一次选中单元格会播放正向绘制动画,再次选中就会触发反向擦除动画,完美实现你要的效果~
内容的提问来源于stack exchange,提问作者Mrugesh Tank
相关产品推荐
相关产品推荐

