【问题标题】: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 TreePresentation Treelayer tree 只存储目标值,而presentation Layer 存储中间值。
      当您访问view.frame 时,它始终会给出layer tree 中帧的值,而不是它所采用的中间帧。

      因此,您必须使用presentation Layer frame 来获取中间帧。

      希望这会有所帮助。

      【讨论】:

        【解决方案3】:

        UIDynamics 和碰撞行为在这里值得研究。您可以设置一个在发生冲突时调用的委托。

        有关详细信息,请参阅collision behaviour documentation

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-12-06
          • 2020-03-11
          • 2011-08-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-21
          相关资源
          最近更新 更多