2011-02-27 80 views
5

我需要點擊並拖動屏幕上的UIView減速。我已經編寫了很好的代碼,用於通過觸摸來移動視圖,但需要物體保持一定的慣性(一旦達到特定的加速度閾值),減速直至停止或滿足屏幕邊界。這不是一款遊戲,而是使用一些標準的UIView控件。我最大的努力就是加速。隨着減速移動UIView

你已經寫出來的任何好的算法來完成相同的任務嗎?

編輯:

我使用的touchesEnded:方法的動畫塊,但有一個人讓去他們的手指和動畫踢的時間之間的明顯的延遲:

[UIView transitionWithView:self 
        duration:UI_USER_INTERFACE_IDIOM() == 
           UIUserInterfaceIdiomPhone ? 0.33f : 0.33f * 2.0f 
        options:UIViewAnimationCurveEaseOut 
       animations:^(void){ 
         if (dir == 1) // Flicked left 
         { 
          self.center = CGPointMake(self.frame.size.width * 0.5f, 
                 self.center.y); 
         } 
         else { // Flicked right 
          self.center = CGPointMake(
           self.superview.bounds.size.width - 
            (self.frame.size.width * 0.5f), self.center.y); 
         } 
        } 
       completion:^(BOOL finished){ 
        // Do nothing 
       }]; 

回答

6

問題在於用於動畫的計時功能。動畫應該像用戶在第一次拖動一樣快,然後迅速減速。以下代碼顯示了實現此行爲的一個非常簡單的示例。

首先,在我的touchesBegan:withEvent:方法中,我將第一個觸摸位置記錄到了我的點緩衝區。我緩衝兩個觸摸位置以獲取視圖的運動矢量,並且可能有不同的獲取矢量的方法。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 
    ivar_lastPoint[0] = [[touches anyObject] locationInView:self]; 
    ivar_lastPoint[1] = ivar_lastPoint[0]; 
    ivar_touchOffset.x = ivar_lastPoint[0].x - self.sprite.position.x; 
    ivar_touchOffset.y = ivar_lastPoint[0].y - self.sprite.position.y; 
    self.lastTime = [NSDate date]; 
} 

然後,在touchesMoved:withEvent:方法中,我更新了視圖的位置。實際上,我使用CALayer而不是視圖,因爲我想爲其動畫使用自定義定時功能。所以,我根據用戶更新圖層的位置,並且在給定的時間間隔內更新位置緩衝區。

#define kSampleInterval 0.02f 

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { 

    [CATransaction begin]; 
    [CATransaction setDisableActions:YES]; 
    /* First of all, move the object */ 
    CGPoint currentPoint = [[touches anyObject] locationInView:self]; 
    CGPoint center = self.sprite.position; 
    center.x = currentPoint.x - ivar_touchOffset.x; 
    center.y = currentPoint.y - ivar_touchOffset.y; 
    self.sprite.position = center; 

    /* Sample locations */ 
    NSDate *currentTime = [NSDate date]; 
    NSTimeInterval interval = [currentTime timeIntervalSinceDate:self.lastTime]; 
    if (interval > kSampleInterval) { 
     ivar_lastPoint[0] = ivar_lastPoint[1]; 
     ivar_lastPoint[1] = currentPoint; 
     self.lastTime = currentTime; 
     self.lastInterval = interval; 

    } 
    [CATransaction commit]; 
} 

self.sprite是對我的觀點CALayer對象的引用。我不需要拖動動畫,因此我使用CATransaction類對象禁用了它。

最後,我計算矢量並在touchesEnded:withEvent:方法中應用動畫。在這裏,我創建了一個自定義的CAMediaTimingFunction,所以它實際上是「快進,緩出」。

#define kDecelerationDuration 1.0f 
#define kDamping 5.0f 

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 
    CGPoint targetPoint; 
    NSDate *currentTime = [NSDate date]; 
    NSTimeInterval interval = self.lastInterval + [currentTime timeIntervalSinceDate:self.lastTime]; 
    targetPoint.x = self.sprite.position.x + (ivar_lastPoint[1].x - ivar_lastPoint[0].x)/interval*kDecelerationDuration/kDamping; 
    targetPoint.y = self.sprite.position.y + (ivar_lastPoint[1].y - ivar_lastPoint[0].y)/interval*kDecelerationDuration/kDamping; 
    if (targetPoint.x < 0) { 
     targetPoint.x = 0; 
    } else if (targetPoint.x > self.bounds.size.width) { 
     targetPoint.x = self.bounds.size.width; 
    } 
    if (targetPoint.y < 0) { 
     targetPoint.y = 0; 
    } else if (targetPoint.y > self.bounds.size.height) { 
     targetPoint.y = self.bounds.size.height; 
    } 
    CAMediaTimingFunction *timingFunction = [CAMediaTimingFunction functionWithControlPoints: 
              0.1f : 0.9f :0.2f :1.0f]; 

    [CATransaction begin]; 
    [CATransaction setValue:[NSNumber numberWithFloat:kDecelerationDuration] forKey:kCATransactionAnimationDuration]; 
    [CATransaction setAnimationTimingFunction:timingFunction]; 

    self.sprite.position = targetPoint; 
    [CATransaction commit]; 

} 

這是一個非常簡單的例子。你可能想要一個更好的向量獲取機制。此外,這隻會移動一個可視化組件(CALayer)。您可能需要一個UIView對象來處理來自該對象的事件。在這種情況下,您可能想要通過CALayer進行動畫處理,並分別移動實際的UIView對象。可以有多種方式來處理CALayer動畫和UIView重定位。

+0

請輸入示例代碼? – 2011-03-10 00:52:05

+0

我意識到'touchesMoved:withEvent:'中的動畫不適合你的情況,因爲在這種情況下,用戶在脫下手指之前不應該開始動畫。但是,我也意識到,當您提交動畫和實際開始動畫之間的時間差別不應該只是簡單地移動視圖。問題在於計時功能,所以動畫的開始應該足夠快以感覺運動是連續的。查看我的代碼更新的答案。 – MHC 2011-03-10 03:33:36

0

您可以使用恆定的加速度使對象停止以恆定的速度慢速移動。或者,您可以使加速度增加或減小,具體取決於您是否希望減速到達零速度點時更快/更慢。

struct Velocity { 
float x; 
float y; } 

Vector acceleration = your acceleration equation. // can be constant

Vector newVelocity = oldVelocity + acceleration * timeDelta;

Vector newPosition = newVelocity * timeDelta;

假設你有一個標準化的矢量爲您的出行的方向,你只是打擊你可以用浮動的,而不是向量中的方向。

您必須在邊界上綁定newPosition。當速度變爲負值時,您必須停止每時間增量迭代。

+0

因此,這是創建矢量的一個很好的答案,但是如何在UIView和觸摸事件的上下文中實現它。我目前的實現需要一個時間戳,當觸摸什麼時候開始和什麼時候結束,加上觸摸開始和結束時視圖的位置。然後我使用一個動畫塊來將視圖從一端移動到另一端,但是從觸摸結束和動畫執行時開始有明顯的延遲。我如何使這更加流暢? – 2011-03-07 15:23:41

3

使用核心動畫。如果您查看UIView的文檔,則非常簡單 - 創建一個設置視圖最終位置的動畫,並指定UIViewAnimationOptionCurveEaseOut作爲時序曲線。整個事情將需要您執行幾條線。

+0

我已經有了這個部分,但它保持了更加棘手的加速度。看到我留給Scott的帖子的評論。 – 2011-03-07 17:45:36

+0

太棒了 - 聽起來你已經在使用Core Animation來移動視圖了。當觸摸結束時,您將再創建一個如上所述的動畫。根據視圖的當前速度縮放從當前位置到最終位置的距離以及動畫持續時間。我猜測這更像是讓它看起來比擁有精確的物理模型更好,但如果你想要更多地控制減速曲線,你可以做一系列短小的連接動畫。 – Caleb 2011-03-07 17:59:20