在CALayer上绘图有2种方法
1)创建一个CALayer的子类,然后覆盖drawInContext:方法,可以使用Quartz2D api进行绘图
2)设置CALayer的代理,让代理实现drawLayer:inContext方法进行绘图。一般是在控制器里实现。这样会增加控制器的负担。
调用这2个方法以后都必须调用setNeedsDisplay方法重新绘制视图,才能生效。
所有的非root layer都存在隐式动画,根图层没有隐式动画。负责UIVIEW部分。视图上的根图层是没有隐式动画的。
1)采用代理方式在图层上绘图的代码
//
// MainViewController.m
// CALayer绘图_demo1
//
// 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@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的动画效果好,就是把所有的图层合成一个位图来执行,这样的效果会好。