首页 > 代码库 > 长路漫漫,唯剑作伴--Core Animation

长路漫漫,唯剑作伴--Core Animation

一、UIView和CALayer

  1. 关系

    • 在创建UIView对象时,UIView内部会自动创建一个层(即CALayer对象),通过UIView的layer属性可以访问这个层。当UIView需要显示到屏幕上时,会调用drawRect:方法进行绘图,并且会将所有内容绘制在自己的层上,绘图完毕后,系统会将层拷贝到屏幕上,于是就完成了UIView的显示。

    • 换句话说,UIView本身不具备显示的功能,是它内部的层才有显示功能。

    • 两者都有树状层级结构,layer 内部有 SubLayers,View 内部有 SubViews.但是 Layer 比 View 多了个AnchorPoint
  2. 不同

    • 两者最明显的区别是 View可以接受并处理事件,而 Layer 不可以.
    • UIView主要是对显示内容的管理而 CALayer 主要侧重显示内容的绘制。

    • 在做 iOS 动画的时候,修改非 RootLayer的属性(譬如位置、背景色等)会默认产生隐式动画,而修改UIView则不会。

  3. 修改CALayer的属性

    - (IBAction)changeCorners:(UIButton *)sender {
        self.layer.cornerRadius = (self.layer.cornerRadius == 0.0f) ? 30.0f : 0.0f;
        NSLog(@"%@",sender.nextResponder);
    }
    - (IBAction)changeOpacity:(UIButton *)sender {
        self.layer.opacity = (self.layer.opacity == 1.0f) ? 0.8f : 1.0f;
    }
    - (IBAction)changeBorders:(UIButton *)sender {
        self.layer.borderWidth = (self.layer.borderWidth == 10) ? 0 : 10;
    }
    - (IBAction)changeColors:(UIButton *)sender {
        self.layer.backgroundColor = (self.layer.backgroundColor == [UIColor blueColor].CGColor) ? [UIColor redColor].CGColor : [UIColor blueColor].CGColor;
    }
    - (IBAction)changePositon:(UIButton *)sender {
        self.layer.position = (self.layer.position.x == 207) ? CGPointMake(300, 368) : CGPointMake(207, 368);
    }
    - (IBAction)changeBounds:(UIButton *)sender {
        self.layer.bounds = (self.layer.bounds.size.width == 100) ? CGRectMake(0, 0, 150, 100) : CGRectMake(0, 0, 100, 100);
    }
    - (IBAction)changeTransform:(UIButton *)sender {
        self.layer.transform = CATransform3DMakeRotation(M_PI_4, 0, 0, 1);
    }

二、Core Animation

  1. CABasicAnimation

    - (IBAction)translation:(UIButton *)sender {
        CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"position"];
        anim.delegate = self;
        anim.duration = 1.5;
        anim.toValue = [NSValue valueWithCGPoint:CGPointMake(200, 300)];
    //    anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
    //    anim.removedOnCompletion = NO;
    //    anim.fillMode = kCAFillModeForwards;
        [self.redView.layer addAnimation:anim forKey:@"ppposition"];
    }
    - (void)translation {
        CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform"];
        anim.delegate = self;
        anim.duration = 1.5;
        CATransform3D form = CATransform3DMakeTranslation(200, 300, 0);
        anim.toValue = [NSValue valueWithCATransform3D:form];
    //    anim.removedOnCompletion = NO;
    //    anim.fillMode = kCAFillModeForwards;
        [self.redView.layer addAnimation:anim forKey:@"position"];
    }
    - (IBAction)scale:(UIButton *)sender {
        CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"bounds"];
        anim.duration = 1.5;
        anim.delegate = self;
        anim.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0, 200, 200)];
        [self.redView.layer addAnimation:anim forKey:@"ssscale"];
    }
    - (void)scale {
        CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform"];
        anim.delegate = self;
        anim.duration = 1.5;
        anim.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1, 1, 1)];
        anim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(2, 2, 1)];
        [self.redView.layer addAnimation:anim forKey:nil];
    }
    - (IBAction)rotation:(UIButton *)sender {
        CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform"];
        anim.duration = 1.5;
        anim.delegate = self;
        anim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI_4, 0, 0, 1)];
        [self.redView.layer addAnimation:anim forKey:nil];
    }
    
    
    - (void)animationDidStart:(CAAnimation *)anim {
        NSLog(@"____animation start");
    }
    - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
        NSLog(@"____animation done");
    }
  2. CAKeyframeAnimation

    - (IBAction)points:(UIButton *)sender {
        CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
        anim.duration = 1.5;
        anim.delegate = self;
        NSValue *v1 = [NSValue valueWithCGPoint:CGPointMake(100, 100)];
        NSValue *v2 = [NSValue valueWithCGPoint:CGPointMake(200, 100)];
        NSValue *v3 = [NSValue valueWithCGPoint:CGPointMake(200, 200)];
        NSValue *v4 = [NSValue valueWithCGPoint:CGPointMake(100, 200)];
        anim.values = @[v1, v2, v3, v4];
        [self.redView.layer addAnimation:anim forKey:@""];
    }
    - (IBAction)path:(UIButton *)sender {
        CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
        anim.duration = 1.5;
        anim.delegate = self;
        CGMutablePathRef path = CGPathCreateMutable();
        CGPathAddEllipseInRect(path, NULL, CGRectMake(100, 100, 300, 300));
        anim.path = path;
        [self.redView.layer addAnimation:anim forKey:@""];
    }
    - (void)animationDidStart:(CAAnimation *)anim {
        NSLog(@"____start");
    }
    - (void)animationDidStop:(CAAnimation *)anim {
        NSLog(@"____done");
    }
  3. CAAnimationGroup

    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        CABasicAnimation *scale = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
        scale.toValue = @(0.0);
        
        CABasicAnimation *move = [CABasicAnimation animationWithKeyPath:@"transform.translation"];
        move.toValue = [NSValue valueWithCGPoint:CGPointMake(200, 200)];
        
        CABasicAnimation *rotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
        rotation.toValue = @(M_PI);
        
        CAAnimationGroup *group = [CAAnimationGroup animation];
        group.duration = 2;
        group.delegate = self;
        group.animations = @[scale, move, rotation];
        [self.redView.layer addAnimation:group forKey:nil];
    }
    - (void)animationDidStart:(CAAnimation *)anim {
        NSLog(@"____start");
    }
    - (void)animationDidStop:(CAAnimation *)anim {
        NSLog(@"____done");
    }
  4. CATransition

    - (IBAction)pre:(UIButton *)sender {
        self.index--;
        if (self.index == 0) {
            self.index = 4;
        }
        NSString *name = [NSString stringWithFormat:@"%d.png",_index];
        self.imgView.image = [UIImage imageNamed:name];
        CATransition *anim = [CATransition animation];
        //    anim.type = @"cube";
        //    anim.subtype = kCATransitionFromLeft;
        anim.type = @"pageCurl";
        anim.duration = 0.5;
        [self.imgView.layer addAnimation:anim forKey:nil];
    
    }
    - (IBAction)nex:(UIButton *)sender {
        self.index++;
        NSLog(@"__%d",self.index);
        if (self.index == 5) {
            self.index = 1;
        }
        NSString *name = [NSString stringWithFormat:@"%d.png",_index];
        self.imgView.image = [UIImage imageNamed:name];
        CATransition *anim = [CATransition animation];
        //    anim.type = @"cube";
        //    anim.subtype = kCATransitionFromLeft;
        anim.type = @"pageUnCurl";
        anim.duration = 0.5;
        [self.imgView.layer addAnimation:anim forKey:nil];
    }

三、UIBezierPath

  1. ZYView.h

    #import <UIKit/UIKit.h>
    
    typedef NS_ENUM(NSUInteger, ZYBezierPathType) {
        kDefaultPath = 1, // 三角形
        kRectPath = 2, // 矩形
        kCirclePath = 3,//
        kOvalPath = 4, // 椭圆
        kRoundedRectPath = 5, // 带圆角的矩形
        kArcPath = 6, //
        kSecondBezierPath = 7, // 二次贝塞尔曲线
        kThirdBezierPath = 8 // 三次贝塞尔曲线
    };
    
    @interface ZYView : UIView
    
    @property (nonatomic, assign)ZYBezierPathType type;
    
    @end
  2. ZYView.m

    #import "ZYView.h"
    
    @implementation ZYView
    
    - (void)drawRect:(CGRect)rect {
        switch (self.type) {
            case kDefaultPath: {// 三角形
                [self drawTrianglePath];
                break;
            }
            case kRectPath: {// 矩形
                [self drawRectPath];
                break;
            }
            case kCirclePath: {//
                [self drawCiclePath];
                break;
            }
            case kOvalPath: {// 椭圆
                [self drawOvalPath];
                break;
            }
            case kRoundedRectPath: {// 带圆角的矩形
                [self drawRoundedRectPath];
                break;
            }
            case kArcPath: {//
                [self drawARCPath];
                break;
            }
            case kSecondBezierPath: {// 二次贝塞尔曲线
                [self drawSecondBezierPath];
                break;
            }
            case kThirdBezierPath: {// 三次贝塞尔曲线
                [self drawThirdBezierPath];
                break;
            }
            default: {
                break;
            }
        }
    }
    
    // 画三角形
    - (void)drawTrianglePath {
        UIBezierPath *path = [UIBezierPath bezierPath];
        [path moveToPoint:CGPointMake(20, 20)];
        [path addLineToPoint:CGPointMake(self.frame.size.width - 40, 20)];
        [path addLineToPoint:CGPointMake(self.frame.size.width / 2, self.frame.size.height - 20)];
        
        // 最后的闭合线是可以通过调用closePath方法来自动生成的,也可以调用-addLineToPoint:方法来添加
        //  [path addLineToPoint:CGPointMake(20, 20)];
        
        [path closePath];
        
        // 设置线宽
        path.lineWidth = 1.5;
        
        // 设置填充颜色
        UIColor *fillColor = [UIColor greenColor];
        [fillColor set];
        [path fill];
        
        // 设置画笔颜色
        UIColor *strokeColor = [UIColor blueColor];
        [strokeColor set];
        
        // 根据我们设置的各个点连线
        [path stroke];
    }
    
    // 画矩形
    - (void)drawRectPath {
        UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(20, 20, self.frame.size.width - 40, self.frame.size.height - 40)];
        
        path.lineWidth = 1.5;
        path.lineCapStyle = kCGLineCapRound;
        path.lineJoinStyle = kCGLineJoinBevel;
        
        // 设置填充颜色
        UIColor *fillColor = [UIColor greenColor];
        [fillColor set];
        [path fill];
        
        // 设置画笔颜色
        UIColor *strokeColor = [UIColor blueColor];
        [strokeColor set];
        
        // 根据我们设置的各个点连线
        [path stroke];
    }
    
    // 画圆
    - (void)drawCiclePath {
        // 传的是正方形,因此就可以绘制出圆了
        UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(20, 20, self.frame.size.width - 40, self.frame.size.width - 40)];
        
        // 设置填充颜色
        UIColor *fillColor = [UIColor greenColor];
        [fillColor set];
        [path fill];
        
        // 设置画笔颜色
        UIColor *strokeColor = [UIColor blueColor];
        [strokeColor set];
        
        // 根据我们设置的各个点连线
        [path stroke];
    }
    
    // 画椭圆
    - (void)drawOvalPath {
        // 传的是不是正方形,因此就可以绘制出椭圆圆了
        UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(20, 20, self.frame.size.width - 80, self.frame.size.height - 40)];
        
        // 设置填充颜色
        UIColor *fillColor = [UIColor greenColor];
        [fillColor set];
        [path fill];
        
        // 设置画笔颜色
        UIColor *strokeColor = [UIColor blueColor];
        [strokeColor set];
        
        // 根据我们设置的各个点连线
        [path stroke];
    }
    
    - (void)drawRoundedRectPath {
        //  UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(20, 20, self.frame.size.width - 40, self.frame.size.height - 40) cornerRadius:10];
        
        UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(20, 20, self.frame.size.width - 40, self.frame.size.height - 40) byRoundingCorners:UIRectCornerTopRight cornerRadii:CGSizeMake(20, 20)];
        // 设置填充颜色
        UIColor *fillColor = [UIColor greenColor];
        [fillColor set];
        [path fill];
        
        // 设置画笔颜色
        UIColor *strokeColor = [UIColor blueColor];
        [strokeColor set];
        
        // 根据我们设置的各个点连线
        [path stroke];
    }
    // 画弧
    #define   kDegreesToRadians(degrees)  ((pi * degrees)/ 180)
    - (void)drawARCPath {
        const CGFloat pi = 3.14159265359;
        
        CGPoint center = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2);
        UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center
                                                            radius:100
                                                        startAngle:0
                                                          endAngle:kDegreesToRadians(135)
                                                         clockwise:YES];
        
        path.lineCapStyle = kCGLineCapRound;
        path.lineJoinStyle = kCGLineJoinRound;
        path.lineWidth = 5.0;
        
        UIColor *strokeColor = [UIColor redColor];
        [strokeColor set];
        
        [path stroke];
    }
    // 二次贝塞尔曲线
    - (void)drawSecondBezierPath {
        UIBezierPath *path = [UIBezierPath bezierPath];
        
        // 首先设置一个起始点
        [path moveToPoint:CGPointMake(20, self.frame.size.height - 100)];
        
        // 添加二次曲线
        [path addQuadCurveToPoint:CGPointMake(self.frame.size.width - 20, self.frame.size.height - 100)
                     controlPoint:CGPointMake(self.frame.size.width / 2, 0)];
        
        path.lineCapStyle = kCGLineCapRound;
        path.lineJoinStyle = kCGLineJoinRound;
        path.lineWidth = 5.0;
        
        UIColor *strokeColor = [UIColor redColor];
        [strokeColor set];
        
        [path stroke];
    }
    // 三次贝塞尔曲线
    - (void)drawThirdBezierPath {
        UIBezierPath *path = [UIBezierPath bezierPath];
        
        // 设置起始端点
        [path moveToPoint:CGPointMake(20, 150)];
        
        [path addCurveToPoint:CGPointMake(300, 150)
                controlPoint1:CGPointMake(160, 0)
                controlPoint2:CGPointMake(160, 250)];
        
        path.lineCapStyle = kCGLineCapRound;
        path.lineJoinStyle = kCGLineJoinRound;
        path.lineWidth = 5.0;
        
        UIColor *strokeColor = [UIColor redColor];
        [strokeColor set];
        
        [path stroke];
    }
    
    
    @end
  3. ViewController.m

    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        ZYView *view = [[ZYView alloc] initWithFrame:CGRectMake(100, 100, 250, 250)];
        view.backgroundColor = [UIColor redColor];
        view.type = kDefaultPath;
        [view setNeedsDisplay];
        [self.view addSubview:view];
    }

三、CAShaplayer

  1. 说到CAShapLayer就不得不说到UIBezierPath: UIBezierPath是在 UIKit 中的一个类,继承于NSObject,可以创建基于矢量的路径。使用此类可以定义常见的圆形、多边形等形状 。我们使用直线、弧(arc)来创建复杂的曲线形状。每一个直线段或者曲线段的结束的地方是下一个的开始的地方。每一个连接的直线或者曲线段的集合成为subpath。一个UIBezierPath对象定义一个完整的路径包括一个或者多个subpaths
  2. CAShapeLayer: CAShapeLayer顾名思义,继承于CALayer。 每个CAShapeLayer对象都代表着将要被渲染到屏幕上的一个任意的形状(shape)。具体的形状由其path(类型为CGPathRef)属性指定。 普通的CALayer是矩形,所以需要frame属性。CAShapeLayer初始化时也需要指定frame值,但 它本身没有形状,它的形状来源于其属性path 。CAShapeLayer有不同于CALayer的属性,它从CALayer继承而来的属性在绘制时是不起作用的
  3. 步骤:
    1、新建UIBezierPath对象bezierPath
    2、新建CAShapeLayer对象caShapeLayer
    3、将bezierPath的CGPath赋值给caShapeLayer的path,即caShapeLayer.path = bezierPath.CGPath
    4、把caShapeLayer添加到某个显示该图形的layer中

四、综合实例

 

长路漫漫,唯剑作伴--Core Animation