在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的动画效果好,就是把全部的图层合成一个位图来执行,这样的效果会好。