手动动画

timeOffset一个很有用的功能在于你可以它可以让你手动控制动画进程,通过设置speed为0,可以禁用动画的自动播放,然后来使用timeOffset来来回显示动画序列。这可以使得运用手势来手动控制动画变得很简单。

举个简单的例子:还是之前关门的动画,修改代码来用手势控制动画。我们给视图添加一个UIPanGestureRecognizer,然后用timeOffset左右摇晃。

因为在动画添加到图层之后不能再做修改了,我们来通过调整layertimeOffset达到同样的效果(清单9.4)。

清单9.4 通过触摸手势手动控制动画

  1. @interface ViewController ()
  2. @property (nonatomic, weak) UIView *containerView;
  3. @property (nonatomic, strong) CALayer *doorLayer;
  4. @end
  5. @implementation ViewController
  6. - (void)viewDidLoad
  7. {
  8. [super viewDidLoad];
  9. //add the door
  10. self.doorLayer = [CALayer layer];
  11. self.doorLayer.frame = CGRectMake(0, 0, 128, 256);
  12. self.doorLayer.position = CGPointMake(150 - 64, 150);
  13. self.doorLayer.anchorPoint = CGPointMake(0, 0.5);
  14. self.doorLayer.contents = (__bridge id)[UIImage imageNamed:@"Door.png"].CGImage;
  15. [self.containerView.layer addSublayer:self.doorLayer];
  16. //apply perspective transform
  17. CATransform3D perspective = CATransform3DIdentity;
  18. perspective.m34 = -1.0 / 500.0;
  19. self.containerView.layer.sublayerTransform = perspective;
  20. //add pan gesture recognizer to handle swipes
  21. UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] init];
  22. [pan addTarget:self action:@selector(pan:)];
  23. [self.view addGestureRecognizer:pan];
  24. //pause all layer animations
  25. self.doorLayer.speed = 0.0;
  26. //apply swinging animation (which won't play because layer is paused)
  27. CABasicAnimation *animation = [CABasicAnimation animation];
  28. animation.keyPath = @"transform.rotation.y";
  29. animation.toValue = @(-M_PI_2);
  30. animation.duration = 1.0;
  31. [self.doorLayer addAnimation:animation forKey:nil];
  32. }
  33. - (void)pan:(UIPanGestureRecognizer *)pan
  34. {
  35. //get horizontal component of pan gesture
  36. CGFloat x = [pan translationInView:self.view].x;
  37. //convert from points to animation duration //using a reasonable scale factor
  38. x /= 200.0f;
  39. //update timeOffset and clamp result
  40. CFTimeInterval timeOffset = self.doorLayer.timeOffset;
  41. timeOffset = MIN(0.999, MAX(0.0, timeOffset - x));
  42. self.doorLayer.timeOffset = timeOffset;
  43. //reset pan gesture
  44. [pan setTranslation:CGPointZero inView:self.view];
  45. }
  46. @end

这其实是个小诡计,也许相对于设置个动画然后每次显示一帧而言,用移动手势来直接设置门的transform会更简单。

在这个例子中的确是这样,但是对于比如说关键这这样更加复杂的情况,或者有多个图层的动画组,相对于实时计算每个图层的属性而言,这就显得方便的多了。