先pop上几篇优秀的文章,并在此感谢原作者不吝的分享:
http://www.cocoachina.com/ios/20141022/10005.html
http://www.cnblogs.com/wendingding/p/3800736.html
http://www.cnblogs.com/kenshincui/p/3972100.html
根据上篇章可以知道一些常用的CALayer 属性,而根据上面的博客文章,一定要学会position和anchorPoint这两个非常重要的属性。而弄懂它们的关键是它们之间的关系!
看了上面几篇文章,真的感觉技术出了学会了会用了。但是要比较好地阐述出来也不是一件容易的事情,例如上面两个文章,大神文顶顶说得就容易理解很多。
/* The position in the superlayer that the anchor point of the layer's
* bounds rect is aligned to. Defaults to the zero point. Animatable. */
@property CGPoint position;
/* Defines the anchor point of the layer's bounds rect, as a point in
* normalized layer coordinates - '(0, 0)' is the bottom left corner of
* the bounds rect, '(1, 1)' is the top right corner. Defaults to
* '(0.5, 0.5)', i.e. the center of the bounds rect. Animatable. */
@property CGPoint anchorPoint;
anchorPoint和position几个要点:
1.anchorPoint和position位置永远重合
2.anchorPoint的x,y系数是相对与所在图层的宽高系数
3.position是相对于super图层的
附上文顶顶大神的一个图解并适当注释:
首先上图绿色图层为superlayer,红色图层为要添加的图层,并设置position为:(100, 100),anchorPoint为(0.5, 0.5),用红色图层的宽高乘以系数可以得到anchorPoint为红色图层的中心点,并且这个中心点与position重合,故结果为上图,其他情况的数值也同理可得。
例子二:
可以明显看到position位置一直不变,而随着anchorPoint的系数变化图层的变化也十分明显,所以说anchorPoint和position可以决定图层的最终位置,并且anchorPoint可以动态地去使得图层的位置变化,现在想想,就会觉得设置两个值是有他的理由的。一个直接的理由是因为frame虽然可以一个值完成这两个值的效果,但是CALayer的Frame并不支持动画效果。
anchorPoint 和 position例子:
#define WIDTH 50
#import "CALayerPractiseController.h"
@interface CALayerPractiseController ()
@end
@implementation CALayerPractiseController
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view setBackgroundColor:[UIColor whiteColor]];
[self drawMyLayer];
}
#pragma mark - QuartzCore 绘制图形
- (void)drawMyLayer
{
CGSize size = [UIScreen mainScreen].bounds.size;
CALayer* layer = [[CALayer alloc] init]; //也是直接地alloc init
//设置背景颜色,由于QuartzCore是跨平台框架,无法直接使用UIColor,哈哈,解决多年的困扰啊,原来添加CG是因为跨平台的原因
layer.backgroundColor = [UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0].CGColor;
//设置固定的中心点
layer.position = CGPointMake(size.width/2, size.height/2);
layer.bounds = CGRectMake(0, 0, WIDTH, WIDTH);
//设置圆角,当圆角半径等于矩形的一半时看起来就是一个圆形
layer.cornerRadius = WIDTH/2;
layer.shadowColor = [UIColor grayColor].CGColor;
layer.shadowOffset = CGSizeMake(2, 2);
layer.shadowOpacity = 0.9;
//layer.borderColor=[UIColor whiteColor].CGColor;
//layer.borderWidth=1;
//layer.anchorPoint=CGPointZero;
//在self.view的根图层上添加哦。
[self.view.layer addSublayer:layer];
}
#pragma mark - 点击放大效果
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch* touch = [touches anyObject];
CALayer* layer = self.view.layer.sublayers[0];
CGFloat width = layer.bounds.size.width;
if (width == WIDTH)
{
width = WIDTH*4;
}
else
{
width = WIDTH;
}
layer.bounds = CGRectMake(0, 0, width, width);
layer.position = [touch locationInView:self.view];
layer.cornerRadius = width/2;
}
@end
别人的例子,真的技术的境界只有越来越深,很多方法我都头一次见到,而寥寥几句却能带来这样一般效果!这也是我继续向前的动力!好了不扯远了。
上面的例子看起来就只像是做了2D的UI图层方向的绘制,但是却能展现出动画的效果,就是因为CALayer隐式的动画效果。这也是映山做的游戏的效果,其实是可以完全用Core Animation提供而不用OpenGL这种高深的东西。
对于CALayer,绘制主要有两种方法:drawLayer:inContext, drawInContext:
/* If defined, called by the default implementation of -drawInContext: */
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;
/* 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;
/** Delegate methods. **/
@interface NSObject (CALayerDelegate)
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;
@end
刚开始看到这种形式也不是太理解的,delegate为什么要做成类目? 后来看到文章解释得很清楚,
需要注意这个方法虽然是代理方法但是不用手动实现CALayerDelegate,因为CALayer定义中给NSObject做了分类扩展,所有的NSObject都包含这个方法。另外设置完代理后必须要调用图层的setNeedDisplay方法,否则绘制的内容无法显示。同时注意这里的setNeedDisplay的方法是图层定义的而不是UIKit框架定义的那个。
/* Marks that -display needs to be called before the layer is next
* committed. If a region is specified, only that region of the layer
* is invalidated. */
- (void)setNeedsDisplay;
这里接下来给我们介绍一个了一个如何用图层来绘制圆框的方法,嘿嘿,可不是设置CornerRadius那么弱智的哦,还记得一道面试题吗?如何高性能的给UIImageView加个圆角?(不准说layer.cornerRadius!) 很多时候我们不是为了找到一个多炫的不同的实现方法,而是一个更好的不同的实现方法,更多的是从性能和优化的角度出发。为什么爱员工第一版的创建活动会那么卡,就是因为直接回的button.layer.CornerRadius是非常低能的做法。
这个方法简直太帅了,就是先绘制一个图层,然后在图层上面添加CGImageRef这种原生的图像类:
- (void)viewDidLoad
{
[super viewDidLoad];
//摸索到了一个规律,每次都先添加(创建)一个新图层CALayer对象
CALayer* layer = [[CALayer alloc] init];
layer.bounds = CGRectMake(0, 0, PHOTO_HEIGHT, PHOTO_HEIGHT);
layer.position = CGPointMake(160, 200);
layer.backgroundColor = [UIColor redColor].CGColor;
layer.cornerRadius = PHOTO_HEIGHT/2;
layer.masksToBounds = YES;
layer.borderColor = [UIColor whiteColor].CGColor;
layer.borderWidth = 2.0;
layer.delegate = self;
[self.view.layer addSublayer:layer];
[layer setNeedsDisplay]; <span style="white-space:pre"> </span>//要调用这个,代理方法才能有效
}
#pragma mark 绘制图形、图像到图层,注意参数中的ctx是图层的图形上下文,其中绘图位置也是相对图层而言的
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
CGContextSaveGState(ctx);
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -PHOTO_HEIGHT);
UIImage* image = [UIImage imageNamed:@"1.jpg"];
CGContextDrawImage(ctx, CGRectMake(0, 0, PHOTO_HEIGHT, PHOTO_HEIGHT), image.CGImage);
CGContextRestoreGState(ctx);
}
分别在上面的delegate设置下面和绘制代理方法下面添加如下代码:
NSLog(@"Check the layer : %@", layer);
NSLog(@"Check the delegate layer is : %@", layer);
结果:
可以很清楚知道,这两个设置delegate之后的layer就是代理方法里面的layer。
我们都知道如果不调用layer的setNeedsDisplay将没有效果,我们试着注释它,结果:
很显然的没有调用setNeedsDisplay就是没有调用代理方法。
转大牛的原话:解决一个从来都觉得模糊的地方:masksToBounds的设置
需要注意的是上面代码中绘制图片圆形裁切效果时如果不设置masksToBounds是无法显示圆形,但是对于其他图形却没有这个限制。原因就是当绘制一张图片到图层上的时候会重新创建一个图层添加到当前图层,这样一来如果设置了圆角之后虽然底图层有圆角效果,但是子图层还是矩形,只有设置了masksToBounds为YES让子图层按底图层剪切才能显示圆角效果。同样的,有些朋友经常在网上提问说为什么使用UIImageView的layer设置圆角后图片无法显示圆角,只有设置masksToBounds才能出现效果,也是类似的问题。
第二个例子是添加阴影:
/** 其他没变,添加这一段 **/
CGPoint position = CGPointMake(160, 200);
CGRect bounds = CGRectMake(0, 0, PHOTO_HEIGHT, PHOTO_HEIGHT);
CGFloat cornerRadius = PHOTO_HEIGHT/2;
CGFloat borderWidth = 2;
CALayer *layerShadow=[[CALayer alloc]init];
layerShadow.bounds=bounds;
layerShadow.position=position;
layerShadow.cornerRadius=cornerRadius;
layerShadow.shadowColor=[UIColor grayColor].CGColor;
layerShadow.shadowOffset=CGSizeMake(2, 1);
layerShadow.shadowOpacity=1;
layerShadow.borderColor=[UIColor whiteColor].CGColor;
layerShadow.borderWidth=borderWidth;
[self.view.layer addSublayer:layerShadow];
第三个例子是图形形变:
//关于形变的就这一句
layer.transform = CATransform3DMakeRotation(M_PI, 1, 0, 0);
//***************
#pragma mark 绘制图形、图像到图层,注意参数中的ctx时图层的图形上下文,其中绘图位置也是相对图层而言的
-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
// NSLog(@"%@",layer);//这个图层正是上面定义的图层
NSLog(@"Check the delegate layer is : %@", layer);
UIImage* image = [UIImage imageNamed:@"12.jpg"];
CGContextDrawImage(ctx, CGRectMake(0, 0, PHOTO_HEIGHT, PHOTO_HEIGHT), image.CGImage);
}
设置倒立的效果:
layer.transform = CATransform3DMakeRotation(M_PI, 1, 0, 0);
UIImage* image = [UIImage imageNamed:@"12.jpg"];
[layer setContents:(id)image.CGImage];
[self.view.layer addSublayer:layer];
效果:
上面为什么要重点介绍几个关于阴影,cornerRadius,形变,是因为这几个都是与动画密切相关的属性。然而iOS还提供了一种设置keyPath的方法,这种方法是利用KVC的动态性质。这种方式更加灵活,可以任意组合多种设置方法。
//将上面这句话改成下面的那句
layer.transform = CATransform3DMakeRotation(M_PI, 1, 0, 0);
[layer setValue:@M_PI forKeyPath:@"transform.rotation.x"];
KVC,日后的响应式编程等等东西都会接触到的一个重要编程思想。