手动动画
timeOffset
一个很有用的功能在于你可以它可以让你手动控制动画进程,通过设置speed
为0,可以禁用动画的自动播放,然后来使用timeOffset
来来回显示动画序列。这可以使得运用手势来手动控制动画变得很简单。
举个简单的例子:还是之前关门的动画,修改代码来用手势控制动画。我们给视图添加一个UIPanGestureRecognizer
,然后用timeOffset
左右摇晃。
因为在动画添加到图层之后不能再做修改了,我们来通过调整layer
的timeOffset
达到同样的效果(清单9.4)。
清单9.4 通过触摸手势手动控制动画
1 @interface ViewController () 2 3 @property (nonatomic, weak) UIView *containerView; 4 @property (nonatomic, strong) CALayer *doorLayer; 5 6 @end 7 8 @implementation ViewController 9 10 - (void)viewDidLoad 11 { 12 [super viewDidLoad]; 13 //add the door 14 self.doorLayer = [CALayer layer]; 15 self.doorLayer.frame = CGRectMake(0, 0, 128, 256); 16 self.doorLayer.position = CGPointMake(150 - 64, 150); 17 self.doorLayer.anchorPoint = CGPointMake(0, 0.5); 18 self.doorLayer.contents = (__bridge id)[UIImage imageNamed:@"Door.png"].CGImage; 19 [self.containerView.layer addSublayer:self.doorLayer]; 20 //apply perspective transform 21 CATransform3D perspective = CATransform3DIdentity; 22 perspective.m34 = -1.0 / 500.0; 23 self.containerView.layer.sublayerTransform = perspective; 24 //add pan gesture recognizer to handle swipes 25 UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] init]; 26 [pan addTarget:self action:@selector(pan:)]; 27 [self.view addGestureRecognizer:pan]; 28 //pause all layer animations 29 self.doorLayer.speed = 0.0; 30 //apply swinging animation (which won‘t play because layer is paused) 31 CABasicAnimation *animation = [CABasicAnimation animation]; 32 animation.keyPath = @"transform.rotation.y"; 33 animation.toValue = @(-M_PI_2); 34 animation.duration = 1.0; 35 [self.doorLayer addAnimation:animation forKey:nil]; 36 } 37 38 - (void)pan:(UIPanGestureRecognizer *)pan 39 { 40 //get horizontal component of pan gesture 41 CGFloat x = [pan translationInView:self.view].x; 42 //convert from points to animation duration //using a reasonable scale factor 43 x /= 200.0f; 44 //update timeOffset and clamp result 45 CFTimeInterval timeOffset = self.doorLayer.timeOffset; 46 timeOffset = MIN(0.999, MAX(0.0, timeOffset - x)); 47 self.doorLayer.timeOffset = timeOffset; 48 //reset pan gesture 49 [pan setTranslation:CGPointZero inView:self.view]; 50 } 51 52 @end
这其实是个小诡计,也许相对于设置个动画然后每次显示一帧而言,用移动手势来直接设置门的transform
会更简单。
在这个例子中的确是这样,但是对于比如说关键这这样更加复杂的情况,或者有多个图层的动画组,相对于实时计算每个图层的属性而言,这就显得方便的多了。