有没有办法观察UIViewPropertyAnimator中fractionComplete的变化

问题描述 投票:0回答:3

我一直在研究 iOS 10 中非常酷的新

UIViewPropertyAnimator
类。它可以让你轻松地 执行诸如暂停、恢复和反转飞行中 UIView 动画之类的操作。过去,您必须操作系统创建的底层 CAAnimations 才能对 UIView 动画执行此操作。

新的

UIViewPropertyAnimator
类有一个属性
fractionComplete
。它从 0.0(动画开始)到 1.0(动画结束)变化。如果更改该值,您可以从头到尾“拖动”动画。

我在 Github 上有一个名为 KeyframeViewAnimations 的演示项目,它允许您使用相当神秘的 CAAnimation 技巧使用滑块来回滑动 UIView 和 CAAnimations。当动画运行时,滑块从开始移动到结束以显示动画的进度。该项目是在 Swift 发布之前用 Objective-C 编写的,但底层技术在 Objective-C 或 Swift 中是相同的。

我决定使用

UIViewPropertyAnimator
创建一个等效的项目。虽然有一些怪癖,但相当简单。我不知道如何干净地做的一件事是观察动画的
fractionComplete
属性的进度,以便我可以随着动画的进展更新滑块。我尝试在
fractionComplete
属性上设置 KVO(键值观察),但在动画完成之前它不会触发 KVO 通知。

我最终做的是设置一个以非常小的间隔运行的计时器,并重复查询

UIViewPropertyAnimator
fractionComplete
属性,并随着进度更新滑块。它有效,但不是一种非常干净的做事方式。如果有一种方法可以通知动画的进度那就更好了。

我希望我遗漏了一些东西。

您可以通过此链接在 Github 上查看基于

UIViewPropertyAnimator
的项目:UIViewPropertyAnimator-test

从动画师获取进度值的代码是这样的代码:

func startTimer(_ start: Bool) {
  if start {
    timer = Timer.scheduledTimer(withTimeInterval: 0.02,
     repeats: true) {
        timer in
        var value = Float(self.viewAnimator.fractionComplete)
        if self.animatingBackwards {
          value = 1 - value
        }
        self.animationSlider.value = value
     }
  }
  else {
    self.timer?.invalidate()
  }
}

我可能应该将上面的代码转换为使用

CADisplayLink
计时器而不是普通计时器(NSTimer),但我希望有更好的方法。

ios swift uiview uikit uiviewpropertyanimator
3个回答
4
投票

仅供参考,经过研究后,答案似乎是不,随着动画的进展,没有办法获取更新。您必须设置一个计时器(

Timer
CADisplayLink
)并在每次触发时更新您的进度值。


0
投票

您是否尝试子类化 UIViewPropertyAnimator 然后覆盖fractionComplete 属性?

class MyPropertyAnimator : UIViewPropertyAnimator {
    override var fractionComplete: CGFloat {
        didSet {
            print("didSetFraction")
        }
    }
}

0
投票

这是一个

UIViewPropertyAnimator
子类,它使用
CADisplayLink
将更新发布到
onFractionCompleteUpdated
回调。

使用方法

let animator = MonitorableUIViewPropertyAnimator(duration: 0.45, dampingRatio: 0.8) {
    // Animation Code
}

animator.onFractionCompleteUpdated = { fractionComplete in
    print("Animation Fraction Complete: \(fractionComplete)")
}

实施

/// A animator that allows you to monitor fractionComplete via `onFractionCompleteUpdated`.
/// Useful when a animation has key events that need to happen but the events themselves are not animatable.
/// Example: You need to change the zIndex of two views half way through.
class MonitorableUIViewPropertyAnimator : UIViewPropertyAnimator {
    
    private var displayLink: CADisplayLink?
    private var lastFractionComplete: CGFloat?
    
    /// Called everytime `self.fractionComplete` has a new value.
    var onFractionCompleteUpdated: ((CGFloat) -> ())? = nil {
        didSet {
            if onFractionCompleteUpdated != nil {
                if displayLink == nil {
                    displayLink = CADisplayLink(target: self, selector: #selector(handleDisplayLink(_:)))
                    displayLink?.add(to: .main, forMode: .common)
                    
                    // Clean up when the animation completes
                    addCompletion { [weak self] _ in
                        self?.displayLink?.invalidate()
                    }
                }
            } else {
                // No longer need display link - clean up
                displayLink?.invalidate()
            }
            
        }
    }
    
    @objc private func handleDisplayLink(_ displayLink: CADisplayLink) {
        // Don't call if the animation is paused or not moving
        if lastFractionComplete != fractionComplete {
            onFractionCompleteUpdated?(fractionComplete)
            lastFractionComplete = fractionComplete
        }
        
        // If the animation stops outside of a normal completion - clean up
        if state == .stopped {
            displayLink.invalidate()
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.