一、View和Window簡介
在iOS中,我們用window和view來呈現(xiàn)應用的內(nèi)容。事實上window本身不能呈現(xiàn)任何可視的內(nèi)容,但是它可以為view提供基本的容器功能。view相當于定義了window中的一塊用于顯示內(nèi)容的區(qū)域。例如:用來顯示圖片、文字、圖形或者以上幾者的組合的view。你也可以用view來組織和管理其他view。
每個應用都至少有一個window和一個view組成。UIKit和其他系統(tǒng)框架提供了一些預定義的view:從最簡單的button、text label到復雜的table view、picker view、scroll view。當預定義的view不能滿足你的需求的時候,你還能創(chuàng)建自定義view來管理view的繪制和事件處理。
view是UIView類(或其子類)的實例,它管理應用的window中的一塊矩形區(qū)域。view負責繪制內(nèi)容、處理事件、管理subview的布局。繪制內(nèi)容涉及到使用Core Graphics、OpenGL ES或者UIKit來繪制形狀、圖片和文字。view同時還負責處理對其矩形區(qū)域內(nèi)發(fā)生的觸摸或者手勢事件。在view的層級結(jié)構(gòu)(hierarchy)中,parent view負責他們的child view的布局并且可以動態(tài)的隨時調(diào)整布局,這在界面旋轉(zhuǎn)或者實現(xiàn)界面動畫的情況下尤其有用。
window是UIWindow類的實例并且用于處理應用總的界面呈現(xiàn)。一般來說應用的window基本不會變化:當一個window被創(chuàng)建后,它就待在那兒,只有window里的view會經(jīng)常發(fā)生變化?;旧弦粋€應用就一個window,如果設備接入了外部顯示,那么應用會創(chuàng)建第二個window用于在外部顯示上呈現(xiàn)內(nèi)容。
動畫用來為用戶提供內(nèi)容變化的視覺反饋。系統(tǒng)定義了在模態(tài)化view和不同view之間切換的標準動畫。當然view的很多屬性也可以使用動畫效果,例如:view的透明度、位置、背景色等。如果你直接使用view底層的Core Animation Layer,你還能做出更多炫酷的動畫效果。
Interface Builder是一個可視化編輯window和view的工具。使用IB,你的view都存放在一個nib(xib)文件中。你可以把nib文件想像成是存儲view和其他對象的序列化資源文件。當你在運行時load一個nib文件的時候,里面的對象會重新組成到內(nèi)存中并且可以被代碼操作。
二、View和Window的結(jié)構(gòu)
1. View結(jié)構(gòu)基礎
view和Core Animation Layer配合來處理view的繪制和動畫效果。每個view的背后都有一個layer對象(通常是CALayer類的實例)負責這個view的繪制和動畫效果。大多數(shù)情況下你只需要和UIView的接口打交道,只有在某些特定的情況下你才需要操作layer對象。
為了幫助理解view和layer之間的關系,可以參考圖1-1。在圖1-1中我們可以看到UIBarButtonItem是沒有l(wèi)ayer的,因為它是由UIToolbar繪制出來的,并非一個獨立的view。
2. View的層級結(jié)構(gòu)和Subview管理
當一個view包含另一個view的時候,兩個view之間就有了parent-child關系。child view就叫subview,parent view就叫superview。
視覺上,subview的內(nèi)容遮蓋superview全部或部分的內(nèi)容空間。如果subview是不透明的,那他就徹底遮蓋了superview對應的部分。如果subview是部分透明的,那這兩個view的內(nèi)容就會混合在一起顯示(想象成高斯模糊的那種效果)。每個superview將它的subview存儲在一個排序過的數(shù)組里。越晚添加的或者順序越大的顯示在越上面。
superview - subview的這種關系也會影響到一些view的行為。例如:改變superview的大小也會使subview的大小和位置發(fā)生改變。還包括改變superview的透明度、改變superview的坐標系統(tǒng)。
view的層級關系還影響到事件的傳遞。當某個view中發(fā)生了觸摸事件,如果當前的這個view無法處理,事件會順著view的層級向上傳遞。特定的view還會把事件傳遞給responder對象,例如一個view controller。如果事件最終傳遞到最頂級的application對象,通常這個事件就被丟棄了。
3. View的繪制周期
UIView使用按需繪制的模式來呈現(xiàn)內(nèi)容。當一個view首次呈現(xiàn)在屏幕上時,系統(tǒng)會要求它繪制一下它的內(nèi)容。然后系統(tǒng)會捕捉一張快照,并且一直使用它。如果你不改變view的內(nèi)容,那么view的繪制代碼就不會被再次執(zhí)行。當你改變了view的顯示內(nèi)容,你應該使用view的setNeedsDisplay或setNeedsDisplayInRect方法來要求重繪。如果你是自定義UIView的子類,那么通常你應該重載drawRect方法來繪制你的view。你也有其他方法來繪制view,例如直接在layer上繪制,但通常你應該重載drawRect來實現(xiàn)自定義view的繪制。
4. View的Content Mode
每個view有一個content mode,當view的幾何形狀發(fā)生變化時,它用來控制view的內(nèi)容是否需要重繪或者將如何改變。
當你進行如下操作時,content mode將會起作用:
- 改變view的frame或者bounds的寬高
- view的transform屬性含有scale factor
默認的content mode是UIViewContentModeScaleToFill。它會使view的內(nèi)容縮放以適應新的frame尺寸。圖1-2顯示了各種content mode下的內(nèi)容縮放情況。
content mode有助于避免不必要的內(nèi)容重繪,當然你也可以設置content mode為UIViewContentModeRedraw來強制view在每次縮放或尺寸改變時重繪(調(diào)用drawRect方法),但是通常你應當避免這么做。
5. 可拉伸的View
你可以指定view的某塊區(qū)域為可拉伸,這樣當view的尺寸改變的時候,只有可拉伸的區(qū)域會受到影響。一般會在按鈕上使用這種技術(shù)。圖1-3顯示了可拉伸時,view是如何變化的。
使用view的content stretch屬性來指定可拉伸的區(qū)域。這個屬性接受一個rectangle值來表示可拉伸區(qū)域,并且這個值會歸一化到0.0 - 1.0。這樣當拉伸發(fā)生時,系統(tǒng)可以用view的當前bounds以及scale factor乘以這個歸一化的值來決定如何縮放。另外view的content mode也會影響拉伸。只有UIViewContentModeScaleToFill、UIVieweContentModeScaleAspectFit和UIViewContentModeScaleAspectFill這幾個content mode才會出發(fā)拉伸。
推薦在設置view的背景時使用一個可拉伸的UIImage對象來實現(xiàn)拉伸
6. 內(nèi)置動畫支持
UIView類的一些屬性是支持半自動動畫的。你只需要:
- 告訴UIKit你要使用動畫了!
- 改變這些屬性的值
這些屬性有:
- frame —— 位置和尺寸的動畫
- bounds —— 尺寸動畫
- center —— 位置動畫
- transform —— 旋轉(zhuǎn)和縮放動畫
- alpha —— 改變透明度
- backgroundColor —— 改變背景色
- cententStretch —— 改變view如何拉伸
一般來說,viewcontroller之間過度時會使用動畫。當然你也可以直接在兩個view之間使用動畫。另外,除了使用UIKit類來實現(xiàn)動畫,你還能使用Core Animation Layer來實現(xiàn)動畫,它能提供更多的時間和屬性上的控制。
下篇 View編程指南(二)