El problema está en la función de temporización utilizada para la animación. La animación debe ser tan rápida como la del usuario al arrastrar la primera y desacelera rápidamente. El siguiente código muestra un ejemplo muy simple de implementación de este comportamiento.
Primero, en mi método touchesBegan:withEvent:
, grabé la primera ubicación táctil en mi buffer de puntos. Estoy almacenando dos ubicaciones táctiles para obtener el vector de movimiento de la vista, y podría haber diferentes formas de obtener el vector.
- (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];
}
Luego, en touchesMoved:withEvent:
método, he actualizado la ubicación de la vista. En realidad, utilicé un CALayer en lugar de una vista, ya que quiero utilizar una función de temporización personalizada para su animación. Entonces, actualizo la ubicación de la capa de acuerdo con el usuario, y durante un intervalo dado, actualizo los búferes de ubicación.
#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
es una referencia al objeto CALayer en mi opinión. No necesito animaciones para arrastrar, así que lo desactivé usando el objeto de clase CATransaction.
Finalmente, calculo el vector y aplico la animación en el método touchesEnded:withEvent:
.Aquí, creé una función CAMediaTimingFunction personalizada, por lo que es realmente "entrada rápida, salida fácil".
#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];
}
Este es un ejemplo muy simple. Es posible que desee un mejor mecanismo de obtención de vectores. Además, esto solo mueve un componente visual (CALayer). Probablemente necesites un objeto UIView para manejar eventos del objeto. En este caso, es posible que desee animar a través de CALayer y mover el objeto UIView real por separado. Podría haber múltiples formas de manejar la animación CALayer y la reubicación UIView juntas.
Código de ejemplo, por favor? –
Me di cuenta de que la animación en 'touchesMoved: withEvent:' no funcionaría para su caso, porque en este caso, no debe comenzar la animación antes de que el usuario se quite un dedo. Sin embargo, también me di cuenta de que la diferencia de tiempo entre el momento en que se compromete una animación y cuándo realmente comienza no debería importar simplemente mover una vista. El problema es más bien en la función de tiempo, por lo que el comienzo de la animación debe ser lo suficientemente rápido como para sentir que el movimiento es continuo. Vea mi respuesta actualizada para el código. – MHC