【问题标题】:Observing change in frame of a UIView during animation在动画期间观察 UIView 框架的变化
【发布时间】:2014-03-22 19:07:17
【问题描述】:
我想在使用animateWithDuration:delay:options:animations:completion: 对其进行动画处理时观察 UIView 原点的 x 坐标的变化。我想在此动画期间以粒度级别跟踪 x 坐标的变化,因为我想对另一个视图的交互进行更改,正在动画的视图可能会与之接触。我想在确切的联系点进行更改。我想了解在更高级别执行此类操作的最佳方法:
-- 我应该在联系点的完成回调中使用animateWithDuration:... 吗?换句话说,第一个动画一直运行到它到达那个 x 坐标,然后动画的其余部分发生在完成回调中?
-- 我应该使用NSNotification 观察者并观察框架属性的变化吗?这有多准确/精细?我可以跟踪 x 的每一个变化吗?我应该在单独的线程中执行此操作吗?
欢迎提出任何其他建议。我正在寻找最佳实践。
【问题讨论】:
标签:
ios
animation
uiview
observer-pattern
【解决方案1】:
使用CADisplayLink,因为它是专门为此目的而构建的。在文档中,它说:
一旦显示链接与运行循环相关联,当屏幕内容需要更新时,就会调用目标上的选择器。
对我来说,我有一个填满的栏,当它通过某个标记时,我必须更改该标记上方视图的颜色。
这就是我所做的:
let displayLink = CADisplayLink(target: self, selector: #selector(animationDidUpdate))
displayLink.frameInterval = 3
displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSDefaultRunLoopMode)
UIView.animateWithDuration(1.2, delay: 0.0, options: [.CurveEaseInOut], animations: {
self.viewGaugeGraph.frame.size.width = self.graphWidth
self.imageViewGraphCoin.center.x = self.graphWidth
}, completion: { (_) in
displayLink.invalidate()
})
func animationDidUpdate(displayLink: CADisplayLink) {
let presentationLayer = self.viewGaugeGraph.layer.presentationLayer() as! CALayer
let newWidth = presentationLayer.bounds.width
switch newWidth {
case 0 ..< width * 0.3:
break
case width * 0.3 ..< width * 0.6:
// Color first mark
break
case width * 0.6 ..< width * 0.9:
// Color second mark
break
case width * 0.9 ... width:
// Color third mark
break
default:
fatalError("Invalid value observed. \(newWidth) cannot be bigger than \(width).")
}
}
在示例中,我将frameInterval 属性设置为3,因为我不必严格更新。默认为1,这意味着它会为每一帧触发,但会影响性能。
【解决方案2】:
创建一个NSTimer 并在每次延迟后运行特定的选择器。
在该方法中检查动画视图的框架并将其与您的碰撞视图进行比较。
并确保您使用 presentationLayer 框架,因为如果您在制作动画时访问 view.frame,它会提供在整个动画中保持不变的目标框架。
CGRect animationViewFrame= [[animationView.layer presentationLayer] frame];
如果你不想创建计时器,write a selector which calls itself after some delay。延迟大约 0.01 秒。
澄清->
假设您有一个视图,您正在将其位置从 (0,0) 动画到 (100,100),持续时间为 5 秒。假设您在此视图的框架中实现了 KVO
当您调用 animateWithDuration block 时,视图的位置会直接更改为 (100,100),这是最终值,即使视图以中间位置值移动。
因此,您的KVO 将在动画开始的瞬间触发一次。
因为,层有layer Tree 和Presentation Tree。 layer tree 只存储目标值,而presentation Layer 存储中间值。
当您访问view.frame 时,它始终会给出layer tree 中帧的值,而不是它所采用的中间帧。
因此,您必须使用presentation Layer frame 来获取中间帧。
希望这会有所帮助。