前言
前面發了一篇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,和 tra
nsform 共同決定的,而一個 View 的 frame 只是簡單的返回 Layer的 frame,同樣 View 的 center和 bounds 也是返回 Layer 的一些屬性。(PS:center有些特列)為了證明這些,我做了如下的測試。
首先我自定義了兩個類CustomView,CustomLayer分別繼承 UIView 和 CALayer
在 CustomView 中重寫了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
+ (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 相關的所有方法中都加上斷點,可以看到大致如下的呼叫順序如下
1 2 3 4 5 6 |
[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 分別重寫了父類的方法。
1 2 3 |
[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