Проблема в функции синхронизации, используемой для анимации. Анимация должна быть такой же быстрой, как перетаскивание пользователем в первую очередь, и быстро замедляться. Следующий код показывает очень простой пример реализации этого поведения.
Во-первых, в моем методе 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:
. Здесь я создал собственную функцию CAMediaTiming, поэтому она действительно «быстрая, легкая».
#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 вместе.