iOS Core Animation Advanced Techniques-图层时间
上八章节:
这篇随笔主要介绍有关图层时间。
- CAMediaTiming协议:
- 定义了在一段动画内用来控制逝去时间的属性的集合
- CALayer和CAAnimation都实现了这个协议,所以时间可以被任意基于一个图层或者一段动画的类控制
- 属性:
- duration:
- 指定动画的一次迭代的时间(默认0,代表默认时间0.25)
- repeatCount:
- 动画重复的迭代次数(默认0,代表默认次数1)
- /*
- 完整的动画时长=duration*repeatCount
- */
- 示范例子:
- //add the ship
- self.shipLayer = [CALayer layer];
- self.shipLayer.frame = CGRectMake(0, 0, 128, 128);
- self.shipLayer.position = CGPointMake(150, 150);
- self.shipLayer.contents = (__bridge id)[UIImage imageNamed: @"Ship.png"].CGImage;
- [self.containerView.layer addSublayer:self.shipLayer];
- CFTimeInterval duration = 3.0;
- float repeatCount = 10.0;
- CABasicAnimation *animation = [CABasicAnimation animation];
- animation.keyPath = @"transform.rotation";
- animation.duration = duration;
- animation.repeatCount = repeatCount;
- animation.byValue = @(M_PI * 2);
- [self.shipLayer addAnimation:animation forKey:@"rotateAnimation"];
- duration:
-
-
- repeatDuration:
- 让动画重复一个指定的时间
- autoreverses:
- 是否在每次间隔交替循环过程中自动回放
- /*
- repeatCount和repeatDuration可能会相互冲突,所以你只要对其中一个指定非零值
- */
- 示范例子:
- //add the door
- CALayer *doorLayer = [CALayer layer];
- doorLayer.frame = CGRectMake(0, 0, 128, 256);
- doorLayer.position = CGPointMake(150 - 64, 150);
- doorLayer.anchorPoint = CGPointMake(0, 0.5);
- doorLayer.contents = (__bridge id)[UIImage imageNamed: @"Door.png"].CGImage;
- [self.containerView.layer addSublayer:doorLayer];
- //apply perspective transform
- CATransform3D perspective = CATransform3DIdentity;
- perspective.m34 = -1.0 / 500.0;
- self.containerView.layer.sublayerTransform = perspective;
- //apply swinging animation
- CABasicAnimation *animation = [CABasicAnimation animation];
- animation.keyPath = @"transform.rotation.y";
- animation.toValue = @(-M_PI_2);
- animation.duration = 2.0;
- animation.repeatDuration = INFINITY;
- animation.autoreverses = YES;
- [doorLayer addAnimation:animation forKey:nil];
- beginTime:
- 指定了动画开始之前的的延迟时间(默认0,动画马上执行)
- speed:
- 一个时间的倍数,默认1.0
- 减少它会减慢图层/动画的时间;
- 增加它会加快速度
- timeOffset:
- 让动画快进到某一点
- 例如:
- 对于一个持续1秒的动画来说,设置timeOffset为0.5意味着动画将从一半的地方开始。
- 不受speed的影响
- 示范例子:
- //create a path
- self.bezierPath = [[UIBezierPath alloc] init];
- [self.bezierPath moveToPoint:CGPointMake(0, 150)];
- [self.bezierPath addCurveToPoint:CGPointMake(300, 150) controlPoint1:CGPointMake(75, 0) controlPoint2:CGPointMake(225, 300)];
- //draw the path using a CAShapeLayer
- CAShapeLayer *pathLayer = [CAShapeLayer layer];
- pathLayer.path = self.bezierPath.CGPath;
- pathLayer.fillColor = [UIColor clearColor].CGColor;
- pathLayer.strokeColor = [UIColor redColor].CGColor;
- pathLayer.lineWidth = 3.0f;
- [self.containerView.layer addSublayer:pathLayer];
- //add the ship
- self.shipLayer = [CALayer layer];
- self.shipLayer.frame = CGRectMake(0, 0, 64, 64);
- self.shipLayer.position = CGPointMake(0, 150);
- self.shipLayer.contents = (__bridge id)[UIImage imageNamed: @"Ship.png"].CGImage;
- [self.containerView.layer addSublayer:self.shipLayer];
- //create the keyframe animation
- CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
- animation.keyPath = @"position";
- animation.timeOffset = self.timeOffsetSlider.value;
- animation.speed = self.speedSlider.value;
- animation.duration = 1.0;
- animation.path = self.bezierPath.CGPath;
- animation.rotationMode = kCAAnimationRotateAuto;
- animation.removedOnCompletion = NO;//在动画结束的时候仍然保持之前的状态
- [self.shipLayer addAnimation:animation forKey:@"slide"];
- fillMode:
- 避免在动画结束的时候急速返回,当用它来解决这个问题的时候,
- 需要把removeOnCompletion设置为NO
- 另外需要给动画添加一个非空的键,于是可以在不需要动画的时候把它从图层上移除。
- 一个NSString类型
- 接受如下四种常量:
- kCAFillModeForwards
- 向前
- kCAFillModeBackwards
- 向后
- kCAFillModeBoth
- 向前又向后去填充动画状态
- kCAFillModeRemoved(默认值)
- kCAFillModeForwards
- 当动画不再播放的时候就显示图层模型指定的值
- repeatDuration:
-
- 层级关系时间:
- 每个动画和图层在时间上都有它自己的层级概念,相对于它的父亲来测量(类似与子图层的坐标体系)
- 对图层调整时间将会影响到它本身和子图层的动画,但不会影响到父图层。
- /*
- beginTime,timeOffset和speed属性将会影响到子动画
- 但是对CALayer或者CAGroupAnimation调整duration和repeatCount/repeatDuration属性并不会影响到子动画
- */
- 为了同步不同图层之间有不同的speed,timeOffset和beginTime的动画
- CALayer同样也提供了方法来转换不同图层之间的本地时间。如下:
- - (CFTimeInterval)convertTime:(CFTimeInterval)t fromLayer:(CALayer *)l;
- - (CFTimeInterval)convertTime:(CFTimeInterval)t toLayer:(CALayer *)l;
- 暂停,倒回和快进:
- 如果移除图层正在进行的动画,图层将会急速返回动画之前的状态。
- 给图层添加一个CAAnimation实际上是给动画对象做了一个不可改变的拷贝,所以对原始动画对象属性的改变对真实的动画并没有作用。
- 直接用-animationForKey:来检索图层正在进行的动画可以返回正确的动画对象,但是修改它的属性将会抛出异常。
- 如果在动画移除之前拷贝呈现图层到模型图层,动画将会看起来暂停在那里。但是不好的地方在于之后就不能再恢复动画了。
- 解决方案:
- 通过属性speed实现暂停,倒回,快进:
- 暂停:
- 把图层的speed设置成0,它会暂停任何添加到图层上的动画
- 倒回:
- 设置成一个负值将会倒回动画。
- 快进:
- 设置speed大于1.0将会快进
- /*
- 通过增加主窗口图层的speed,可以暂停整个应用程序的动画。
- */
- 暂停:
- 通过属性speed实现暂停,倒回,快进:
- 手动动画:
- 通过timeOffset手动控制动画进程
- 再通过speed设置为0,禁用动画的自动播放
- 再使用timeOffset来回显示动画序列
- 示范例子:
- //add the door
- self.doorLayer = [CALayer layer];
- self.doorLayer.frame = CGRectMake(0, 0, 128, 256);
- self.doorLayer.position = CGPointMake(150 - 64, 150);
- self.doorLayer.anchorPoint = CGPointMake(0, 0.5);
- self.doorLayer.contents = (__bridge id)[UIImage imageNamed:@"Door.png"].CGImage;
- [self.containerView.layer addSublayer:self.doorLayer];
- //apply perspective transform
- CATransform3D perspective = CATransform3DIdentity;
- perspective.m34 = -1.0 / 500.0;
- self.containerView.layer.sublayerTransform = perspective;
- //add pan gesture recognizer to handle swipes
- UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] init];
- [pan addTarget:self action:@selector(pan:)];
- [self.view addGestureRecognizer:pan];
- //pause all layer animations
- self.doorLayer.speed = 0.0;
- //apply swinging animation (which won't play because layer is paused)
- CABasicAnimation *animation = [CABasicAnimation animation];
- animation.keyPath = @"transform.rotation.y";
- animation.toValue = @(-M_PI_2);
- animation.duration = 1.0;
- [self.doorLayer addAnimation:animation forKey:nil];
- - (void)pan:(UIPanGestureRecognizer *)pan{
- //get horizontal component of pan gesture
- CGFloat x = [pan translationInView:self.view].x;
- //convert from points to animation duration //using a reasonable scale factor
- x /= 200.0f;
- //update timeOffset and clamp result
- CFTimeInterval timeOffset = self.doorLayer.timeOffset;
- timeOffset = MIN(0.999, MAX(0.0, timeOffset - x));
- self.doorLayer.timeOffset = timeOffset;
- //reset pan gesture
- [pan setTranslation:CGPointZero inView:self.view];//重置手势
- }