长路漫漫,唯剑作伴--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则不会。

    • UIView是CALayer的delegate;
  3. 简单的例子

    1. 代码
      //设置边框的宽度为20
          self.customView.layer.borderWidth=5;
          //设置边框的颜色
          self.customView.layer.borderColor=[UIColor blackColor].CGColor;
          
          //设置layer的圆角
          self.customView.layer.cornerRadius=20;
          
          //在view的图层上添加一个image,contents表示接受内容
          self.customView.layer.contents=(id)[UIImage imageNamed:@"me"].CGImage;

      效果


      contents是id类型,可以接受内容,上面的实例让layer显示一张图片,仔细观察可以发现四个圆角的部分露了一个角出来。原因:

      customview上的根layer

      UIimage的图层

      添加后

      那是因为设置的image不是展示在主图层上的,而是显示在子图层上的。可以通过设置一个范围,设置超出主图层的部分把它给剪切掉。

      有以下两种方法,建议使用layer中的方法(第二种)self.customView.layer.masksToBounds=YES;
      //设置边框的宽度为20
          self.customView.layer.borderWidth=5;
          //设置边框的颜色
          self.customView.layer.borderColor=[UIColor blackColor].CGColor;
          
          //设置layer的圆角
          self.customView.layer.cornerRadius=20;
          //设置超过子图层的部分裁减掉
          //UI框架中使用的方法
      //    self.customView.clipsToBounds=YES;
          self.customView.layer.masksToBounds=YES;
          
          //在view的图层上添加一个image,contents表示接受内容
          self.customView.layer.contents=(id)[UIImage imageNamed:@"me"].CGImage;

  4. 修改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中

四、综合实例

  1. 画圆并且添加变大动画

    // 画圆变大
    - (void)test {
        CAShapeLayer *clickCicrleLayer = [CAShapeLayer layer];
        clickCicrleLayer.frame = CGRectMake(150, 250, 100, 100);
        clickCicrleLayer.fillColor = [UIColor redColor].CGColor;
        clickCicrleLayer.path = [self drawclickCircleBezierPath:50].CGPath;
        [self.view.layer addSublayer:clickCicrleLayer];
        
        //放大变色圆形
        CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
        basicAnimation.duration = 2;
        basicAnimation.toValue = (__bridge id _Nullable)(([self drawclickCircleBezierPath:100].CGPath));
        basicAnimation.removedOnCompletion = NO;
        basicAnimation.fillMode = kCAFillModeForwards;
        
        [clickCicrleLayer addAnimation:basicAnimation forKey:@"clickCicrleAnimation"];
    }
    //画圆
    -(UIBezierPath *)drawclickCircleBezierPath:(CGFloat)radius{
        UIBezierPath *bezierPath = [UIBezierPath bezierPath];
        /**
         *  center: 弧线中心点的坐标
         radius: 弧线所在圆的半径
         startAngle: 弧线开始的角度值
         endAngle: 弧线结束的角度值
         clockwise: 是否顺时针画弧线
         *
         */
        [bezierPath addArcWithCenter:CGPointMake(0,0) radius:radius startAngle:0 endAngle:M_PI*2 clockwise:YES];
        [bezierPath closePath];
        return bezierPath;
    }
  2. 带圆角的矩形并且缩小

    // 带圆角的矩形并且缩小
    - (void)roundRect1 {
        CAShapeLayer *layer = [[CAShapeLayer alloc] init];
        layer.backgroundColor = [UIColor yellowColor].CGColor;
        layer.frame = CGRectMake(150, 250, 200, 100);
        layer.fillColor = [UIColor redColor].CGColor;
        UIBezierPath *path1 = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 200, 100) byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight | UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(20, 20)];
        layer.path = path1.CGPath;
        [self.view.layer addSublayer:layer];
        
        CABasicAnimation *anim = [CABasicAnimation animation];
        anim.keyPath = @"path";
        anim.duration = 2.0;
        UIBezierPath *path2 = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(70, 20, 60, 60) byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight | UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(30, 30)];
        anim.toValue = (__bridge id _Nullable)(path2.CGPath);
        anim.removedOnCompletion = NO;
        anim.fillMode = kCAFillModeForwards;
        [layer addAnimation:anim forKey:@""];
    }
  3. 画圆环

    // 画圆环
    - (void)yuanhuan {
        CAShapeLayer *clickCicrleLayer = [CAShapeLayer layer];
        clickCicrleLayer.frame = CGRectMake(150, 250, 100, 100);
        clickCicrleLayer.fillColor = [UIColor redColor].CGColor;
        clickCicrleLayer.path = [self drawclickCircleBezierPath:50].CGPath;
        clickCicrleLayer.lineWidth = 20;
        clickCicrleLayer.strokeColor = [UIColor greenColor].CGColor;
        [self.view.layer addSublayer:clickCicrleLayer];
    }
  4. 画转圆圈的圆弧

    // 画转圈的圆弧
    - (void)juhua {
        CAShapeLayer *layer = [CAShapeLayer layer];
        layer.position = CGPointMake(150, 250);
        layer.fillColor = [UIColor clearColor].CGColor;
        layer.strokeColor = [UIColor blackColor].CGColor;
        layer.lineWidth = 2;
        layer.path = [self drawLoadingBezierPath].CGPath;
        [self.view.layer addSublayer:layer];
        
        CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
        basicAnimation.fromValue = @(0);
        basicAnimation.toValue = @(M_PI*2);
        basicAnimation.duration = 0.5;
        basicAnimation.repeatCount = LONG_MAX;
        [layer addAnimation:basicAnimation forKey:@"loadingAnimation"];
    }
    -(UIBezierPath *)drawLoadingBezierPath{
        CGFloat radius = 60;
        UIBezierPath *bezierPath = [UIBezierPath bezierPath];
        [bezierPath addArcWithCenter:CGPointMake(0,0) radius:radius startAngle:M_PI/2 endAngle:M_PI/2+M_PI/2 clockwise:YES];
        return bezierPath;
    }

     

五、

 

posted @ 2017-03-28 23:53  来事啊  阅读(200)  评论(0编辑  收藏  举报