还是总结大神的好帖:http://www.cocoachina.com/ios/20141022/10005.html
接下来我们了解自定义的CALayer,自定义图层,首先我们应该知道:
1.Quartz 2D在UIView中绘制图形的本质也是绘制到图层中(绘制技术一般提及的就是UIKit和Core Graphics)
2.CALayer本质使用的是位图上下文
3.UIView
利用一个例子来进一步说明:自定义图层绘图时没有直接在视图控制器中调用自定义图层,而是在一个UIView将自定义图层添加到UIView的根图层中(例子中的UIView跟自定义图层绘图没有直接关系,分离测试,并且要记得起那个层的继承结构,UIView本身自带有一个根图层)。
了解本例子之前,先来了解几个至关重要的类型和方法:
自定义的CALayer中,
/* Called via the -display method when the `contents' property is being
* updated. Default implementation does nothing. The context may be
* clipped to protect valid layer content. Subclasses that wish to find
* the actual region to draw can call CGContextGetClipBoundingBox(). */
- (void)drawInContext:(CGContextRef)ctx;
理解这个方法和参数是至关重要的,这个方法主要做的是在特定的图形上下文中绘制图层的内容。
这个方法默认的实现并没有做任何绘制本身的操作。但是如果图层的代理实现了这个drawLayer:inContext:方法,这个方法会被调用来实现实质上的绘制。子类可以重载这个方法并且用来绘制图层的内容。当绘制的时候,所有坐标点都应在点的逻辑坐标体系中确定(是不是说相对super图层的坐标体系?)
其参数:typedef struct CGContext *CGContextRef; 是一个结构体指针,代表的意义是图形上下文。
- (void)drawRect:(CGRect)rect;
这个方法老生常谈了,好好看文档,应该能弄懂。默认是不进行任何操作的,但是重载该方法可以利用UIKit或者Core Graphics技术来绘制视图的内容(就是一些绘制代码),如果用了别的绘制视图内容的方式,你大可以不调用这个方法,所以说一般的frame定制UI并不用使用到这个方法,当你调用这个方法的时候,UIKit会自动帮你配置好绘制环境。包括你可以通过UIGraphicsGetCurrentContext()来得到当前的图形上下文。
pop上代码:
自定义的CALayer对象:
#import "LBLayer.h"
@implementation LBLayer
//重写其方法
- (void)drawInContext:(CGContextRef)ctx
{
NSLog(@"3-drawInContext:");
//习惯直接把你觉得有兴趣的输出参看前后的变化
NSLog(@"CGContext:%@",ctx);
CGContextSetRGBFillColor(ctx, 135.0/255.0, 232.0/255.0, 84.0/255.0, 1 );
CGContextSetRGBStrokeColor(ctx, 135.0/255.0, 232.0/255.0, 84.0/255.0, 1);
//就是从点(94.5, 33.5)开始找着路径path画图,结果是一个闭合的五角星
CGContextMoveToPoint(ctx, 94.5, 33.5);
CGContextAddLineToPoint(ctx,104.02, 47.39);
CGContextAddLineToPoint(ctx,120.18, 52.16);
CGContextAddLineToPoint(ctx,109.91, 65.51);
CGContextAddLineToPoint(ctx,110.37, 82.34);
CGContextAddLineToPoint(ctx,94.5, 76.7);
CGContextAddLineToPoint(ctx,78.63, 82.34);
CGContextAddLineToPoint(ctx,79.09, 65.51);
CGContextAddLineToPoint(ctx,68.82, 52.16);
CGContextAddLineToPoint(ctx,84.98, 47.39);
CGContextClosePath(ctx);
CGContextDrawPath(ctx, kCGPathFillStroke);
}
@end
自定义的UIView对象,并在初始化的时候把上面的自定义CALayer对象添加在本本对象的图层上,并且通过调用drawRect:方法输出当前的图形上下文,前后对比(为此添加了一个序号来控制)
#import "LBView.h"
#import "LBLayer.h"
@implementation LBView
- (instancetype)initWithFrame:(CGRect)frame
{
NSLog(@"initWithFrame:");
//为了安全的 先super 父类方法
if (self = [super initWithFrame:frame])
{
//init的目的,在看官方文档已经很清楚了,就是为了alloc以后让对象有一个适当的值
//而一个适当的“值”,除了可以哦,直接地,赋予变量初始值,还可以做一些适当地配置,如直接初始化一个图层并且添加
LBLayer* layer = [[LBLayer alloc] init];
//注意这里,一个普通的图层只要bounds,position,backgroundColor注意到了就可以,并且都是可动画的
layer.bounds = CGRectMake(0, 0, 185, 185);
layer.position = CGPointMake(160, 284);
layer.backgroundColor=[UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0].CGColor;
[layer setNeedsDisplay];
[self.layer addSublayer:layer];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
//太醒目了,通过添加 x-drawRect 来检测先后的drawRect次序
NSLog(@"2-drawRect:");
NSLog(@"CGContext:%@",UIGraphicsGetCurrentContext());//得到的当前图形上下文正是drawLayer中传递的
//不知道这个super有毛意义
[super drawRect:rect];
}
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
NSLog(@"1-drawLayer:inContext:");
NSLog(@"CGContext:%@",ctx);
[super drawLayer:layer inContext:ctx];
}
@end
在viewController中单独对UIView做UIKit的绘制工作,你可以看到只有简单的frame和background操作(及时在init的时候添加了图层,涉及了图层),但是还是能验证在使用Quartz 2D在UIView中绘制图形的本质也是绘制到图层中这个想法。
#import "LBView.h"
#import "LBLayerPractiseController.h"
@interface LBLayerPractiseController ()
@end
@implementation LBLayerPractiseController
- (void)viewDidLoad
{
[super viewDidLoad];
LBView* view = [[LBView alloc] initWithFrame:[UIScreen mainScreen].bounds];
view.backgroundColor=[UIColor colorWithRed:249.0/255.0 green:249.0/255.0 blue:249.0/255.0 alpha:1];
[self.view addSubview:view];
}
@end
结果:
输出结果:
以后学习大牛的输出次序方法,在前面加序号,控制调用方法的先后,可以清楚看到的是最先调用的是代理方法:drawLayer:inContext:,这个方法为什么不用我们设置代理,因为视图本身会自动设置成自身,而参数inContext:也是由自身传递当前的图形上下文。其次调用UIView提供的方法:drawRect:,并且在这个方法的图形上下文也即是前面提到的那个参数,最后才调用initWithFrame中初始化定义图层的方法当中的drawInContext:(CGContextRef)ctx,是不是很出乎意料咧,调用并不是初始化为先呢! 参看上面结果可以知道,最后在自定义的图层的图形上下文并不是前面提到的图形上下文,也非常好理解,因为前面两个在viewController里面操作,所以图形上下文基于的是self.view,而自定义的图层是在UIView中实现的,基于的图形上下文便是LBView的,而管理图形上下文是在一个栈的数据结构中管理的,所以他们的对象的地址都是相差不大的。(初步理解,如果有错希望指正,日后重新整理绘图方面也会重新理解一遍)
最后附上大牛的原话,帮助理解:
前面的文章中曾经说过,在使用Quartz 2D在UIView中绘制图形的本质也是绘制到图层中,为了说明这个问题下面演示自定义图层绘图时没有直接在视图控制器中调用自定义图层,而是在一个UIView将自定义图层添加到UIView的根图层中(例子中的UIView跟自定义图层绘图没有直接关系)。从下面的代码中可以看到:UIView在显示时其根图层会自动创建一个CGContextRef(CALayer本质使用的是位图上下文),同时调用图层代理(UIView创建图层会自动设置图层代理为其自身)的draw: inContext:方法并将图形上下文作为参数传递给这个方法。而在UIView的draw:inContext:方法中会调用其drawRect:方法,在drawRect:方法中使用UIGraphicsGetCurrentContext()方法得到的上下文正是前面创建的上下文。
注:本文章是总结于,采例于文章:http://www.cocoachina.com/ios/20141022/10005.html,如有侵权或其他不好的影响,马上修改,感谢分享技术的大神们!