用减速移动UIView

时间:2011-02-27 04:05:28

标签: iphone objective-c touch

我需要在减速时点击并拖动屏幕上的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
                }];

3 个答案:

答案 0 :(得分: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重定位。

答案 1 :(得分:3)

使用核心动画。如果你看一下UIView的文档,那就非常简单 - 创建一个设置视图最终位置的动画,并指定UIViewAnimationOptionCurveEaseOut作为时序曲线。整个过程只需要几行就可以实现。

答案 2 :(得分:0)

您可以使用恒定加速度使对象停止以恒定的减速速度移动。或者您可以根据您是否希望减速度在达到零速度时更快/更慢来增加或减少加速度。

struct Velocity {
float x;
float y; }

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

Vector newVelocity = oldVelocity + acceleration * timeDelta;

Vector newPosition = newVelocity * timeDelta;

假设你的行进方向有一个标准化的向量,而你只是反对那个方向,你可以使用float而不是Vector。

您必须在边框上绑定newPosition。当速度变为负值时,你必须停止每个时间增量的迭代。