作者:@武蘊牛x 授權本站轉載。
前言
前面發了一篇iOS 面試的文章,在說到 UIView 和 CALayer 的區別和聯系的時候,被喵神指出沒有切中要點,所以這裡就 CALayer 和 UIView 這個問題重新整理了下。這裡會先分條解釋,最後會在文章的結尾給出概括性總結。
1.首先UIView可以響應事件,Layer不可以.
UIKit使用UIResponder作為響應對象,來響應系統傳遞過來的事件並進行處理。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如何創建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 { [super setFrame:frame]; } - (void)setCenter:(CGPoint)center { [super setCenter:center]; } - (void)setBounds:(CGRect)bounds { [super setBounds:bounds]; }
同樣在 CustomLayer中同樣重寫這些方法。只是 setCenter方法改成setPosition方法
我在兩個類的初始化方法中都打下了斷點
首先我們會發現,我們在 [view initWithFrame] 的時候調用私有方法【UIView _createLayerWithFrame】去創建 CALayer。
然後我在創建 View 的時候,在 Layer 和 View 中Frame 相關的所有方法中都加上斷點,可以看到大致如下的調用順序如下
[UIView _createLayerWithFrame] [Layer setBounds:bounds] [UIView setFrame:Frame] [Layer setFrame:frame] [Layer setPosition:position] [Layer setBounds:bounds]
我發現在創建的過程只有調用了 Layer 的設置尺寸和位置的然而並沒有調用View 的 SetCenter 和 SetBounds 方法。
然後我發現當我修改了 view的 bounds.size 或者 bounds.origin 的時候也只會調用上邊 Layer的一些方法。所以我大膽的猜一下,View 的 Center 和 Bounds 只是直接返回layer 對應的 Position 和 Bounds.
View中frame getter方法,bounds和center,UIView並沒有做什麼工作;它只是簡單的各自調用它底層的CALayer的frame,bounds和position方法。
關於 Frame 的理解參考:http://www.cocoachina.com/industry/20131209/7498.html
3.UIView主要是對顯示內容的管理而 CALayer 主要側重顯示內容的繪制。
我在 UIView 和 CALayer 分別重寫了父類的方法。
[UIView drawRect:rect]//UIView [CALayer display]//CALayer
然後我在上面兩個方法加了斷點,可以看到如下的執行。
可以看到 UIView 是 CALayer 的CALayerDelegate,我猜測是在代理方法內部[UIView(CALayerDelegate) drawLayer:inContext]調用 UIView 的 DrawRect方法,從而繪制出了 UIView 的內容.
4.在做 iOS 動畫的時候,修改非 RootLayer的屬性(譬如位置、背景色等)會默認產生隱式動畫,而修改UIView則不會。
對於每一個 UIView 都有一個 layer,把這個 layer 且稱作RootLayer,而不是 View 的根 Layer的叫做 非 RootLayer。我們對UIView的屬性修改時時不會產生默認動畫,而對單獨 layer屬性直接修改會,這個默認動畫的時間缺省值是0.25s.
在 Core Animation 編程指南的 “How to Animate Layer-Backed Views” 中,對為什麼會這樣做出了一個解釋:
UIView 默認情況下禁止了 layer 動畫,但是在 animation block 中又重新啟用了它們
是因為任何可動畫的 layer 屬性改變時,layer 都會尋找並運行合適的 'action' 來實行這個改變。在 Core Animation 的專業術語中就把這樣的動畫統稱為動作 (action,或者 CAAction)。
layer 通過向它的 delegate 發送 actionForLayer:forKey: 消息來詢問提供一個對應屬性變化的 action。delegate 可以通過返回以下三者之一來進行響應:
它可以返回一個動作對象,這種情況下 layer 將使用這個動作。
它可以返回一個 nil, 這樣 layer 就會到其他地方繼續尋找。
它可以返回一個 NSNull 對象,告訴 layer 這裡不需要執行一個動作,搜索也會就此停止。
當 layer 在背後支持一個 view 的時候,view 就是它的 delegate;
這部分的具體內容參考:http://objccn.io/issue-12-4/
總結
總接來說就是如下幾點:
每個 UIView 內部都有一個 CALayer 在背後提供內容的繪制和顯示,並且 UIView 的尺寸樣式都由內部的 Layer 所提供。兩者都有樹狀層級結構,layer 內部有 SubLayers,View 內部有 SubViews.但是 Layer 比 View 多了個AnchorPoint
在 View顯示的時候,UIView 做為 Layer 的 CALayerDelegate,View 的顯示內容由內部的 CALayer 的 display
CALayer 是默認修改屬性支持隱式動畫的,在給 UIView 的 Layer 做動畫的時候,View 作為 Layer 的代理,Layer 通過 actionForLayer:forKey:向 View請求相應的 action(動畫行為)
layer 內部維護著三分 layer tree,分別是 presentLayer Tree(動畫樹),modeLayer Tree(模型樹), Render Tree (渲染樹),在做 iOS動畫的時候,我們修改動畫的屬性,在動畫的其實是 Layer 的 presentLayer的屬性值,而最終展示在界面上的其實是提供 View的modelLayer
兩者最明顯的區別是 View可以接受並處理事件,而 Layer 不可以
參考鏈接
http://blog.csdn.net/weiwangchao_/article/details/7771538