【问题标题】:"Push" animation in Objective-CObjective-C 中的“推”动画
【发布时间】:2013-03-12 16:16:29
【问题描述】:

我有两个视图:A 和 B。A 位于屏幕顶部,B 位于屏幕底部。

当用户按下按钮时,视图 B 会以 EaseInEaseOut 贝塞尔曲线向上动画,直到到达 y = 0。当 B 正在前往其目的地的途中,它应该在碰到 A 时将 A 向上推。换句话说,当 B 在从底部到顶部的过渡过程中通过了某个 y 坐标(A 的 y 原点 + 高度)时,A 应该粘在 B 上,所以看起来 B 将 A 向上推。

到目前为止我所尝试的:

  • 在用户按下按钮后立即将目标 + 选择器注册到 CADisplayLink。在这个选择器中,通过访问它的presentationLayer 请求视图B 的y 坐标,并相应地调整A 的y 坐标。但是,这种方法结果不够准确:presentationLayer 的帧落后于 B 在屏幕上的当前位置(这可能是因为 -presentationLayer 重新计算动画视图在当前时间的位置,这需要超过 1 帧) .当我增加 B 的动画持续时间时,这种方法效果很好。
  • 在用户按下按钮后立即将目标 + 选择器注册到 CADisplayLink。在此选择器中,通过求解贝塞尔方程 x = 经过时间/动画持续时间(应返回行进距离商/总距离)来计算 B 的当前 y 坐标。为此,我使用了 Apple 的开源 UnitBezier.h (http://opensource.apple.com/source/WebCore/WebCore-955.66/platform/graphics/UnitBezier.h)。但是,结果不正确。

对我接下来可以尝试什么有什么建议吗?

【问题讨论】:

    标签: iphone ios objective-c core-animation


    【解决方案1】:

    两个简单的解决方案:

    1. 仅使用animationWithDuration:您可以将动画分成两个嵌套动画,使用“ease in”为“B”移动到“A”设置动画,然后使用“ease out”为剩下的就是“B”和“A”。这里唯一的技巧是确保两个 duration 值有意义,这样动画的速度不会出现变化。

      CGFloat animationDuration = 0.5;
      CGFloat firstPortionDistance = self.b.frame.origin.y - (self.a.frame.origin.y + self.a.frame.size.height);
      CGFloat secondPortionDistance = self.a.frame.size.height;
      CGFloat firstPortionDuration = animationDuration * firstPortionDistance / (firstPortionDistance + secondPortionDistance);
      CGFloat secondPortionDuration = animationDuration * secondPortionDistance / (firstPortionDistance + secondPortionDistance);
      
      [UIView animateWithDuration:firstPortionDuration
                            delay:0.0
                          options:UIViewAnimationOptionCurveEaseIn
                       animations:^{
                           CGRect frame = self.b.frame;
                           frame.origin.y -= firstPortionDistance;
                           self.b.frame = frame;
                       }
                       completion:^(BOOL finished) {
                           [UIView animateWithDuration:secondPortionDuration
                                                 delay:0.0
                                               options:UIViewAnimationOptionCurveEaseOut
                                            animations:^{
                                                CGRect frame = self.b.frame;
                                                frame.origin.y -= secondPortionDistance;
                                                self.b.frame = frame;
      
                                                frame = self.a.frame;
                                                frame.origin.y -= secondPortionDistance;
                                                self.a.frame = frame;
                                            }
                                            completion:nil];
                       }];
      
    2. 您可以让animateWithDuration 处理“B”的完整动画,然后使用CADisplayLink 并使用presentationLayer 检索B 的当前frame 并相应地调整A 的帧:

      [self startDisplayLink];
      [UIView animateWithDuration:0.5
                            delay:0.0
                          options:UIViewAnimationOptionCurveEaseInOut
                       animations:^{
                           CGRect frame = self.b.frame;
                           frame.origin.y = self.a.frame.origin.y;
                           self.b.frame = frame;
                       }
                       completion:^(BOOL finished) {
                           [self stopDisplayLink];
                       }];
      

      其中启动、停止和处理显示链接的方法定义如下:

      - (void)startDisplayLink
      {
          self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)];
          [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
      }
      
      - (void)stopDisplayLink
      {
          [self.displayLink invalidate];
          self.displayLink = nil;
      }
      
      - (void)handleDisplayLink:(CADisplayLink *)displayLink
      {
          CALayer *presentationLayer = self.b.layer.presentationLayer;
      
          if (presentationLayer.frame.origin.y < (self.a.frame.origin.y + self.a.frame.size.height))
          {
              CGRect frame = self.a.frame;
              frame.origin.y = presentationLayer.frame.origin.y - self.a.frame.size.height;
              self.a.frame = frame;
          }
      }
      

    【讨论】:

    • 在类似的情况下,我采用了一种方法,其中两个视图都在整个距离上进行动画处理,但是在 A 的动画上使用带有 timeOffset 和 beginTime 的 CAAnimationGroup 以防止它在 B 命中之前实际显示它。 (请参阅 wangling.me/2011/06/time-warp-in-animation.html)不过,您的解决方案听起来更容易。
    【解决方案2】:

    您说您尝试了“动画 B 并使用显示链接更新 A”技术,结果导致“A”落后于“B”。理论上,您可以为新视图“C”设置动画,然后在显示链接中相应地调整 B 和 A 的帧,这应该消除任何滞后(相对于彼此)。

    【讨论】:

    • 出色且完全简单的解决方案。谢谢!
    【解决方案3】:

    您可以尝试以下算法:

    1)将A和B放在UIView中(即UIview *gropedView)

    2)改变 B.y 直到它等于 A.y+A.height(所以 B 将在 A 的正下方)

    3)动画分组视图

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-07
      相关资源
      最近更新 更多