我需要在减速时点击并拖动屏幕上的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
}];
答案 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。当速度变为负值时,你必须停止每个时间增量的迭代。