8

我有两个视图: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 )。然而,结果并不正确。

关于下一步我可以尝试什么的任何建议?

4

3 回答 3

4

两个简单的解决方案:

  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;
        }
    }
    
于 2013-03-12T17:13:21.403 回答
2

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

于 2013-03-12T18:26:09.410 回答
0

您可以尝试以下算法:

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

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

3)动画分组视图

于 2013-03-12T19:22:01.517 回答