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

Swift自定义TabController切换标签时路径描边无法同步动画问题求解

问题原因

  • 当前你实现的路径过渡动画只作用于图层遮罩(mask),描边是通过CoreGraphics的stroke()方法直接绘制的静态内容,仅在drawCurve调用时渲染一次,不会跟随遮罩的path动画同步变化。
  • 描边没有独立的动画载体,无法参与CoreAnimation的属性过渡。

解决步骤

  1. 新增全局持久化的CAShapeLayer作为专门的描边层,不要每次调用drawCurve都重复创建
  2. 描边的样式、path都统一交给这个CAShapeLayer管理,删除drawCurve里直接调用stroke()的逻辑
  3. 动画触发时,同时给遮罩层和描边层添加完全相同的path过渡动画

修改后参考代码

// 类内先声明持久化持有描边层
private let strokeLayer: CAShapeLayer = {
    let layer = CAShapeLayer()
    layer.lineWidth = 1
    layer.fillColor = UIColor.clear.cgColor
    layer.strokeColor = Colors.borderTabbarGrayColor.cgColor
    return layer
}()

// 初始化时把描边层添加到当前视图的layer上
override init(frame: CGRect) {
    super.init(frame: frame)
    layer.addSublayer(strokeLayer)
}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

private func drawCurve() {
    let fillColor: UIColor = .white
    tabWidth = self.bounds.width / CGFloat(self.items!.count)
    let bezPath = drawPathBorder(for: index)
    bezPath.close()
    
    // 保留原有的填充逻辑
    fillColor.setFill()
    bezPath.fill()
    
    let mask = CAShapeLayer()
    mask.fillRule = .evenOdd
    mask.path = bezPath.cgPath
    
    // 给描边层赋值最终状态的path
    strokeLayer.path = bezPath.cgPath
    
    if (self.animated) {
        let bezPathFrom = drawPathBorder(for: previousIndex)
        // 创建mask的path动画
        let maskAnimation = CABasicAnimation(keyPath: "path")
        maskAnimation.fromValue = bezPathFrom.cgPath
        maskAnimation.toValue = bezPath.cgPath
        maskAnimation.duration = 3.3
        maskAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
        // 可选配置:避免动画结束后闪回
        maskAnimation.fillMode = .forwards
        maskAnimation.isRemovedOnCompletion = false
        mask.add(maskAnimation, forKey: nil)
        
        // 同步给描边层添加相同的动画
        let strokeAnimation = maskAnimation.copy() as! CABasicAnimation
        strokeLayer.add(strokeAnimation, forKey: "pathAnimation")
    }
    self.layer.mask = mask
}

额外注意点

如果你的tabbar有布局变化需要适配,记得在layoutSubviews方法里同步更新strokeLayer的frame,避免描边位置偏移。

内容的提问来源于stack exchange,提问作者user883884

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 22:36:00