8

AとBの2つのビューがあります。Aは画面の上部に配置され、Bは画面の下部に配置されます。

ユーザーがボタンを押すと、ビューBはy = 0に達するまで、EaseInEaseOutベジェ曲線で上向きにアニメーション化します。Bが目的地に向かう途中で、Aに当たったときにAを押し上げる必要があります。下から上への遷移中に特定の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

2つの簡単な解決策:

  1. 使用animationWithDurationのみ:アニメーションを2つのネストされたアニメーションに分割できます。「イーズイン」を使用して「B」から「A」までの移動をアニメーション化し、「イーズアウト」を使用して「B」と「A」の移動をアニメーション化します。 「残りの道。ここでの唯一の秘訣durationは、アニメーションの速度が変化していないように見えるように、2つの値が意味をなすようにすることです。

    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して、presentationLayerBの電流を取得し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) A.y+A.height と等しくなるまで By を変更します (したがって、B は A の真下になります)。

3) groupedView をアニメーション化する

于 2013-03-12T19:22:01.517 に答える