You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为已添加的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 17:01:46