在 Swift 中向 UIViewPropertyAnimator 添加完成处理程序
Adding a completion handler to UIViewPropertyAnimator in Swift
我正在尝试让 属性 动画师在显示视图控制器时启动动画。
现在动画正在播放,但是 UIViewPropertyAnimator
没有响应添加到它的完成处理程序。
UIVisualEffectView
子class.
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(具有视觉效果视图的控制器)呈现后添加的。我曾尝试将完成处理程序移动到 viewDidLoad
和 viewDidAppear
等不同的地方,但似乎没有任何效果。
整个事情似乎设计不正确。
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
可以在初始化程序中设置。
我正在尝试让 属性 动画师在显示视图控制器时启动动画。
现在动画正在播放,但是 UIViewPropertyAnimator
没有响应添加到它的完成处理程序。
UIVisualEffectView
子class.
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(具有视觉效果视图的控制器)呈现后添加的。我曾尝试将完成处理程序移动到 viewDidLoad
和 viewDidAppear
等不同的地方,但似乎没有任何效果。
整个事情似乎设计不正确。
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
可以在初始化程序中设置。