iOS開發(fā)系列:讓你的應(yīng)用“動(dòng)”起來(lái)--CALayer

概覽

在iOS中隨處都可以看到絢麗的動(dòng)畫效果,實(shí)現(xiàn)這些動(dòng)畫的過(guò)程并不復(fù)雜厦画,今天將帶大家一窺iOS動(dòng)畫全貌。在這里你可以看到iOS中如何使用圖層精簡(jiǎn)非交互式繪圖,如何通過(guò)核心動(dòng)畫創(chuàng)建基礎(chǔ)動(dòng)畫查蓉、關(guān)鍵幀動(dòng)畫、動(dòng)畫組榜贴、轉(zhuǎn)場(chǎng)動(dòng)畫豌研,如何通過(guò)UIView的裝飾方法對(duì)這些動(dòng)畫操作進(jìn)行簡(jiǎn)化等。在今天的文章里您可以看到動(dòng)畫操作在iOS中是如何簡(jiǎn)單和高效竣灌,很多原來(lái)想做但是苦于沒有思路的動(dòng)畫在iOS中將變得越發(fā)簡(jiǎn)單:

  • CALayer:
  • CALayer簡(jiǎn)介
  • CALayer常用屬性
  • CALayer繪圖

CALayer

1. CALayer簡(jiǎn)介

在介紹動(dòng)畫操作之前我們必須先來(lái)了解一個(gè)動(dòng)畫中常用的對(duì)象CALayer聂沙。CALayer包含在QuartzCore框架中秆麸,這是一個(gè)跨平臺(tái)的框架初嘹,既可以用在iOS中又可以用在Mac OS X中。在使用Core Animation開發(fā)動(dòng)畫的本質(zhì)就是將CALayer中的內(nèi)容轉(zhuǎn)化為位圖從而供硬件操作沮趣,所以要熟練掌握動(dòng)畫操作必須先來(lái)熟悉CALayer屯烦。

使用Quartz 2D繪圖時(shí)大家其實(shí)已經(jīng)用到了CALayer,當(dāng)利用drawRect:方法繪圖的本質(zhì)就是繪制到了UIView的layer(屬性)中房铭,可是這個(gè)過(guò)程大家在上一節(jié)中根本體會(huì)不到驻龟。但是在Core Animation中我們操作更多的則不再是UIView而是直接面對(duì)CALayer。下圖描繪了CALayer和UIView的關(guān)系缸匪,在UIView中有一個(gè)layer屬性作為根圖層翁狐,根圖層上可以放其他子圖層,在UIView中所有能夠看到的內(nèi)容都包含在layer中:



2. CALayer常用屬性

在iOS中CALayer的設(shè)計(jì)主要是了為了內(nèi)容展示和動(dòng)畫操作凌蔬,CALayer本身并不包含在UIKit中露懒,它不能響應(yīng)事件。由于CALayer在設(shè)計(jì)之初就考慮它的動(dòng)畫操作功能砂心,CALayer很多屬性在修改時(shí)都能形成動(dòng)畫效果懈词,這種屬性稱為“隱式動(dòng)畫屬性”。但是對(duì)于UIView的根圖層而言屬性的修改并不形成動(dòng)畫效果辩诞,因?yàn)楹芏嗲闆r下根圖層更多的充當(dāng)容器的做用坎弯,如果它的屬性變動(dòng)形成動(dòng)畫效果會(huì)直接影響子圖層。另外,UIView的根圖層創(chuàng)建工作完全由iOS負(fù)責(zé)完成抠忘,無(wú)法重新創(chuàng)建撩炊,但是可以往根圖層中添加子圖層或移除子圖層。

下表列出了CALayer常用的屬性:

屬性 說(shuō)明 是否支持隱式動(dòng)畫
anchorPoint 和中心點(diǎn)position重合的一個(gè)點(diǎn)崎脉,稱為“錨點(diǎn)”衰抑,錨點(diǎn)的描述是相對(duì)于x、y位置比例而言的默認(rèn)在圖像中心點(diǎn)(0.5,0.5)的位置
backgroundColor 圖層背景顏色
borderColor 邊框顏色
borderWidth 邊框?qū)挾?/td>
bounds 圖層大小
contents 圖層顯示內(nèi)容荧嵌,例如可以將圖片作為圖層內(nèi)容顯示
contentsRect 圖層顯示內(nèi)容的大小和位置
cornerRadius 圓角半徑
doubleSided 圖層背面是否顯示呛踊,默認(rèn)為YES
frame 圖層大小和位置,不支持隱式動(dòng)畫啦撮,所以CALayer中很少使用frame谭网,通常使用bounds和position代替
hidden 是否隱藏
mask 圖層蒙版
maskToBounds 子圖層是否剪切圖層邊界,默認(rèn)為NO
opacity 透明度 赃春,類似于UIView的alpha
position 圖層中心點(diǎn)位置愉择,類似于UIView的center
shadowColor 陰影顏色
shadowOffset 陰影偏移量
shadowOpacity 陰影透明度,注意默認(rèn)為0织中,如果設(shè)置陰影必須設(shè)置此屬性
shadowPath 陰影的形狀
shadowRadius 陰影模糊半徑
sublayers 子圖層
sublayerTransform 子圖層形變
transform 圖層形變
  • 隱式屬性動(dòng)畫的本質(zhì)是這些屬性的變動(dòng)默認(rèn)隱含了CABasicAnimation動(dòng)畫實(shí)現(xiàn)锥涕,詳情大家可以參照Xcode幫助文檔中“Animatable Properties”一節(jié)。
  • 在CALayer中很少使用frame屬性狭吼,因?yàn)閒rame本身不支持動(dòng)畫效果层坠,通常使用bounds和position代替。
  • CALayer中透明度使用opacity表示而不是alpha刁笙;中心點(diǎn)使用position表示而不是center破花。
  • anchorPoint屬性是圖層的錨點(diǎn)伞租,范圍在(01,01)表示在x休里、y軸的比例,這個(gè)點(diǎn)永遠(yuǎn)可以同position(中心點(diǎn))重合高诺,當(dāng)圖層中心點(diǎn)固定后摘悴,調(diào)整anchorPoint即可達(dá)到調(diào)整圖層顯示位置的作用(因?yàn)樗肋h(yuǎn)和position重合)

為了進(jìn)一步說(shuō)明anchorPoint的作用峭梳,假設(shè)有一個(gè)層大小100*100,現(xiàn)在中心點(diǎn)位置(50,50)蹂喻,由此可以得出frame(0,0,100,100)葱椭。上面說(shuō)過(guò)anchorPoint默認(rèn)為(0.5,0.5),同中心點(diǎn)position重合叉橱,此時(shí)使用圖形描述如圖1挫以;當(dāng)修改anchorPoint為(0,0),此時(shí)錨點(diǎn)處于圖層左上角窃祝,但是中心點(diǎn)poition并不會(huì)改變掐松,因此圖層會(huì)向右下角移動(dòng),如圖2;然后修改anchorPoint為(1,1,)大磺,position還是保持位置不變抡句,錨點(diǎn)處于圖層右下角,此時(shí)圖層如圖3杠愧。



下面通過(guò)一個(gè)簡(jiǎn)單的例子演示一下上面幾個(gè)屬性待榔,程序初始化階段我們定義一個(gè)正方形,但是圓角路徑調(diào)整為正方形邊長(zhǎng)的一半流济,使其看起來(lái)是一個(gè)圓形锐锣,在點(diǎn)擊屏幕的時(shí)候修改圖層的屬性形成動(dòng)畫效果(注意在程序中沒有直接修改UIView的layer屬性,因?yàn)楦鶊D層無(wú)法形成動(dòng)畫效果):

//  KCMainViewController.m
//  CALayer
//
//  Created by Kenshin Cui on 14-3-22.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
#import "KCMainViewController.h"
#define WIDTH 50
@interface KCMainViewController ()
@end
@implementation KCMainViewController

- (void)viewDidLoad {
[super viewDidLoad];
[self drawMyLayer];
}

#pragma mark 繪制圖層
-(void)drawMyLayer{
CGSize size=[UIScreen mainScreen].bounds.size;
//獲得根圖層
CALayer *layer=[[CALayer alloc]init];
//設(shè)置背景顏色,由于QuartzCore是跨平臺(tái)框架绳瘟,無(wú)法直接使用UIColor
layer.backgroundColor=[UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0].CGColor;
//設(shè)置中心點(diǎn)
layer.position=CGPointMake(size.width/2, size.height/2);
//設(shè)置大小
layer.bounds=CGRectMake(0, 0, WIDTH,WIDTH);
//設(shè)置圓角,當(dāng)圓角半徑等于矩形的一半時(shí)看起來(lái)就是一個(gè)圓形
layer.cornerRadius=WIDTH/2;
//設(shè)置陰影
layer.shadowColor=[UIColor grayColor].CGColor;
layer.shadowOffset=CGSizeMake(2, 2);
layer.shadowOpacity=.9;
//設(shè)置邊框
//    layer.borderColor=[UIColor whiteColor].CGColor;
//    layer.borderWidth=1;
//   設(shè)置錨點(diǎn)
//    layer.anchorPoint=CGPointZero;
[self.view.layer addSublayer:layer];
}
#pragma mark 點(diǎn)擊放大
-(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

運(yùn)行效果:



3. CALayer繪圖

在使用Quartz 2D繪圖時(shí)雕憔,當(dāng)調(diào)用了UIView的drawRect:方法繪制圖形、圖像糖声,這種方式本質(zhì)還是在圖層中繪制斤彼,但是這里會(huì)著重介紹一下如何直接在圖層中繪圖。在圖層中繪圖的方式跟原來(lái)基本沒有區(qū)別蘸泻,只是drawRect:方法是由UIKit組件進(jìn)行調(diào)用琉苇,因此里面可以使用一些UIKit封裝的方法進(jìn)行繪圖,而直接繪制到圖層的方法由于并非UIKit直接調(diào)用因此只能用原生的Core Graphics方法繪制悦施。

圖層繪圖有兩種方法并扇,不管使用哪種方法繪制完必須調(diào)用圖層的setNeedDisplay方法(注意是圖層的方法,不是UIView的方法歼争,前面我們介紹過(guò)UIView也有此方法)

  1. 通過(guò)圖層代理drawLayer: inContext:方法繪制
  2. 通過(guò)自定義圖層drawInContext:方法繪制

使用代理方法繪圖

通過(guò)代理方法進(jìn)行圖層繪圖只要指定圖層的代理拜马,然后在代理對(duì)象中重寫*-(void)drawLayer:(CALayer )layer inContext:(CGContextRef)ctx方法即可渗勘。需要注意這個(gè)方法雖然是代理方法但是不用手動(dòng)實(shí)現(xiàn)CALayerDelegate沐绒,因?yàn)镃ALayer定義中給NSObject做了分類擴(kuò)展,所有的NSObject都包含這個(gè)方法旺坠。另外設(shè)置完代理后必須要調(diào)用圖層的setNeedDisplay方法乔遮,否則繪制的內(nèi)容無(wú)法顯示。

下面的代碼演示了在一個(gè)自定義圖層繪制一張圖像并將圖像設(shè)置成圓形取刃,這種效果在很多應(yīng)用中很常見蹋肮,如最新版的手機(jī)QQ頭像就是這種效果:

//
//  KCMainViewController.m
//  CALayer
//
//  Created by Kenshin Cui on 14-3-22.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
#import "KCMainViewController.h"
#define PHOTO_HEIGHT 150
@interface KCMainViewController ()
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
//自定義圖層
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;
//注意僅僅設(shè)置圓角,對(duì)于圖形而言可以正常顯示璧疗,但是對(duì)于圖層中繪制的圖片無(wú)法正確顯示
//如果想要正確顯示則必須設(shè)置masksToBounds=YES坯辩,剪切子圖層
layer.masksToBounds=YES;
//陰影效果無(wú)法和masksToBounds同時(shí)使用,因?yàn)閙asksToBounds的目的就是剪切外邊框崩侠,而陰影效果剛好在外邊框
//    layer.shadowColor=[UIColor grayColor].CGColor;
//    layer.shadowOffset=CGSizeMake(2, 2);
//    layer.shadowOpacity=1;
//設(shè)置邊框
layer.borderColor=[UIColor whiteColor].CGColor;
layer.borderWidth=2;
//設(shè)置圖層代理
layer.delegate=self;
//添加圖層到根圖層
[self.view.layer addSublayer:layer];
//調(diào)用圖層setNeedDisplay,否則代理方法不會(huì)被調(diào)用
[layer setNeedsDisplay];
}
#pragma mark 繪制圖形漆魔、圖像到圖層,注意參數(shù)中的ctx是圖層的圖形上下文,其中繪圖位置也是相對(duì)圖層而言的
-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{
//    NSLog(@"%@",layer);//這個(gè)圖層正是上面定義的圖層
CGContextSaveGState(ctx);
//圖形上下文形變改抡,解決圖片倒立的問題
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -PHOTO_HEIGHT);
UIImage *image=[UIImage imageNamed:@"photo.png"];
//注意這個(gè)位置是相對(duì)于圖層而言的不是屏幕
CGContextDrawImage(ctx, CGRectMake(0, 0, PHOTO_HEIGHT, PHOTO_HEIGHT), image.CGImage);
//    CGContextFillRect(ctx, CGRectMake(0, 0, 100, 100));
//    CGContextDrawPath(ctx, kCGPathFillStroke);
CGContextRestoreGState(ctx);
}
@end

運(yùn)行效果:


使用代理方法繪制圖形矢炼、圖像時(shí)在drawLayer:inContext:方法中可以通過(guò)事件參數(shù)獲得繪制的圖層和圖形上下文。在這個(gè)方法中繪圖時(shí)所有的位置都是相對(duì)于圖層而言的阿纤,圖形上下文指的也是當(dāng)前圖層的圖形上下文句灌。

需要注意的是上面代碼中繪制圖片圓形裁切效果時(shí)如果不設(shè)置masksToBounds是無(wú)法顯示圓形,但是對(duì)于其他圖形卻沒有這個(gè)限制欠拾。原因就是當(dāng)繪制一張圖片到圖層上的時(shí)候會(huì)重新創(chuàng)建一個(gè)圖層添加到當(dāng)前圖層胰锌,這樣一來(lái)如果設(shè)置了圓角之后雖然底圖層有圓角效果,但是子圖層還是矩形藐窄,只有設(shè)置了masksToBounds為YES讓子圖層按底圖層剪切才能顯示圓角效果匕荸。同樣的,有些朋友經(jīng)常在網(wǎng)上提問說(shuō)為什么使用UIImageView的layer設(shè)置圓角后圖片無(wú)法顯示圓角枷邪,只有設(shè)置masksToBounds才能出現(xiàn)效果榛搔,也是類似的問題。

擴(kuò)展1--帶陰影效果的圓形圖片裁切

如果設(shè)置了masksToBounds=YES之后確實(shí)可以顯示圖片圓角效果东揣,但遺憾的是設(shè)置了這個(gè)屬性之后就無(wú)法設(shè)置陰影效果践惑。因?yàn)閙asksToBounds=YES就意味著外邊框不能顯示,而陰影恰恰作為外邊框繪制的嘶卧,這樣兩個(gè)設(shè)置就產(chǎn)生了矛盾尔觉。要解決這個(gè)問題不妨換個(gè)思路:使用兩個(gè)大小一樣的圖層,下面的圖層負(fù)責(zé)繪制陰影芥吟,上面的圖層用來(lái)顯示圖片侦铜。

//
//  KCMainViewController.m
//  CALayer
//
//  Created by Kenshin Cui on 14-3-22.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
#import "KCMainViewController.h"
#define PHOTO_HEIGHT 150
@interface KCMainViewController ()
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
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];
//容器圖層
CALayer *layer=[[CALayer alloc]init];
layer.bounds=bounds;
layer.position=position;
layer.backgroundColor=[UIColor redColor].CGColor;
layer.cornerRadius=cornerRadius;
layer.masksToBounds=YES;
layer.borderColor=[UIColor whiteColor].CGColor;
layer.borderWidth=borderWidth;
//設(shè)置圖層代理
layer.delegate=self;
//添加圖層到根圖層
[self.view.layer addSublayer:layer];
//調(diào)用圖層setNeedDisplay,否則代理方法不會(huì)被調(diào)用
[layer setNeedsDisplay];
}
#pragma mark 繪制圖形、圖像到圖層钟鸵,注意參數(shù)中的ctx是圖層的圖形上下文钉稍,其中繪圖位置也是相對(duì)圖層而言的
-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{
//    NSLog(@"%@",layer);//這個(gè)圖層正是上面定義的圖層
CGContextSaveGState(ctx);
//圖形上下文形變,解決圖片倒立的問題
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -PHOTO_HEIGHT);
UIImage *image=[UIImage imageNamed:@"photo.jpg"];
//注意這個(gè)位置是相對(duì)于圖層而言的不是屏幕
CGContextDrawImage(ctx, CGRectMake(0, 0, PHOTO_HEIGHT, PHOTO_HEIGHT), image.CGImage);
//    CGContextFillRect(ctx, CGRectMake(0, 0, 100, 100));
//    CGContextDrawPath(ctx, kCGPathFillStroke);
CGContextRestoreGState(ctx);
}
@end

運(yùn)行效果:

擴(kuò)展2--圖層的形變

從上面代碼中大家不難發(fā)現(xiàn)使用Core Graphics繪制圖片時(shí)會(huì)倒立顯示棺耍,對(duì)圖層的圖形上下文進(jìn)行了反轉(zhuǎn)贡未。在前一篇文章中也采用了類似的方法去解決這個(gè)問題,但是在那篇文章中也提到過(guò)如果直接讓圖像沿著x軸旋轉(zhuǎn)180度同樣可以達(dá)到正確顯示的目的蒙袍,只是當(dāng)時(shí)的旋轉(zhuǎn)靠圖形上下文還無(wú)法繞x軸旋轉(zhuǎn)俊卤。今天學(xué)習(xí)了圖層之后,其實(shí)可以控制圖層直接旋轉(zhuǎn)而不用借助于圖形上下文的形變操作害幅,而且這么操作起來(lái)會(huì)更加簡(jiǎn)單和直觀消恍。對(duì)于上面的程序,只需要設(shè)置圖層的transform屬性即可以现。需要注意的是transform是CATransform3D類型狠怨,形變可以在三個(gè)維度上進(jìn)行佩抹,使用方法和前面介紹的二維形變是類似的,而且都有對(duì)應(yīng)的形變?cè)O(shè)置方法(如:CATransform3DMakeTranslation()取董、CATransform3DMakeScale()棍苹、CATransform3DMakeRotation())。下面的代碼通過(guò)CATransform3DMakeRotation()方法在x軸旋轉(zhuǎn)180度解決倒立問題:

//
//  形變演示
//  CALayer
//
//  Created by Kenshin Cui on 14-3-22.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
#import "KCMainViewController.h"
#define PHOTO_HEIGHT 150
@interface KCMainViewController ()
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
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];
//容器圖層
CALayer *layer=[[CALayer alloc]init];
layer.bounds=bounds;
layer.position=position;
layer.backgroundColor=[UIColor redColor].CGColor;
layer.cornerRadius=cornerRadius;
layer.masksToBounds=YES;
layer.borderColor=[UIColor whiteColor].CGColor;
layer.borderWidth=borderWidth;
//利用圖層形變解決圖像倒立問題
layer.transform=CATransform3DMakeRotation(M_PI, 1, 0, 0);
//設(shè)置圖層代理
layer.delegate=self;
//添加圖層到根圖層
[self.view.layer addSublayer:layer];
//調(diào)用圖層setNeedDisplay,否則代理方法不會(huì)被調(diào)用
[layer setNeedsDisplay];
}
#pragma mark 繪制圖形茵汰、圖像到圖層枢里,注意參數(shù)中的ctx時(shí)圖層的圖形上下文,其中繪圖位置也是相對(duì)圖層而言的
-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{
//    NSLog(@"%@",layer);//這個(gè)圖層正是上面定義的圖層
UIImage *image=[UIImage imageNamed:@"photo.jpg"];
//注意這個(gè)位置是相對(duì)于圖層而言的不是屏幕
CGContextDrawImage(ctx, CGRectMake(0, 0, PHOTO_HEIGHT, PHOTO_HEIGHT), image.CGImage);
}
@end

事實(shí)上如果僅僅就顯示一張圖片在圖層中當(dāng)然沒有必要那么麻煩蹂午,直接設(shè)置圖層contents就可以了栏豺,不牽涉到繪圖也就沒有倒立的問題了。

//
//  圖層內(nèi)容設(shè)置
//  CALayer
//
//  Created by Kenshin Cui on 14-3-22.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
#import "KCMainViewController.h"
#define PHOTO_HEIGHT 150
@interface KCMainViewController ()
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
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];
//容器圖層
CALayer *layer=[[CALayer alloc]init];
layer.bounds=bounds;
layer.position=position;
layer.backgroundColor=[UIColor redColor].CGColor;
layer.cornerRadius=cornerRadius;
layer.masksToBounds=YES;
layer.borderColor=[UIColor whiteColor].CGColor;
layer.borderWidth=borderWidth;
//設(shè)置內(nèi)容(注意這里一定要轉(zhuǎn)換為CGImage)
UIImage *image=[UIImage imageNamed:@"photo.jpg"];
//    layer.contents=(id)image.CGImage;
[layer setContents:(id)image.CGImage];
//添加圖層到根圖層
[self.view.layer addSublayer:layer];
}
@end

既然如此為什么還大費(fèi)周章的說(shuō)形變呢豆胸,因?yàn)樾巫儗?duì)于動(dòng)畫有特殊的意義奥洼。在動(dòng)畫開發(fā)中形變往往不是直接設(shè)置transform,而是通過(guò)keyPath進(jìn)行設(shè)置晚胡。這種方法設(shè)置形變的本質(zhì)和前面沒有區(qū)別灵奖,只是利用了KVC可以動(dòng)態(tài)修改其屬性值而已,但是這種方式在動(dòng)畫中確實(shí)很常用的估盘,因?yàn)樗梢院芊奖愕膶追N形變組合到一起使用瓷患。同樣是解決動(dòng)畫旋轉(zhuǎn)問題,只要將前面的旋轉(zhuǎn)代碼改為下面的代碼即可:

[layer setValue:@M_PI forKeyPath:@"transform.rotation.x"];

當(dāng)然遣妥,通過(guò)key path設(shè)置形變參數(shù)就需要了解有哪些key path可以設(shè)置擅编,這里就不再一一列舉,大家可以參照Xcode幫助文檔中“CATransform3D Key Paths”一節(jié)箫踩,里面描述的很詳細(xì)爱态。

使用自定義圖層繪圖

在自定義圖層中繪圖時(shí)只要自己編寫一個(gè)類繼承于CALayer然后在drawInContext:中繪圖即可。同前面在代理方法繪圖一樣境钟,要顯示圖層中繪制的內(nèi)容也要調(diào)用圖層的setNeedDisplay方法锦担,否則drawInContext方法將不會(huì)調(diào)用。

在使用Quartz 2D在UIView中繪制圖形的本質(zhì)也是繪制到圖層中吱韭,為了說(shuō)明這個(gè)問題下面演示自定義圖層繪圖時(shí)沒有直接在視圖控制器中調(diào)用自定義圖層吆豹,而是在一個(gè)UIView將自定義圖層添加到UIView的根圖層中(例子中的UIView跟自定義圖層繪圖沒有直接關(guān)系)。從下面的代碼中可以看到:UIView在顯示時(shí)其根圖層會(huì)自動(dòng)創(chuàng)建一個(gè)CGContextRef(CALayer本質(zhì)使用的是位圖上下文)理盆,同時(shí)調(diào)用圖層代理(UIView創(chuàng)建圖層會(huì)自動(dòng)設(shè)置圖層代理為其自身)的draw: inContext:方法并將圖形上下文作為參數(shù)傳遞給這個(gè)方法。而在UIView的draw:inContext:方法中會(huì)調(diào)用其drawRect:方法凑阶,在drawRect:方法中使用UIGraphicsGetCurrentContext()方法得到的上下文正是前面創(chuàng)建的上下文猿规。
KCLayer.m

//
//  KCLayer.m
//  CALayer
//
//  Created by Kenshin Cui on 14-3-22.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
#import "KCLayer.h"
@implementation KCLayer
-(void)drawInContext:(CGContextRef)ctx{
NSLog(@"3-drawInContext:");
NSLog(@"CGContext:%@",ctx);
//    CGContextRotateCTM(ctx, M_PI_4);
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);
//    CGContextFillRect(ctx, CGRectMake(0, 0, 100, 100));
//    CGContextFillEllipseInRect(ctx, CGRectMake(50, 50, 100, 100));
CGContextMoveToPoint(ctx, 94.5, 33.5);
//// Star Drawing
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

KCView.m

//
//  KCView.m
//  CALayer
//
//  Created by Kenshin Cui on 14-3-22.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
#import "KCView.h"
#import "KCLayer.h"
@implementation KCView
-(instancetype)initWithFrame:(CGRect)frame{
NSLog(@"initWithFrame:");
if (self=[super initWithFrame:frame]) {
KCLayer *layer=[[KCLayer alloc]init];
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{
NSLog(@"2-drawRect:");
NSLog(@"CGContext:%@",UIGraphicsGetCurrentContext());//得到的當(dāng)前圖形上下文正是drawLayer中傳遞的
[super drawRect:rect];
}
-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{
NSLog(@"1-drawLayer:inContext:");
NSLog(@"CGContext:%@",ctx);
[super drawLayer:layer inContext:ctx];
}
@end

KCMainViewController.m

//
//  KCMainViewController.m
//  CALayer
//
//  Created by Kenshin Cui on 14-3-22.
//  Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//
#import "KCMainViewController.h"
#import "KCView.h"
@interface KCMainViewController ()
@end

@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
KCView *view=[[KCView 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

運(yùn)行效果:


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市宙橱,隨后出現(xiàn)的幾起案子姨俩,更是在濱河造成了極大的恐慌蘸拔,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,222評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件环葵,死亡現(xiàn)場(chǎng)離奇詭異调窍,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)张遭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,455評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門邓萨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人菊卷,你說(shuō)我怎么就攤上這事缔恳。” “怎么了洁闰?”我有些...
    開封第一講書人閱讀 157,720評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵歉甚,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我扑眉,道長(zhǎng)纸泄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,568評(píng)論 1 284
  • 正文 為了忘掉前任腰素,我火速辦了婚禮刃滓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘耸弄。我一直安慰自己咧虎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,696評(píng)論 6 386
  • 文/花漫 我一把揭開白布计呈。 她就那樣靜靜地躺著砰诵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪捌显。 梳的紋絲不亂的頭發(fā)上茁彭,一...
    開封第一講書人閱讀 49,879評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音扶歪,去河邊找鬼理肺。 笑死,一個(gè)胖子當(dāng)著我的面吹牛善镰,可吹牛的內(nèi)容都是我干的妹萨。 我是一名探鬼主播,決...
    沈念sama閱讀 39,028評(píng)論 3 409
  • 文/蒼蘭香墨 我猛地睜開眼炫欺,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼乎完!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起品洛,我...
    開封第一講書人閱讀 37,773評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤树姨,失蹤者是張志新(化名)和其女友劉穎摩桶,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體帽揪,經(jīng)...
    沈念sama閱讀 44,220評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡硝清,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,550評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了转晰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芦拿。...
    茶點(diǎn)故事閱讀 38,697評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖挽霉,靈堂內(nèi)的尸體忽然破棺而出防嗡,到底是詐尸還是另有隱情,我是刑警寧澤侠坎,帶...
    沈念sama閱讀 34,360評(píng)論 4 332
  • 正文 年R本政府宣布蚁趁,位于F島的核電站,受9級(jí)特大地震影響实胸,放射性物質(zhì)發(fā)生泄漏他嫡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,002評(píng)論 3 315
  • 文/蒙蒙 一庐完、第九天 我趴在偏房一處隱蔽的房頂上張望钢属。 院中可真熱鬧,春花似錦门躯、人聲如沸淆党。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,782評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)染乌。三九已至,卻和暖如春懂讯,著一層夾襖步出監(jiān)牢的瞬間荷憋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,010評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工褐望, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留勒庄,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,433評(píng)論 2 360
  • 正文 我出身青樓瘫里,卻偏偏與公主長(zhǎng)得像实蔽,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子减宣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,587評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容

  • 在iOS中隨處都可以看到絢麗的動(dòng)畫效果盐须,實(shí)現(xiàn)這些動(dòng)畫的過(guò)程并不復(fù)雜,今天將帶大家一窺ios動(dòng)畫全貌漆腌。在這里你可以看...
    每天刷兩次牙閱讀 8,471評(píng)論 6 30
  • 在iOS中隨處都可以看到絢麗的動(dòng)畫效果贼邓,實(shí)現(xiàn)這些動(dòng)畫的過(guò)程并不復(fù)雜,今天將帶大家一窺iOS動(dòng)畫全貌闷尿。在這里你可以看...
    F麥子閱讀 5,104評(píng)論 5 13
  • 概覽 在iOS中隨處都可以看到絢麗的動(dòng)畫效果塑径,實(shí)現(xiàn)這些動(dòng)畫的過(guò)程并不復(fù)雜,今天將帶大家一窺iOS動(dòng)畫全貌填具。在這里你...
    Yiart閱讀 3,799評(píng)論 3 34
  • 前言 本文只要描述了iOS中的Core Animation(核心動(dòng)畫:隱式動(dòng)畫统舀、顯示動(dòng)畫)、貝塞爾曲線劳景、UIVie...
    GitHubPorter閱讀 3,607評(píng)論 7 11
  • 概覽 在iOS中隨處都可以看到絢麗的動(dòng)畫效果盟广,實(shí)現(xiàn)這些動(dòng)畫的過(guò)程并不復(fù)雜闷串,今天將帶大家一窺iOS動(dòng)畫全貌。在這里你...
    被吹落的風(fēng)閱讀 1,555評(píng)論 1 2