View Programming Guide For IOS 官方地址
View and Window Architecture(視圖與窗口的結構)
View Architecture Fundamentals(視圖的基礎結構)
當你處理視圖相關的問題時,最常接觸到的類是UIView. View 對象在屏幕上定義了一塊矩形區域.并且管理這塊區域的點擊事件. View 也可以作為其他 View 的父視圖.并且管理對應子 View 位置和大小. UIView 類做了大量的管理各個視圖的關系,你也可以自定義這些關系.
視圖與 Core Animation 的 layer 結合在一起,用于管理視圖動畫內容相關.每個UIKIt中的視圖下層都有 layer 對象 .管理下層儲存和管理視圖動畫相關.大多數情況下,你執行相關的效果只需要使用 UIView 的公開屬性就可以了.然后,如果你想得到更多的控制視圖動畫相關的能力,你可以使用視圖的layer層.
為了更好的理解視圖與layer相關,我們通過下面的例子來理解.
Core Animation 的 layer 對象的功能對底層的實現有影響.實際上繪制視圖的代碼盡可能少去調用.當代碼被調用,繪制結果將被 Core Animation 緩存下來,并且盡可能的重用.重用已經繪制好的視圖可以減少重新繪制視圖所造成的額外消耗.重用機制在動畫執行時有重要作用.
View Hierarchies and Subview Management
父視圖通過數組來保存子視圖,子視圖在數組中的順序是會影響視圖的顯示的
父視圖中有兩個子視圖,這兩個子視圖有重疊部分,那么后添加(或者是被移動到數組最后)的視圖將會顯示在上面.
父視圖子視圖的關系也會影響視圖行為,改變父視圖的大小將會導致子視圖的位置跟大小發生改變.其他改變也會影響子視圖,比如:
1、隱藏父視圖
2、改變父視圖的透明度
3、改變父視圖的坐標系統
視圖的排布決定了程序如何響應點擊事件.
當一個點擊事件發生在屏幕上時,系統將發送事件到對應的視圖上,對應視圖來處理事件.如果對應的視圖沒有處理事件的能力,那么事件將傳給對應視圖的父視圖.如果父視圖也不能處理,那么事件將傳給父視圖的父視圖.以此類推.
這是一個響應者鏈 Response Chain .
視圖也可以將事件傳給交互的實體,比如viewController.
如果沒有實體處理事件,事件最終將傳給應用實體 (Application Object).它來統一丟棄(discard)
The View Drawing Cycle
UIView類是以 “按需繪圖模式" 去展現內容.
當一個視圖第一次展現在屏幕上時,系統會要求它去繪制自己的內容.
系統將獲取View中內容的 snapshot(截屏)并用截屏 snapshot(截屏)作為視圖的可視部分的代替.
如果你從未改變視圖內容,那么視圖繪制代碼將不會被執行.
對應的 snapshot(截屏)會被重復利用.在大多數跟這個視圖相關的操作中.如果你改變了視圖的內容,你去通知系統對應的視圖發生了改變.視圖將重復繪制視圖的過程并且獲取視圖新的截屏.
當視圖的內容發生改變,你不需要直接去重新繪制視圖.
你通過使setNeedsDisplay和setNeedsDisplayInRect:方法主動去作廢之前的視圖.
這些方法告訴系統視圖的內容被修改了,在下個opportunity需要重新繪制.
系統將會在下次初始化繪制動作前和現在的runloop結束后重新繪制.
這個等待將會給你充分的時間去(invalidate)作廢多個View,或者是從View層級中移除視圖,隱藏視圖,改變視圖大小,改變視圖位置.
所有你做的改變將會同時反應出來.
到了繪制 view 的內容的時機,真正決定繪制內容是取決于 view 和 view 的屬性.
系統視圖實現了私有的繪制方法,并在頭部展示出來.
一些系統的視圖類將接口暴露出來,你通過修改相關屬性來控制視圖的顯示.
自定一個視圖UIView子類,你可以通過重寫子類中drawRect:方法,去繪制視圖.還有其他方式來提供視圖內容,比如直接設置內容的(underlying layer)下層.但是drawRect:方式是最通用實現方法.
Content Modes
每個view都有 content mode, content mode 的作用是在視圖的坐標發生改變時,如果去回收利用內容。當一個view第一次被展示時,它的內容將會被捕獲,捕獲的結果以bitmap的形式.改變坐標系時,bitmap將不會被重新繪制. content mode 屬性決定bitmap該如何被拉伸。或者僅僅只是移動位置。
視圖的 Content Mode 將被應用在下面的情況
- 當你改變 frame 或者 bounds 的高和寬時,將會應用 contentmode.
- 當修改 View 的 transform 屬性時,transform 屬性包含放大所有的因素時。
大多數view的 contentmode 是 UIViewContentModeScaleToFill
請自行查找 contentmode 各個值
StretchableViews(可拉伸區域)
你可以定義一塊區域作為可拉伸的區域,當view的大小改變時,只有在可拉伸區域的內容受到影響.
經常人們會定義一個可拉伸區域,作為試圖的一個重復的模塊
可拉伸區域可以從xy軸兩個方向拉伸
通過contentStretch屬性來申明一個可拉伸的區域,值的范圍是0~1
contentmodel在可拉伸區中如何拉伸中起到很重要的作用.可拉伸區域只有在contentmode導致視圖內容被放大或者縮小時.
這意味著只有在contentmode為以下內容時才可以被拉伸:
- UIViewContentModeScaleToFill,
- UIViewContentModeScaleAspectFit,
- UIViewContentModeScaleAspectFill
Built-In Animation Support
在每一個視圖(View)下面都有一個 layer (圖層)的好處是,我們可以很容易完成跟view相關的動畫.
想要執行一個動畫,你必須要做一下兩個步驟.
- 告訴UIKIt,將要執行一個動畫
- 改變屬性的值
改變以下UIView屬性時,將會有動畫
frame — Use this to animate position and size changes for the view.
bounds — Use this to animate changes to the size of the view.
center — Use this to animate the position of the view.
transform — Use this to rotate or scale the view.
alpha — Use this to change the transparency of the view.
background Color — Use this to change the background color of the view.
content Stretch — Use this to change how the view’s contents stretch.
Core Animation 將會告訴你如何開發動畫
視圖的幾何意義上的坐標系
在UIkit中以左上角為坐標原點,使用 floating point number
每一個view和屏幕都有自己的坐標系。
The Relationship of the Frame,Bounds,and Center Properties
視圖
默認情況下,子視圖超出父視圖時,父視圖將不會裁剪子視圖.你可以設置clipsToBounds,來改變這個效果.
不管子視圖有沒有超區父視圖,當事件傳過來時,只會在父視圖的frame中響應。
Points Versus Pixels 點與像素之間的關系
Points(點)提供一個frame(矩形區域)用來繪圖。
ios是一個以點為計量單位來定義了用戶坐標空間的系統
一個點并不是代表一個在屏幕上的像素
The Runtime Interaction Model for Views 運行時交互
當用戶點擊你的交互界面,或者你代碼修改一些object,一系列復雜的事件將要被觸發在UIKit中,來管理交互.這些事件是有序列的。在UIKit調用這個特定序列的事件中特殊時期,UIKit將會調用你的類,來讓你的類處理對應的交互事件。
下面是這個事件隊列。

用戶點擊屏幕
硬件報告事件給UIKit
UIKit打包touch事件到UIEvent事件,并將對應事件發送到對應的視圖。
-
視圖的事件處理代碼將被觸發來回應對應的事件。
- 改變視圖的屬性
- 調用setNeedsLayout方法 標記view需要 layout
- 調用setNeedsDisplay: 或者 setNeedsDisplayInRect:去標記視圖需要重新繪制
- 通知視圖控制器去改變部分數據
-
如果視圖的幾何坐標修改了,UIKit需要根據下面的規則更新它的子視圖。
- 如果你設置 autoresizing,那么UIKit將會根據你的設置來調整視圖。
- 如果你的視圖實現了layoutSubviews 方法,UIKit將會調用它。
- 你可以重寫此方法來自定義視圖。使用本方法來調整子視圖位置和大小。舉個例子,視圖提供一個大的滑動區域。
如果任何一個視圖被標記為重新繪制,UIKit將會要求視圖去重寫繪制
任何更新完之后的視圖將被復合成對應區域的內容,發給繪圖硬件繪制。
繪圖硬件將繪制完好的內容展現到屏幕上。
總結一下如何自定義一個視圖
-
事件響應方法
- -touchesBegan:withEvent:
- -touchesMoved:withEvent:
- -touchesEnded:withEvent:
- -touchesCancelled:withEvent:
layoutSubviews
drawRect
上面的方法是我們通常自定義一個視圖需要重寫的方法。但你不必要每個方法都去實現。如果你想要用手勢來處理事件,你不需要去重寫上面的所有方法。通常,如果你的視圖沒有包含子視圖或者他自己的大小沒有改變,那么就沒有重寫layoutSubview的必要了。最后,當你需要在運行時來修改視圖的和你正在使用 UIKit 和 Core Graphics 去繪圖時 你可以重寫 drawRect 方法是、
window
一個應用程序至少有一個window
window的作用
1.包含可視部分
2.發送事件給你的視圖和其他object
3.跟viewcontroller一起工作,加速旋轉屏幕的效果
跟Window相關的任務
大多數程序,只有在初始化window時才與window發生交互.然后你可以使用程序窗口來執行一些跟程序相關的任務.
- 使用window將點和矩形的塊轉換到以window坐標系的坐標系統
- 使用窗口通知去響應窗口相關改變.
Creating and Configuring a Window(創建和配置window)
我們可以通過 Interface Builder 或者代碼去創建Window.不管使用哪種方法,我們需要在啟動的時候創建Window.并且我們需要retain window. store a reference 在我們應用程序的代理中UIApplicationDelegate實例. 如果你的程序創建了一個新的Window,使用懶加載(需要的時候再加載).
你必須在創建程序的主window,在程序啟動的時候.不管是在前臺foreground,還是后臺background.當你的程序在后臺啟動的時候,你需要保證你的window不被顯示,直到你的應用程序到前臺工作.
Creating window in Interface Builder
xcode項目自動為你做好創建主屏幕的工作.每個項目中都包涵一個Main.storyBoard.除此之外,模板還創建一個outlet,對應程序的主屏幕.你通過outlet去獲取主屏幕.
通過Interface builder創建主屏幕是,要在launch option打開 Full Screen.如果你不這樣做,你的主屏幕可能比設備的屏幕小,用戶動作可能不能被你的視圖處理.因為主屏幕接收不到屏幕意外的touch 事件.因為view默認不會被主屏幕裁剪.View可以展現在屏幕上,但是有可能不處理touch事件.將launch option 中對應key 設置為全屏.使得window能適應整個屏幕.
如果你想更新使用Interface builder來創建主屏幕.現在Interface builder拖進一個屏幕window object到你的nib文件中.你還需要做到以下幾步.
- 想要在運行時獲取當前屏幕,你需要將window跟一個outlet關聯起來.通常做法是在 應用代理中或者 nib 文件對應的 File owner.
- 如果你想要升級,你需要將程序的主nib文件設置為當前的文件.你還必須在 plist中設置 NSMainNibKey的值為你的nib文件名.改變這個值時,你需要確認 nib文件已經被加載并且能被獲取到.
Creating a Window Programmatically 代碼創建屏幕
代碼創建主屏幕,你應該在程序代理的application:didFinishLaunchingWithOptions:方法中創建下面的代碼
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
Displaying Content on an External Display
想要顯示外置的內容,你需要給你的應用添加額外的window.通過screen object去獲取,并展示額外的內容.
通常新的window可以被main Screen獲取到.改變window的screen object致使你屏幕的內容發生響應的改變.只要window可以被當前screen獲取到,你就可以像在主屏幕中添加view并展示一樣做相同的操作.
UIScreen類擁有一個screen object的列表.代表著可以獲取的物理顯示.通常,只有一個screen object作為ios應用程序的主顯示,但是設備是支持額外顯示功能的.所有可以擁有其他的screen.有retina display顯示功能的才有額外顯示功能.3gs就沒有這個功能.
在應用程序構建時,注冊screen 連接和斷開通知
-
當需要顯示的時候,創建并配置一個window
- 通過UIScreen的screen屬性去持有一個screen object,做一些展示額外顯示的準備.
- 創建一個UIWindow實體并配置響應的大小位置.
- 設置UIScreen object為Window的screen屬性.
- 適配screen object,為了支持你所以要顯示的內容.
- 添加合適的view到UIWindow上.
顯示這個window