本文目录
一、什么是CALayer
* 在iOS系统中,你能看得见摸得着的东西基本上都是UIView,比如一个按钮、一个文本标签、一个文本输入框、一个图标等等,这些都是UIView。
* 其实UIView之所以能显示在屏幕上,完全是因为它内部的一个层。
* 在创建UIView对象时,UIView内部会自动创建一个层(即CALayer对象),通过UIView的layer属性可以访问这个层。当UIView需要显示到屏幕上时,会调用drawRect:方法进行绘图,并且会将所有内容绘制在自己的层上,绘图完毕后,系统会将层拷贝到屏幕上,于是就完成了UIView的显示。
* 换句话说,UIView本身不具备显示的功能,是它内部的层才有显示功能。
二、CALayer的简单使用
上面已经说过了,UIView之所以能够显示,完全是因为内部的CALayer对象。因此,通过操作这个CALayer对象,可以很方便地调整UIView的一些界面属性,比如:阴影、圆角大小、边框宽度和颜色等。
1.CALayer是被定义在QuartzCore框架中的,因此要想使用CALayer,先导入QuartzCore框架
1> 点击项目名称,然后点击右边TARGETS下面的target
2> 点击Build Pases后,展开Link Binary....,添加 + 号
3> 在搜索框中输入"Quartz",选中QuartzCore.framework,最后add添加
4> 添加完毕后,这个框架就会出现在项目文件夹中
如果你觉得位置不好看,还可以将它拖到Frameworks文件夹下,跟其他框架放一起
2.在项目代码中导入QuartzCore框架的主头文件
#import
3.通过CALayer修改UIImageView的界面属性
你也可以使用UIButton或者UILabel,这里就以UIImageView为例子
1> 先创建一个UIImageView,添加到控制器的view中
1UIImage *image = [UIImage imageNamed:@"lufy.png"];2UIImageView *imageView =[[[UIImageView alloc] initWithImage:image] autorelease];3imageView.center = CGPointMake(100,100);4[self.view addSubview:imageView];
2> 设置阴影
1imageView.layer.shadowColor=[UIColor grayColor].CGColor;2imageView.layer.shadowOffset= CGSizeMake(10,10);3imageView.layer.shadowOpacity=0.5;
* 第1行设置阴影的颜色为灰色,注意,这里使用的是UIColor的CGColor属性,是一种CGColorRef类型的数据
* 第2行设置阴影的偏移大小,可以看出阴影往原图的右下角偏移
* 第3行设置阴影的不透明度为0.5,表示半透明。如果为1,代表完全不透明。
3>设置圆角大小
通过layer属性可以访问视图内部的CALayer对象
1imageView.layer.cornerRadius=10;2imageView.layer.masksToBounds= YES;
* 第1行设置圆角半径为10
* 第2行的maskToBounds=YES:可以看做是强制内部的所有子层支持圆角效果,少了这个设置,UIImageView是不会有圆角效果的
* 注意,如果设置了maskToBounds=YES,那将不会有阴影效果
4> 设置边框宽度和颜色
1imageView.layer.borderWidth=5;2imageView.layer.borderColor= [UIColor redColor].CGColor;
* 第1行设置边框宽度为5
* 第2行设置边框颜色为红色
5> 设置旋转
imageView.layer.transform=CATransform3DMakeRotation(M_PI_4,0,0,1);
* 利用transform属性可以设置旋转、缩放等效果
* M_PI_4表示四分之π,顺时针旋转45°
* 后面的(0, 0, 1)表示Z轴这个向量,修改这个向量可以做一些三维旋转效果,你可以随便改个值试一下,比如(1, 1, 1)
* 总体的意思是layer会绕着Z轴顺时针旋转45°,也就是在x、y平面进行旋转
本文目录
三、为什么CALayer中使用CGColorRef和CGImageRef这2种数据类型,而不用UIColor和UIImage?
*上一讲已经说过,UIView内部默认有个CALayer对象(层),通过layer属性可以访问这个层。要注意的是,这个默认的层不允许重新创建,但可以往层里面添加子层
*UIView可以通过addSubview:方法添加子视图,类似地,CALayer可以通过addSublayer:方法添加子层
接下来演示一下如何添加子层:
一、添加一个简单的图层
1CALayer *myLayer =[CALayerlayer];2//设置层的宽度和高度(100x100)3myLayer.bounds= CGRectMake(0,0,100,100);4//设置层的位置5myLayer.position= CGPointMake(100,100);6//设置层的背景颜色:红色7myLayer.backgroundColor=[UIColor redColor].CGColor;8//设置层的圆角半径为109myLayer.cornerRadius=10;1011//添加myLayer到控制器的view的layer中12[self.view.layeraddSublayer:myLayer];
* 第1行创建了一个自动释放的CALayer对象,你也可以使用经典的alloc和init方法来创建
* 第12行将创建好的层添加到控制器的view的层中
二、添加一个显示图片的图层
1CALayer *myLayer =[CALayerlayer];2//设置层的宽度和高度(100x100)3myLayer.bounds= CGRectMake(0,0,100,100);4//设置层的位置5myLayer.position= CGPointMake(100,100);6//设置需要显示的图片7myLayer.contents= (id)[UIImage imageNamed:@"lufy.png"].CGImage;8//设置层的圆角半径为109myLayer.cornerRadius=10;10//如果设置了图片,需要设置这个属性为YES才有圆角效果11myLayer.masksToBounds=YES;1213//添加myLayer到控制器的view的layer中14[self.view.layeraddSublayer:myLayer];
* 在第7行设置需要显示的图片,注意,这里用的是UIImage的CGImage属性,是一种CGImageRef类型的数据
三、为什么CALayer中使用CGColorRef和CGImageRef这2种数据类型,而不用UIColor和UIImage?
* 首先要知道:CALayer是定义在QuartzCore框架中的;CGImageRef、CGColorRef两种数据类型是定义在CoreGraphics框架中的;UIColor、UIImage是定义在UIKit框架中的
* 其次,QuartzCore框架和CoreGraphics框架是可以跨平台使用的,在iOS和Mac OS X上都能使用,但是UIKit只能在iOS中使用
* 因此,为了保证可移植性,QuartzCore不能使用UIImage、UIColor,只能使用CGImageRef、CGColorRef
* 不过很多情况下,可以通过UIKit对象的特定方法,得到CoreGraphics对象,比如UIImage的CGImage方法可以返回一个CGImageRef
四、UIView和CALayer的选择
细心的朋友不难发现,其实前面的2个效果不仅可以通过添加层来实现,还可以通过添加UIView来实现。比如,第1个红色的层可以用一个UIView来实现,第2个显示图片的层可以用一个UIImageView来实现。既然CALayer和UIView都能实现相同的显示效果,那究竟该选择谁好呢?
* 其实,对比CALayer,UIView多了一个事件处理的功能。也就是说,CALayer不能处理用户的触摸事件,而UIView可以
* 所以,如果显示出来的东西需要跟用户进行交互的话,用UIView;如果不需要跟用户进行交互,用UIView或者CALayer都可以
* 当然,CALayer的性能会高一些,因为它少了事件处理的功能,更加轻量级
五、UIView和CALayer的其他关系
*UIView可以通过subviews属性访问所有的子视图,类似地,CALayer也可以通过sublayers属性访问所有的子层
*UIView可以通过superview属性访问父视图,类似地,CALayer也可以通过superlayer属性访问父层
* 下面再看一张UIView和CALayer的关系图:
如果两个UIView是父子关系,那么它们内部的CALayer也是父子关系。
本文目录
一、隐式动画属性
* 在前面几讲中已经提到,每一个UIView内部都默认关联着一个CALayer,我们可用称这个Layer为Root Layer(根层)。所有的非Root Layer,也就是手动创建的CALayer对象,都存在着隐式动画。
* 当对非Root Layer的部分属性进行相应的修改时,默认会自动产生一些动画效果,这些属性称为Animatable Properties(可动画属性)。
* 列举几个常见的Animatable Properties:
bounds:用于设置CALayer的宽度和高度。修改这个属性会产生缩放动画
backgroundColor:用于设置CALayer的背景色。修改这个属性会产生背景色的渐变动画
position:用于设置CALayer的位置。修改这个属性会产生平移动画
比如:假设一开始CALayer的position为(100, 100),然后在某个时刻修改为(200, 200),那么整个CALayer就会在短时间内从(100, 100)这个位置平移到(200, 200)
* 我们也可以从官方文档中查询所有的Animatable Properties
1.点击Window -> Organizer
2.在搜索框输入"animatable"即可
二、position和anchorPoint
* position和anchorPoint属性都是CGPoint类型的
* position可以用来设置CALayer在父层中的位置,它是以父层的左上角为坐标原点(0, 0)
* anchorPoint称为"定位点",它决定着CALayer身上的哪个点会在position属性所指的位置。它的x、y取值范围都是0~1,默认值为(0.5, 0.5)
1.创建一个CALayer,添加到控制器的view的layer中
1CALayer *myLayer =[CALayer layer];2//设置层的宽度和高度(100x100)3myLayer.bounds= CGRectMake(0,0,100,100);4//设置层的位置5myLayer.position= CGPointMake(100,100);6//设置层的背景颜色:红色7myLayer.backgroundColor=[UIColor redColor].CGColor;89//添加myLayer到控制器的view的layer中10[self.view.layer addSublayer:myLayer];
第5行设置了myLayer的position为(100, 100),又因为anchorPoint默认是(0.5, 0.5),所以最后的效果是:myLayer的中点会在父层的(100, 100)位置
注意,蓝色线是我自己加上去的,方便大家理解,并不是默认的显示效果。两条蓝色线的宽度均为100。
2.若将anchorPoint改为(0, 0),myLayer的左上角会在(100, 100)位置
1myLayer.anchorPoint= CGPointMake(0,0);
3.若将anchorPoint改为(1, 1),myLayer的右下角会在(100, 100)位置
1myLayer.anchorPoint= CGPointMake(1,1);
4.将anchorPoint改为(0, 1),myLayer的左下角会在(100, 100)位置
1myLayer.anchorPoint = CGPointMake(0,1);
我想,你应该已经明白anchorPoint的用途了吧,它决定着CALayer身上的哪个点会在position所指定的位置上。它的x、y取值范围都是0~1,默认值为(0.5, 0.5),因此,默认情况下,CALayer的中点会在position所指定的位置上。当anchorPoint为其他值时,以此类推。
本文目录
自定义层,其实就是在层上绘图,一共有2种方法,下面详细介绍一下。
一、自定义层的方法1
方法描述:创建一个CALayer的子类,然后覆盖drawInContext:方法,使用Quartz2D API进行绘图
1.创建一个CALayer的子类
2.在.m文件中覆盖drawInContext:方法,在里面绘图
1@implementation MJLayer23#pragmamark 绘制一个实心三角形4- (void)drawInContext:(CGContextRef)ctx {5//设置为蓝色6CGContextSetRGBFillColor(ctx,0,0,1,1);789//设置起点10CGContextMoveToPoint(ctx,50,0);11//从(50, 0)连线到(0, 100)12CGContextAddLineToPoint(ctx,0,100);13//从(0, 100)连线到(100, 100)14CGContextAddLineToPoint(ctx,100,100);15//合并路径,连接起点和终点16CGContextClosePath(ctx);1718//绘制路径19CGContextFillPath(ctx);20}2122@end
3.在控制器中添加图层到屏幕上
1MJLayer *layer =[MJLayer layer];2//设置层的宽高3layer.bounds= CGRectMake(0,0,100,100);4//设置层的位置5layer.position= CGPointMake(100,100);6//开始绘制图层7[layersetNeedsDisplay];8[self.view.layer addSublayer:layer];
注意第7行,需要调用setNeedsDisplay这个方法,才会触发drawInContext:方法的调用,然后进行绘图
二、自定义层的方法2
方法描述:设置CALayer的delegate,然后让delegate实现drawLayer:inContext:方法,当CALayer需要绘图时,会调用delegate的drawLayer:inContext:方法进行绘图。
* 这里要注意的是:不能再将某个UIView设置为CALayer的delegate,因为UIView对象已经是它内部根层的delegate,再次设置为其他层的delegate就会出问题。UIView和它内部CALayer的默认关系图:
1.创建新的层,设置delegate,然后添加到控制器的view的layer中
1CALayer *layer =[CALayer layer];2//设置delegate3layer.delegate=self;4//设置层的宽高5layer.bounds= CGRectMake(0,0,100,100);6//设置层的位置7layer.position= CGPointMake(100,100);8//开始绘制图层9[layersetNeedsDisplay];10[self.view.layer addSublayer:layer];
* 在第3行设置了CALayer的delegate,这里的self是指控制器
*注意第9行,需要调用setNeedsDisplay这个方法,才会通知delegate进行绘图
2.让CALayer的delegate(前面设置的是控制器)实现drawLayer:inContext:方法
1#pragmamark 画一个矩形框2- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {3//设置蓝色4CGContextSetRGBStrokeColor(ctx,0,0,1,1);5//设置边框宽度6CGContextSetLineWidth(ctx,10);78//添加一个跟层一样大的矩形到路径中9CGContextAddRect(ctx, layer.bounds);1011//绘制路径12CGContextStrokePath(ctx);13}
三、其他
1.总结
无论采取哪种方法来自定义层,都必须调用CALayer的setNeedsDisplay方法才能正常绘图。
2.UIView的详细显示过程
* 当UIView需要显示时,它内部的层会准备好一个CGContextRef(图形上下文),然后调用delegate(这里就是UIView)的drawLayer:inContext:方法,并且传入已经准备好的CGContextRef对象。而UIView在drawLayer:inContext:方法中又会调用自己的drawRect:方法
* 平时在drawRect:中通过UIGraphicsGetCurrentContext()获取的就是由层传入的CGContextRef对象,在drawRect:中完成的所有绘图都会填入层的CGContextRef中,然后被拷贝至屏幕