CALayer 和 UIView的區(qū)別和聯(lián)系

前言

前面發(fā)了一篇iOS 面試的文章赋除,在說到 UIView 和 CALayer 的區(qū)別和聯(lián)系的時候,被喵神指出沒有切中要點戳护,所以這里就 CALayer 和 UIView 這個問題重新整理了下金抡。這里會先分條解釋,最后會在文章的結(jié)尾給出概括性總結(jié)腌且。

1.首先UIView可以響應事件梗肝,Layer不可以.

UIKit使用UIResponder作為響應對象,來響應系統(tǒng)傳遞過來的事件并進行處理铺董。UIApplication巫击、UIViewController、UIView精续、和所有從UIView派生出來的UIKit類(包括UIWindow)都直接或間接地繼承自UIResponder類坝锰。

在 UIResponder中定義了處理各種事件和事件傳遞的接口, 而 CALayer直接繼承 NSObject,并沒有相應的處理事件的接口重付。

下面列舉一些處理觸摸事件的接口

– touchesBegan:withEvent:

– touchesMoved:withEvent:

– touchesEnded:withEvent:

– touchesCancelled:withEvent:

其實還有一些運動和遠程控制事件等等顷级,這里就不一一列舉了。

下面的兩篇文章詳細介紹了 iOS 事件的處理和傳遞

參考鏈接

http://blog.csdn.net/chun799/article/details/8223612

http://yishuiliunian.gitbooks.io/implementate-tableview-to-understand-ios/content/uikit/1-1-2.html

2.View和CALayer的Frame映射及View如何創(chuàng)建CALayer.

一個 Layer 的 frame 是由它的 anchorPoint,position,bounds,和 transform 共同決定的确垫,而一個 View 的 frame 只是簡單的返回 Layer的 frame弓颈,同樣 View 的 center和 bounds 也是返回 Layer 的一些屬性拣凹。(PS:center有些特列)為了證明這些,我做了如下的測試恨豁。

首先我自定義了兩個類CustomView,CustomLayer分別繼承 UIView 和 CALayer

在 CustomView 中重寫了

+ (Class)layerClass{return[CustomLayer class];}- (void)setFrame:(CGRect)frame{? ? [supersetFrame:frame];}- (void)setCenter:(CGPoint)center{? ? [supersetCenter:center];}- (void)setBounds:(CGRect)bounds{? ? [supersetBounds:bounds];}

同樣在 CustomLayer中同樣重寫這些方法。只是setCenter方法改成setPosition方法

我在兩個類的初始化方法中都打下了斷點

image

首先我們會發(fā)現(xiàn)爬迟,我們在 [view initWithFrame] 的時候調(diào)用私有方法【UIView _createLayerWithFrame】去創(chuàng)建 CALayer橘蜜。

然后我在創(chuàng)建 View 的時候,在 Layer 和 View 中Frame 相關的所有方法中都加上斷點付呕,可以看到大致如下的調(diào)用順序如下

[UIView _createLayerWithFrame]

[Layer setBounds:bounds]

[UIView setFrame:Frame]

[Layer setFrame:frame]

[Layer setPosition:position]

[Layer setBounds:bounds]

我發(fā)現(xiàn)在創(chuàng)建的過程只有調(diào)用了 Layer 的設置尺寸和位置的然而并沒有調(diào)用View 的SetCenter和SetBounds方法计福。

然后我發(fā)現(xiàn)當我修改了 view的bounds.size或者bounds.origin的時候也只會調(diào)用上邊 Layer的一些方法。所以我大膽的猜一下徽职,View 的 Center 和 Bounds 只是直接返回layer 對應的 Position 和 Bounds.

View中frame getter方法象颖,bounds和center,UIView并沒有做什么工作姆钉;它只是簡單的各自調(diào)用它底層的CALayer的frame说订,bounds和position方法。

關于 Frame 的理解參考:http://www.cocoachina.com/industry/20131209/7498.html

3.UIView主要是對顯示內(nèi)容的管理而 CALayer 主要側(cè)重顯示內(nèi)容的繪制潮瓶。

我在 UIView 和 CALayer 分別重寫了父類的方法陶冷。

[UIViewdrawRect:rect]//UIView[CALayerdisplay]//CALayer

然后我在上面兩個方法加了斷點,可以看到如下的執(zhí)行毯辅。

image

可以看到 UIView 是 CALayer 的CALayerDelegate埂伦,我猜測是在代理方法內(nèi)部[UIView(CALayerDelegate) drawLayer:inContext]調(diào)用 UIView 的 DrawRect方法,從而繪制出了 UIView 的內(nèi)容.

4.在做 iOS 動畫的時候思恐,修改非 RootLayer的屬性(譬如位置沾谜、背景色等)會默認產(chǎn)生隱式動畫,而修改UIView則不會胀莹。

對于每一個 UIView 都有一個 layer,把這個 layer 且稱作RootLayer,而不是 View 的根 Layer的叫做 非 RootLayer基跑。我們對UIView的屬性修改時時不會產(chǎn)生默認動畫,而對單獨 layer屬性直接修改會嗜逻,這個默認動畫的時間缺省值是0.25s.

在 Core Animation 編程指南的 “How to Animate Layer-Backed Views” 中涩僻,對為什么會這樣做出了一個解釋:

UIView 默認情況下禁止了 layer 動畫,但是在 animation block 中又重新啟用了它們

是因為任何可動畫的 layer 屬性改變時栈顷,layer 都會尋找并運行合適的 'action' 來實行這個改變逆日。在 Core Animation 的專業(yè)術(shù)語中就把這樣的動畫統(tǒng)稱為動作 (action,或者 CAAction)萄凤。

layer 通過向它的 delegate 發(fā)送 actionForLayer:forKey: 消息來詢問提供一個對應屬性變化的 action室抽。delegate 可以通過返回以下三者之一來進行響應:

它可以返回一個動作對象,這種情況下 layer 將使用這個動作靡努。

它可以返回一個 nil坪圾, 這樣 layer 就會到其他地方繼續(xù)尋找晓折。

它可以返回一個 NSNull 對象,告訴 layer 這里不需要執(zhí)行一個動作兽泄,搜索也會就此停止漓概。

當 layer 在背后支持一個 view 的時候,view 就是它的 delegate病梢;

這部分的具體內(nèi)容參考:http://objccn.io/issue-12-4/

總結(jié)

總接來說就是如下幾點:

每個 UIView 內(nèi)部都有一個 CALayer 在背后提供內(nèi)容的繪制和顯示胃珍,并且 UIView 的尺寸樣式都由內(nèi)部的 Layer 所提供。兩者都有樹狀層級結(jié)構(gòu)蜓陌,layer 內(nèi)部有 SubLayers觅彰,View 內(nèi)部有 SubViews.但是 Layer 比 View 多了個AnchorPoint

在 View顯示的時候,UIView 做為 Layer 的 CALayerDelegate,View 的顯示內(nèi)容由內(nèi)部的 CALayer 的 display

CALayer 是默認修改屬性支持隱式動畫的钮热,在給 UIView 的 Layer 做動畫的時候填抬,View 作為 Layer 的代理,Layer? 通過 actionForLayer:forKey:向 View請求相應的 action(動畫行為)

layer 內(nèi)部維護著三分 layer tree,分別是 presentLayer Tree(動畫樹),modeLayer Tree(模型樹), Render Tree (渲染樹),在做 iOS動畫的時候隧期,我們修改動畫的屬性飒责,在動畫的其實是 Layer 的 presentLayer的屬性值,而最終展示在界面上的其實是提供 View的modelLayer

兩者最明顯的區(qū)別是 View可以接受并處理事件,而 Layer 不可以

補充:AllanHou(作者) http://www.cocoachina.com/ios/20161205/18278.html(鏈接)

CALayer屬性表具體如下

代碼示例

import"ViewController.h"

@interface?ViewController?()

@end

@implementation?ViewController

(void)viewDidLoad?{

[superviewDidLoad];

[self?仿射變換_07];

}

pragma?mark?-?仿射變換

(void)仿射變換_07

{

CALayer?*layer?=?[CALayer?layer];

layer.frame?=?CGRectMake(60,?60,?200,?300);

layer.backgroundColor?=?[UIColor?redColor].CGColor;

[self.view.layer?addSublayer:layer];

//設置層內(nèi)容

layer.contents?=?(__bridge?id?_Nullable)([UIImage?imageNamed:@"3"].CGImage);

//x軸旋轉(zhuǎn)45度

//layer.transform?=?CATransform3DMakeRotation(88(M_PI)/180,?1,?0,?0);

//旋轉(zhuǎn)45度?度數(shù)?x?y?z

layer.transform?=?CATransform3DMakeRotation(45(M_PI)/180,?0.5,?1,?1);

/

CATransform3DMakeRotation:3D旋轉(zhuǎn)

CATransform3DTranslate:3D位移

CATransform3DMakeScale:3D比例?/

//仿射變換

//layer.affineTransform?=?CGAffineTransformMakeRotation();

}

pragma?mark?-?剪切圖片的一部分

(void)剪切圖片的一部分_06

{

int?width?=?80;

int?height?=?100;

int?sapce?=?1;

for(int?i?=?0;?i?<?9;?i++)

{

UIView?*view?=?[[UIView?alloc]?init];

view.frame?=?CGRectMake(60?+?(width?+?sapce)?*?(i%3),?80?+?(height?+?sapce)?*?(i/3),?width,?height);

view.backgroundColor?=?[UIColor?redColor];

//設置層的內(nèi)容

view.layer.contents?=?(__bridge?id?_Nullable)([UIImage?imageNamed:@"1.jpeg"].CGImage);

//設置圖片剪切的范圍??[0,1]??contentsRect?圖層顯示內(nèi)容的大小和位置

view.layer.contentsRect?=?CGRectMake(1.0/3.0?*?(i%3),?1.0/3.0?*?(i/3),?1.0/3.0,?1.0/3.0);

[self.view?addSubview:view];

/*

1:(0仆潮,0读拆,1/3,1/3)

2:?(1/3,0,1/3,1/3)

3:?(2/3,0鸵闪,1/3,1/3)

*/

}

}

pragma?mark?-?圖層添加邊框和圓角

(void)圖層添加邊框和圓角_05

{

CALayer?*layer?=?[CALayer?layer];

layer.frame?=?CGRectMake(60,?60,?100,?100);

layer.backgroundColor?=?[UIColor?redColor].CGColor;

[self.view.layer?addSublayer:layer];

//邊框顏色

layer.borderColor?=?[UIColor?greenColor].CGColor;

//邊框?qū)挾?/p>

layer.borderWidth?=?3;

//圓角

layer.cornerRadius?=?10;

}

pragma?mark?-?剪切超過父圖層的部分

(void)剪切超過父圖層的部分_04

{

CALayer?*layer?=?[CALayer?layer];

layer.frame?=?CGRectMake(60,?60,?100,?100);

layer.backgroundColor?=?[UIColor?redColor].CGColor;

[self.view.layer?addSublayer:layer];

CALayer?*layer2?=?[CALayer?layer];

layer2.frame?=?CGRectMake(30,?30,?100,?100);

layer2.backgroundColor?=?[UIColor?blueColor].CGColor;

[layer?addSublayer:layer2];

//剪切超過父圖層的部分

layer.masksToBounds?=?YES;

}

pragma?mark?-陰影路徑

(void)陰影路徑_03

{

CALayer?*layer?=?[CALayer?layer];

layer.frame?=?CGRectMake(60,?60,?100,?100);

layer.backgroundColor?=?[UIColor?redColor].CGColor;

[self.view.layer?addSublayer:layer];

//1表示不透明,注意:設置陰影當前值不能為0檐晕,默認是0

layer.shadowOpacity?=?1.0;

//陰影顏色

layer.shadowColor?=?[UIColor?yellowColor].CGColor;

//創(chuàng)建路徑

CGMutablePathRef?path?=?CGPathCreateMutable();

//橢圓

CGPathAddEllipseInRect(path,?NULL,?CGRectMake(0,?0,?200,?200));

layer.shadowPath?=?path;

CGPathRelease(path);

}

pragma?mark?-?添加陰影_02

(void)層的陰影_02

{

CALayer?*layer?=?[CALayer?layer];

layer.frame?=?CGRectMake(60,?60,?100,?100);

layer.backgroundColor?=?[UIColor?redColor].CGColor;

[self.view.layer?addSublayer:layer];

//1表示不透明,注意:設置陰影當前值不能為0,默認是0

layer.shadowOpacity?=?0.9;

//陰影顏色

layer.shadowColor?=?[UIColor?yellowColor].CGColor;

//陰影偏移?->x正?<-x負?y同理

layer.shadowOffset?=?CGSizeMake(10,?-10);

//陰影的圓角半徑

layer.shadowRadius?=?10;

}

pragma?mark?-?圖層內(nèi)容和內(nèi)容模式_01

(void)圖層內(nèi)容和內(nèi)容模式_01?{

CALayer?*layer?=?[CALayer?layer];

layer.frame?=?CGRectMake(20,?20,?100,?100);

layer.backgroundColor?=?[UIColor?redColor].CGColor;

[self.view.layer?addSublayer:layer];

//設置層內(nèi)容

layer.contents?=?(__bridge?id?_Nullable)([UIImage?imageNamed:@"1.jpeg"].CGImage);

//內(nèi)容模式蚌讼,類似于UIImageView的contentMode辟灰。默認是填充整個區(qū)域?kCAGravityResize

//kCAGravityResizeAspectFill?這個會向左邊靠?貼到view的邊邊上

//kCAGravityResizeAspect?這個好像就是按比例了?反正是長方形

layer.contentsGravity?=?kCAGravityResizeAspect;

//設置控制器視圖的背景圖片

/

性能很高。?/

self.view.layer.contents?=?(__bridge?id?_Nullable)([UIImage?imageNamed:@"3"].CGImage);

}

@end



文/kissGod(簡書作者)

原文鏈接:http://www.reibang.com/p/079e5cf0f014

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末篡石,一起剝皮案震驚了整個濱河市芥喇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌凰萨,老刑警劉巖继控,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異胖眷,居然都是意外死亡武通,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門珊搀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來冶忱,“玉大人,你說我怎么就攤上這事境析∏羟梗” “怎么了派诬?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長链沼。 經(jīng)常有香客問我默赂,道長,這世上最難降的妖魔是什么括勺? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任放可,我火速辦了婚禮,結(jié)果婚禮上朝刊,老公的妹妹穿的比我還像新娘。我一直安慰自己蜈缤,他們只是感情好拾氓,可當我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著底哥,像睡著了一般咙鞍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上趾徽,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天续滋,我揣著相機與錄音,去河邊找鬼孵奶。 笑死疲酌,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的了袁。 我是一名探鬼主播朗恳,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼载绿!你這毒婦竟也來了粥诫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤崭庸,失蹤者是張志新(化名)和其女友劉穎怀浆,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體怕享,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡执赡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了函筋。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搀玖。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖驻呐,靈堂內(nèi)的尸體忽然破棺而出灌诅,到底是詐尸還是另有隱情芳来,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布猜拾,位于F島的核電站即舌,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏挎袜。R本人自食惡果不足惜顽聂,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望盯仪。 院中可真熱鬧紊搪,春花似錦、人聲如沸全景。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽爸黄。三九已至滞伟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間炕贵,已是汗流浹背梆奈。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留称开,地道東北人亩钟。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像鳖轰,于是被迫代替她去往敵國和親径荔。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,573評論 2 353

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