在 Swift 中向 UIViewPropertyAnimator 添加完成处理程序

Adding a completion handler to UIViewPropertyAnimator in Swift

我正在尝试让 属性 动画师在显示视图控制器时启动动画。 现在动画正在播放,但是 UIViewPropertyAnimator 没有响应添加到它的完成处理程序。

import UIKit

final class BlurEffectView: UIVisualEffectView {
    
    deinit {
    animator?.stopAnimation(true)
    }

    override func draw(_ rect: CGRect) {
        super.draw(rect)
        effect = nil
        animator = UIViewPropertyAnimator(duration: 1, curve: .linear) { [unowned self] in
          self.effect = theEffect
        }
        animator?.pausesOnCompletion = true
   }

    private let theEffect: UIVisualEffect = UIBlurEffect(style: .regular)
    var animator: UIViewPropertyAnimator?

}

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func doSomething(_ sender: UIButton) {
        let vc = storyboard?.instantiateViewController(identifier: "second") as! SecondVC
        vc.modalPresentationStyle = .overFullScreen

        present(vc, animated: false) { //vc presentation completion handler
            
            //adding a completion handler to the UIViewPropertyAnimator
            vc.blurView.animator?.addCompletion({ (pos) in
                print("animation complete") //the problem is that this line isn't executed 
            })
            vc.blurView.animator?.startAnimation()
        }
    }
    
}
import UIKit

class SecondVC: UIViewController {

    @IBOutlet weak var blurView: BlurEffectView!

    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

此处UIViewPropertyAnimator完成处理程序是在Second View Controller(具有视觉效果视图的控制器)呈现后添加的。我曾尝试将完成处理程序移动到 viewDidLoadviewDidAppear 等不同的地方,但似乎没有任何效果。

整个事情似乎设计不正确。

draw(_ rect:) 不是初始化动画器的地方*,我对发生的事情的最佳猜测是当您尝试启动它时 vc.blurView.animator?nil(您是否验证过不是吗?)。

相反,您的视图 class 可能如下所示**:

final class BlurEffectView: UIVisualEffectView {
    func fadeInEffect(_ completion: @escaping () -> Void) {
        UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 0.5, delay: 0, options: []) {
            self.effect = UIBlurEffect(style: .regular)
        } completion: { _ in
            completion()
        }
    }
}

你会像这样执行你的动画:

present(vc, animated: false) { //vc presentation completion handler
    vc.blurView.fadeInEffect {
        // Completion here
    }
}

*draw(_ rect:) 每次让系统知道您需要重绘视图时都会被调用,并且在内部您应该使用 CoreGraphics 绘制视图的内容,这不是您想要的正在这里做。

**由于您没有使用 属性 动画师的任何更高级的功能,因此似乎没有必要将其存储在 ivar 中。

问题是您将 pausesOnCompletion 设置为 true。这会导致不调用完成处理程序。

如果您确实需要将其设置为真,则需要使用 KVO 来观察 isRunning 属性:

// property of your VC
var ob: NSKeyValueObservation?

...

self.ob?.invalidate()
self.ob = vc.blurView.animator?.observe(\.isRunning, options: [.new], changeHandler: { (animator, change) in
    if !(change.newValue!) {
        print("completed")
    }
})
vc.blurView.animator?.startAnimation()

正如 EmilioPelaez 所说,您不应该在 draw 中初始化您的 animator。同样,如果您确实有使用 pausesOnCompletion = true 的理由,请将它们设置为惰性 属性:

lazy var animator: UIViewPropertyAnimator? = {
    let anim = UIViewPropertyAnimator(duration: 1, curve: .linear) { [unowned self] in
        self.effect = self.theEffect
    }
    anim.pausesOnCompletion = true
    return anim
}()

self.effect = nil 可以在初始化程序中设置。