隐式动画虽然能够实现动画效果,但并不能涵盖所有的动画类型。很多复杂的动画是需要我们自定义动画的,
它能够对一些属性做指定 的自定义动画,或者创建非线性动画,比如沿着任意一条曲线移动。
显示动画主要是由CAAnimation类提供,而我们主要使用CAAnimation的四个子类,下面做一一介绍。
- 属性动画:CAPropertyAnimaition
- 基础动画CABasicAnimation
- 关键帧动画CAKeyframeAnimation
CABasicAnimation:基础动画主要有三个属性,fromValue,toValue,byValue。不过三个属性被封装成对象。
缺点:由于显式动画只是一段动画效果,不会改变属性的真正状态,所以需要我们手动在动画效果后重新设置属性的值。
一般对属性赋新值可以在两个位置,1.在动画效果之前 2.在动画效果之后。而在动画效果之前改变属性值比较简单。
在动画效果之前改变属性值需要注意两个点:
- 改变属性值最好关闭隐式动画(虽然显式动画会覆盖隐式动画)
- 做动画的图层是从当前图层还是模型图层开始。
- (void)applyBasicAnimation:(CABasicAnimation *)animation
toLayer:(CALayer *)layer
{
//set the from value (using presentation layer if available)
animation.
fromValue
= [layer.
presentationLayer
?: layer // ——— 2
valueForKeyPath:animation.keyPath];
//update the property in advance
//note: this approach will only work if toValue != nil
[
CATransaction
begin
]; // ——— 1
[CATransaction setDisableActions:YES];
layer.backgroundColor = (__bridge CGColorRef)(animation.toValue) ;
[CATransaction commit];
//apply animation to layer
[layer addAnimation:animation forKey:nil];
//apply animation to layer
[layer addAnimation:animation forKey:nil];
}
- (IBAction)changeColor
{
//create a new random color
CGFloat red = arc4random() / (CGFloat)INT_MAX;
CGFloat green = arc4random() / (CGFloat)INT_MAX;
CGFloat blue = arc4random() / (CGFloat)INT_MAX;
UIColor *color = [UIColor colorWithRed:red
green:green
blue:blue
{
//create a new random color
CGFloat red = arc4random() / (CGFloat)INT_MAX;
CGFloat green = arc4random() / (CGFloat)INT_MAX;
CGFloat blue = arc4random() / (CGFloat)INT_MAX;
UIColor *color = [UIColor colorWithRed:red
green:green
blue:blue
alpha:1.0];
//create a basic animation
CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = @"backgroundColor";
CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = @"backgroundColor";
animation.toValue = (__bridge id)color.CGColor;
//apply animation without snap-back
[self applyBasicAnimation:animation toLayer:self.colorLayer];
[self applyBasicAnimation:animation toLayer:self.colorLayer];
}
CABasicAnimation
揭示了大多数隐式动画背后依赖的机制,这的确很有趣,但是显式地给图层添加
CABasicA nimation
相较于隐式动画而言,只能说费力不讨好。
CAKeyframeAnimation:关键帧动画
是另一种UIKit没有暴露出来但功能强大的类。和CABasicAnimation不同的是,
限制于设置一个起始和结束的值,而是可以根据一连串随意的值来做动画。
- (IBAction)changeColor
{
//create a keyframe animation
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
animation.keyPath = @"backgroundColor";
animation.duration = 5;
animation.values = @[
(__bridge id)[UIColor blueColor].CGColor,
(__bridge id)[UIColor redColor].CGColor,
(__bridge id)[UIColor greenColor].CGColor,
(__bridge id)[UIColor blueColor].CGColor
];
//apply animation to layer
[self.colorLayer addAnimation:animation forKey:nil];
{
//create a keyframe animation
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
animation.keyPath = @"backgroundColor";
animation.duration = 5;
animation.values = @[
(__bridge id)[UIColor blueColor].CGColor,
(__bridge id)[UIColor redColor].CGColor,
(__bridge id)[UIColor greenColor].CGColor,
(__bridge id)[UIColor blueColor].CGColor
];
//apply animation to layer
[self.colorLayer addAnimation:animation forKey:nil];
}
如代码所示,设置values属性值可以让CALayer按照设定的值做动画,但用数组来描述动画运动并不直观,设置path属性将会更加直观。虽然Core Graphics给我们提供绘制CGPath的方法,但是使用UIKit提供的UIBezierPath会更加方便。
- (void)viewDidLoad
{
{
[super viewDidLoad];
//create a path
UIBezierPath *bezierPath = [[UIBezierPath alloc] init];
[bezierPath moveToPoint:CGPointMake(0, 150)];
[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 = bezierPath.CGPath;
pathLayer.fillColor = [UIColor clearColor].CGColor;
pathLayer.strokeColor = [UIColor redColor].CGColor;
pathLayer.lineWidth = 3.0f;
[self.containerView.layer addSublayer:pathLayer];
//add the ship------
CALayer *shipLayer = [CALayer layer];
shipLayer.frame = CGRectMake(0, 0, 64, 64);
shipLayer.position = CGPointMake(0, 150);
shipLayer.contents = (__bridge id)[UIImage imageNamed:@"Ship.png"].CGImage;
UIBezierPath *bezierPath = [[UIBezierPath alloc] init];
[bezierPath moveToPoint:CGPointMake(0, 150)];
[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 = bezierPath.CGPath;
pathLayer.fillColor = [UIColor clearColor].CGColor;
pathLayer.strokeColor = [UIColor redColor].CGColor;
pathLayer.lineWidth = 3.0f;
[self.containerView.layer addSublayer:pathLayer];
//add the ship------
CALayer *shipLayer = [CALayer layer];
shipLayer.frame = CGRectMake(0, 0, 64, 64);
shipLayer.position = CGPointMake(0, 150);
shipLayer.contents = (__bridge id)[UIImage imageNamed:@"Ship.png"].CGImage;
[self.containerView.layer addSublayer:shipLayer];
//create the keyframe animation
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
animation.keyPath = @"position";
animation.duration = 4.0;
animation.path = bezierPath.CGPath;
[shipLayer addAnimation:animation forKey:nil];
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
animation.keyPath = @"position";
animation.duration = 4.0;
animation.path = bezierPath.CGPath;
[shipLayer addAnimation:animation forKey:nil];
}
属性动画主要是作用于属性,介绍完属性动画CAPropertyAnimaition后。现在介绍动画组CAAnimationGroup,动画组主要是将几个动画组合在一起。
CAAnimationGroup类有一个animations属性。用法比较简单。
CAAnimation还有一个子类:CATransition 过渡动画。不是所有视图的属性都是动画属性,如果要改变不能动画的属性(如title,image...),或者添加移除图层,属性动画将起不到作用。这时候我们可以使用过渡动画。(例子在隐式动画中就有)