CALayer上绘图

在CALayer上绘图有2种方法ios

1)建立一个CALayer的子类,而后覆盖drawInContext:方法,可使用Quartz2D api进行绘图api

2)设置CALayer的代理,让代理实现drawLayer:inContext方法进行绘图。通常是在控制器里实现。这样会增长控制器的负担。iview

调用这2个方法之后都必须调用setNeedsDisplay方法从新绘制视图,才能生效。函数

全部的非root layer都存在隐式动画,根图层没有隐式动画。负责UIVIEW部分。视图上的根图层是没有隐式动画的。动画

 

 

 

1)采用代理方式在图层上绘图的代码ui

 

//atom

//  MainViewController.mspa

//  CALayer绘图_demo1代理

//code

//  Created by mac on 13-10-1.

//  Copyright (c) 2013 mac. All rights reserved.

//

 

#import "MainViewController.h"

#import <QuartzCore/QuartzCore.h>

 

@interfaceMainViewController ()

 

@end

 

@implementation MainViewController

 

- (void)viewDidLoad

{

    [superviewDidLoad];

// Do any additional setup after loading the view.

    

    

    // 建立一个layer

    CALayer *mylayer = [CALayer layer];

    [mylayer setBounds:CGRectMake(0, 0, 200, 200)];

    [mylayer setBackgroundColor:[UIColorredColor].CGColor];

    [mylayer setPosition:CGPointMake(100, 100)];

    [self.view.layer addSublayer:mylayer];

    

    // 这里不能将代理设置成view,由于view已是根layer的代理,再设置一个图层的代理,会由冲突。

    // 一般让控制器来充当代理。若是代理多个图层,最好用一个标志区分开,也能够用tag来区分。

    [mylayer setDelegate:self];

    [mylayer setNeedsDisplay];

    NSLog(@"%@",mylayer);

}

 

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx

{

    NSLog(@"%@",layer);

    CGRect rect = CGRectMake(50, 50, 100, 100);

    // 2个方法都不起做用。不能使用uikit的方法

//    [[UIColor blueColor] set];

//    UIRectFill(rect);

    // 在Core animation里不能使用ui方法。

   // QuartzCore是夸平台的,这里只能使用C语言的函数,不能使用UIKIT方法

    CGContextSetRGBFillColor(ctx, 0, 1, 0, 1);

    CGContextAddRect(ctx, rect);

    CGContextDrawPath(ctx, kCGPathFill);

}

 

@end

 显示结果

 

 

2) 自定义CALayer类,重写drawInContext方法来实现图层绘图

1.自定义layer,重写drawInContext方法

#import "MyLayer.h"

#import <QuartzCore/QuartzCore.h>

 

@implementation MyLayer

  // 从写drawInContext方法是关键,不然不能实如今自定义图层上绘图 

- (void)drawInContext:(CGContextRef)ctx

{

    // 绘制图层 绘制一个椭圆

  // 这里要用quartzcore方法,由于参数是CGContextRef,这个引用不能被uikit方法使用

    CGContextAddEllipseInRect(ctx, CGRectMake(0, 0, 100, 200));

    CGContextSetRGBFillColor(ctx, 1, 0, 0, 1);

    CGContextDrawPath(ctx, kCGPathFill);

}

 

@end

 

 

2.建立自定义视图,在自定义视图里实例化layer

- (id)initWithFrame:(CGRect)frame

{

    self = [super initWithFrame:frame];

    if (self) {

        // Initialization code

        NSLog(@"init myview");

        MyLayer *mylayer = [MyLayer layer];

        // 要设置大小,不然不显示

        [mylayer setFrame:CGRectMake(0, 0, 100, 200)];

        [self.layer addSublayer:mylayer];

        [mylayer setNeedsDisplay];//添加到视图根图层之后,要调用本身的(图层)的setneedsdisplay方法

        self.mylayer = mylayer;

        

    }

    returnself;

}

 

3.在控制器根视图里建立自定义视图

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view.
    
    MyView *myview = [[MyView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
    [myview setBackgroundColor:[UIColor lightGrayColor]];
    [self.view addSubview:myview];
}




方法触发顺序
1.调用视图的init方法
2.在init方法里调用setneedsdisplay方法的时候,会触发代理的drawlayer方法,在drawlayer方法里要调用父类的drawlayer方法,这个会触发视图里的drawinrect方法。
3.而后调用视图的drawinrect方法
4.drawinrect方法再触发调用图层的drawincontext方法。
顺序
1)父视图的init方法
2)父视图的代理方法
3)父视图的drawinrect方法
4)子图层的drawincontext方法


使用代理方式绘图时,当uiview收到setneedsdisplay消息时,calayer会传出来一个cgcontextref引用。这时候能够在cgcontextref里进行绘图。
当使用自定义图层方式绘图时,当uiview收到setneedsdisplay消息时,uiview会调用本身的drawlayer方法,这个方法会调用本身的drawrect方法,这个方法再触发子图层的drawlayer方法。

 

 绘制图像

#import "MyLayer.h"

#import <QuartzCore/QuartzCore.h>

 

@implementation MyLayer

 

- (void)drawInContext:(CGContextRef)ctx

{

    // 绘制图层

    CGContextAddEllipseInRect(ctx, CGRectMake(0, 0, 100, 100));

    CGContextSetRGBFillColor(ctx, 1, 0, 0, 1);

    CGContextDrawPath(ctx, kCGPathFill);

    

    // 绘制图层

    CGContextAddEllipseInRect(ctx, CGRectMake(100, 100, 100, 100));

    CGContextSetRGBFillColor(ctx, 0, 1, 0, 1);

    CGContextDrawPath(ctx, kCGPathFill);

    

    UIImage *image = [UIImage imageNamed:@"头像2.png"];

    CGContextDrawImage(ctx, CGRectMake(50, 50, 100, 100), image.CGImage);

}

 

@end

 结果

头像是反的。

这时候用到形变坐标系,但在形变坐标系以前,应该保存cgcontextref,形变坐标系以后,要恢复cgcontextref

//

#import "MyLayer.h"
#import <QuartzCore/QuartzCore.h>

@implementation MyLayer

- (void)drawInContext:(CGContextRef)ctx
{
    // 绘制图层
    CGContextAddEllipseInRect(ctx, CGRectMake(0, 0, 100, 100));
    CGContextSetRGBFillColor(ctx, 1, 0, 0, 1);
    CGContextDrawPath(ctx, kCGPathFill);
    
    // 绘制图层
    CGContextAddEllipseInRect(ctx, CGRectMake(100, 100, 100, 100));
    CGContextSetRGBFillColor(ctx, 0, 1, 0, 1);
    CGContextDrawPath(ctx, kCGPathFill);
    
    // 在形变坐标系以前应该保存Context CGContextSaveGState(ctx);
    
    // 应该先翻转坐标系,
    // 不然会头像是反的
    CGContextScaleCTM(ctx, 1.0, -1.0);
    CGContextTranslateCTM(ctx, 0, -self.bounds.size.height);
    UIImage *image = [UIImage imageNamed:@"头像2.png"];
    CGContextDrawImage(ctx, CGRectMake(50, 50, 100, 100), image.CGImage);
    // 在形变坐标系以后要恢复context 这样不会影响之后的绘图 CGContextRestoreGState(ctx);
    
    
    
}

 

 在uiview里添加了自定义图层之后,若是在图层里绘图,则uiview里自己的drawrect里的绘图会执行,可是会子图层的显示给覆盖掉

//
//  MyView.m
//  CALayer自定义视图实现图层绘图
//
//  Created by mac on 13-10-2.
//  Copyright (c) 2013年 mac. All rights reserved.
//

#import "MyView.h"
#import "MyLayer.h"

@interface MyView ()
@property (nonatomic,weak) MyLayer *mylayer;
@end

@implementation MyView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        NSLog(@"init myview");
        MyLayer *mylayer = [MyLayer layer];
        [mylayer setBounds:self.bounds];
        [mylayer setPosition:CGPointMake(100, 100)];
        [self.layer addSublayer:mylayer];
        [mylayer setNeedsDisplay];
        self.mylayer = mylayer;
        
    }
    return self;
}

// 这个代码是执行的,可是结果会被子图层的显示给覆盖掉
- (void)drawRect:(CGRect)rect { // Drawing code CGContextRef context = UIGraphicsGetCurrentContext(); CGContextAddRect(context, CGRectMake(50, 50, 20, 20)); CGContextDrawPath(context, kCGPathFill); }


@end


若是使用了calayer的绘图就不要再使用视图的drawrect的绘图方法,即便用了,也会被layer的绘图显示给覆盖掉。CGContextRef是位图的上下文。动画就是把全部的图层和到一个CGContextRef,位图上下文里。提升动画执行效率。ios的动画效果好,就是把全部的图层合成一个位图来执行,这样的效果会好。