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

Posted

技术标签:

【中文标题】在 Swift 中向 UIViewPropertyAnimator 添加完成处理程序【英文标题】:Adding a completion handler to UIViewPropertyAnimator in Swift 【发布时间】:2021-05-31 11:04:10 【问题描述】:

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

UIVisualEffectView 子类。
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完成处理程序是在第二个视图控制器(具有视觉效果视图的控制器)出现之后添加的。我尝试将完成处理程序移动到不同的地方,例如 viewDidLoadviewDidAppear,但似乎没有任何效果。

【问题讨论】:

为什么将pausesOnCompletion 设置为true? 【参考方案1】:

整个事情看起来设计不正确。

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

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

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 中。

【讨论】:

现在完成处理程序工作但动画没有播放(模糊视图突然出现)。你测试过这个吗?有什么建议吗? 必须设置blurView.effect = nil。现在它完美地工作了。谢谢。【参考方案2】:

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

如果您确实需要将其设置为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 可以在初始化器中设置。

【讨论】:

以上是关于在 Swift 中向 UIViewPropertyAnimator 添加完成处理程序的主要内容,如果未能解决你的问题,请参考以下文章

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

在 Swift 中向 UITableView 添加按日期分隔的部分

在swift4中向UIView添加云阴影

在 Swift 中向 UILabel 动态添加行

在 Swift 中向用户显示网络错误消息

如何在 Swift 中向祖父视图控制器发送消息