2013-03-12 35 views
8

我有兩個視圖:A和B.A位於屏幕的頂部,B位於屏幕的底部。Objective-C中的「推動」動畫

當用戶按下按鈕時,視圖B用EaseInEaseOut貝塞爾曲線向上動畫,直到它達到y = 0。當B正在到達目的地的路上時,它應該在A點擊時A向上。換句話說,當B從底部到頂部過渡期間,B已經通過了某個Y座標(A的y原點+高度)時,A應該堅持B,這樣看起來B向上推A。

我迄今爲止嘗試:

  • 註冊一個目標+選擇到CADisplayLink後,立即在用戶按下按鈕。在這個選擇器中,通過訪問其presentationLayer並相應地調整A的y座標來請求視圖B的y座標。然而,這種方法結果不夠準確:presentationLayer的框架落在B屏幕上的當前位置(這可能是因爲-presentationLayer重新計算了當前時間的動畫視圖的位置,這需要超過1幀) 。當我增加B的動畫持續時間時,此方法正常工作。
  • 在用戶按下按鈕後立即將目標+選擇器註冊到CADisplayLink。在此選擇器內部,通過求解x =經過時間/動畫持續時間(應該返回行距/總距離的商距)的貝塞爾方程來計算B的當前y座標。我使用蘋果公司的開源UnitBezier.h(http://opensource.apple.com/source/WebCore/WebCore-955.66/platform/graphics/UnitBezier.h)。但是,結果不正確。

關於我可以嘗試下一步的任何建議?

回答

2

你說你嘗試過「使用動畫B並使用顯示鏈接更新A」技術,並且它導致「A」滯後於「B」。理論上,您可以創建一個新視圖「C」,然後在顯示鏈接中相應地調整B和A的幀,從而消除任何延遲(相對於彼此)。

+0

絕妙而簡單的解決方案。謝謝! – datwelk 2013-03-12 18:35:51

4

兩個簡單的解決方案:

  1. 使用animationWithDuration只有:你可以把你的動畫到兩個嵌套的動畫,使用「緩解」動畫「B」的運動達人「A」,然後使用「放鬆」來動畫「B」和「A」的移動。這裏唯一的技巧是確保兩個值是合理的,這樣動畫的速度看起來不會改變。

    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並相應調整的框架:

    [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; 
        } 
    } 
    
+0

在類似的情況下,我已經與其中兩個視圖動畫的全距離的方法,但用一個timeOffset一個CAAnimationGroup和beginTime在A的動畫上,以防止它在B點擊它之前實際顯示。 (請參閱http://wangling.me/2011/06/time-warp-in-animation.html)然而,您的解決方案聽起來更容易。 – 2013-03-12 17:29:02

0

你可以試着跟隨着algorythm:

1)把A和B的UIView(即UIView的* gropedView)

2)更改B.y直到它將等於A.y + A。高度(因此B將是下右)

3)動畫groupedView