【问题标题】:CABasicAnimation on CAShapeLayer not working for path changeCAShapeLayer 上的 CABasicAnimation 不适用于路径更改
【发布时间】:2017-05-12 07:05:49
【问题描述】:

我有进度方法here

func progress(incremented : CGFloat){
    if incremented <= self.bounds.width{
        self.progressLayer.removeFromSuperlayer()
        let originBezierPathProg = UIBezierPath(roundedRect: CGRect(x:0, y:0, width:0, height:self.bounds.height) , cornerRadius: self.viewCornerRadius)
        originBezierPathProg.close()

        let newBezierPathProg = UIBezierPath(roundedRect: CGRect(x:0, y:0, width:incremented, height:self.bounds.height) , cornerRadius: self.viewCornerRadius)
        bezierPathProg.close()

        self.progressLayer.path = originBezierPathProg.cgPath
        self.borderLayer.addSublayer(self.progressLayer)

        let animation = CABasicAnimation(keyPath: "path")
        animation.fromValue = originBezierPathProg.cgPath
        animation.toValue = newBezierPathProg.cgPath
        animation.duration = 1
        self.progressLayer.add(animation, forKey: animation.keyPath)

        self.progressLayer.path = newBezierPathProg.cgPath
    }
}

我正在尝试以动画方式使进度条取得进展。但是当我调用progress(100) 时,它只是在渲染没有动画的栏。

我该如何解决?

更新:根据 Rob 的建议创建了 MCVE:https://github.com/utkarsh2012/ProgressBarTest。我希望进度条的动画从 width=0 到 width=x(比如 60)

看起来类似于这个问题CABasicAnimation with CALayer path doesn't animate

【问题讨论】:

  • 虽然这个could be tightened up a bit,它对我有用,就这样。所以问题是你如何调用它(太早,太频繁,从后台线程),或者你的boundsincremented 值使得if 语句失败。我们需要问题的可生产示例。
  • 让我想出一个解决这个问题的简单项目。现在已经为此苦苦挣扎了几个小时,我认为真正的问题是它是从 UIView 的 var ididSet 调用的(可能还为时过早?)。之后如果需要,我也会进行布局。查看 setupView:github.com/TwoPence/TwoPence/blob/milestone/TwoPence/…
  • 很难说。那里有一些不相关的东西,但同时,我认为还不足以重现问题。如果您希望我们提供帮助,我们真的需要MCVE,即向我们展示如何创建空白项目,插入显示您问题的最少代码量。或者,如果您想自己诊断问题,请添加检查 (a) 确保它发生在主线程上的调试代码; (b) bounds 是您在调用 progress(incremented:) 时的想法; (c) incremented 的价值是什么。
  • b 和 c 发生正确。我将创建一个空白项目并复制它。感谢您的帮助!
  • @Rob 创建为简单的 xcode 项目(VC 中的 UIView,与我当前的项目相同)及其可重现的 github.com/utkarsh2012/ProgressBarTest

标签: ios swift animation


【解决方案1】:

您问题中最初显示的progress 方法很好。问题在于你如何使用它。说了这么多,让我们来看看 MCVE。

your MCVE 中,有几件事阻止了动画,即:

  1. progress 方法如下:

    func progress(incremented: CGFloat) {
        if incremented <= bounds.width {
            let toPath = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: incremented + 100, height: bounds.height), cornerRadius: viewCornerRadius)
    
            progressLayer.path = toPath.cgPath
            borderLayer.addSublayer(progressLayer)
    
            let animation = CABasicAnimation(keyPath: "path")
            animation.fromValue = self.progressLayer.path
            animation.toValue = toPath.cgPath
            animation.duration = 3
            progressLayer.add(animation, forKey: animation.keyPath)
        }
    }
    

    这就是将path 设置为新路径,然后从该路径动画到新路径(即动画到和从同一路径)。因此,没有动画。

    在将path 更改为其新的“结束状态”并启动动画之前保存旧路径。使用保存的路径作为动画的fromValue

  2. value 属性正在调用 layoutSubviews

    var value: Int? {
        didSet {
            progress(value: value!)
            self.layoutSubviews()
        }
    }
    

    切勿直接致电layoutSubviews()

  3. 视图控制器正在执行以下操作:

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        let view = DetailView(frame: self.view.frame)
        view.value = 60
        self.containerView.addSubview(view)
    }
    

    不要尝试在viewDidLoad 中启动动画。这在视图生命周期中还为时过早。改用viewDidAppear(或其他)。在 viewDidLoad 中执行此操作通常在视图生命周期中为时过早。

  4. viewDidLoad 代码相关,您在将 value 添加到视图层次结构之前更改了 DetailView(触发动画)。


在此处查看修复https://github.com/robertmryan/ProgressBarTest

【讨论】:

  • 哇,谢谢!很多事情都出错了!非常感谢 Github PR!下次你在旧金山的时候,给我喝杯咖啡:)
  • 或者,如果你在洛杉矶,找我。
  • 动画有效,但现在它导致了一种奇怪的行为,即数据从标签中的默认值更改为实际值。 imgur.com/a/zqn2o 如果我找不到修复程序,可能是一个后续问题。
  • 是的,我注意到它看起来很奇怪。这可能是我使用 Swift 的第 4 周,非常欢迎反馈!我将研究如何制作动画效果更好的进度条。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-29
  • 2014-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-29
相关资源
最近更新 更多