轉(zhuǎn)載地址:http://blog.csdn.net/oqqquzi1234567/article/details/9004399
一.視圖的運行時交互模型
當用戶和界面進行交互時,或者由代碼程序性的改變一些東西時,一系列復(fù)雜的事件就會發(fā)生在UIKit的內(nèi)部來處理這些交互。在這個系列中的某些點,UIKit喚出你的視圖類,同時給它們一個機會去響應(yīng)程序的行為。理解這些喚出點對于理解視圖在哪里融入系統(tǒng)很重要。圖 1-7 展示了這些事件的基本序列,從用戶觸屏開始到圖形系統(tǒng)更新屏幕內(nèi)容來響應(yīng)結(jié)束。同樣的事件序列也會發(fā)生在任何程序性啟動的動作。
以下的步驟分解了圖1-7中的事件序列,既解釋了在每一步發(fā)生了什么,也解釋了應(yīng)用如何響應(yīng):
(1)用戶觸屏
(2)硬件報告觸摸事件給UIKit框架
(3) UIKit框架將觸摸事件打包成UIEvent對象,同時分發(fā)給適合的視圖。(對于UIKit框架如何提交事件給視圖的詳細解釋,查看Event
HandingGuide for iOS)
(4)視圖中的事件處理代碼可能進行以下的動作來響應(yīng):
改變視圖或者其子視圖的屬性(frame,bounds,alpha,等等)
;調(diào)用setNeedsLayout方法以標記該視圖(或者它的子視圖)為需要進行布局更新;調(diào)用setNeedsDisplay或者setNeedsDisplayInRect:方法以標記該視圖(或者它的子視圖)需要進行重畫;通知一個控制器關(guān)于一些數(shù)據(jù)的更新.當然,哪些事情要做,哪些方法要被調(diào)用是由視圖來決定的。
(5)如果一個視圖的幾何結(jié)構(gòu)改變了,UIKit會根據(jù)以下幾條規(guī)則來更新它的子視圖:
[a].如果自動重設(shè)尺寸的規(guī)則在發(fā)生作用,UIKit會根據(jù)這些規(guī)則來調(diào)整視圖。獲取更多關(guān)于自動重設(shè)尺寸規(guī)則如何工作,查看"HandlingLayout
Changes Automatically Using Autoresizing Rules."
[b].如果視圖實現(xiàn)了layoutSubviews方法,UIKit會調(diào)用它。你可以在你的定制視圖中覆蓋這個方法同時用它來調(diào)整任何子視圖的位置和大小。例如,一個提供了巨大滾動區(qū)域的視圖會需要使用幾個子視圖作為“瓦塊”而不是創(chuàng)建一個不太可能放進內(nèi)存的巨大視圖。在這個方法的實現(xiàn)中,視圖會隱藏任何屏幕外的子視圖,或者重定位它們?nèi)缓笥脕砝L制新的可視內(nèi)容。作為這個流程的一部分,視圖的布局代碼也可以廢止任何需要被重畫的視圖。
(6)如果任何視圖的任何部分被標記為需要重畫,UIKit會要求視圖重畫自身。
對于顯式的定義了drawRect:方法的定制視圖,UIKit會調(diào)用這個方法。這方法的實現(xiàn)應(yīng)該盡快重畫視圖的指定區(qū)域,并且不應(yīng)該再做其他事。不要在這個點上做額外的布局,也不要改變應(yīng)用的數(shù)據(jù)模型。提供這個方法僅僅是為了更新視圖的可視內(nèi)容。
標準的系統(tǒng)視圖通常不會實現(xiàn)drawRect:方法,但是也會在這個時候管理它們的繪制。
(7)任何已經(jīng)更新的視圖會與應(yīng)用余下的可視內(nèi)容組合在一起,同時被發(fā)送到圖形硬件去顯示。
(8)圖形硬件將已解釋內(nèi)容轉(zhuǎn)化到屏幕上。
注意:上面的更新模型主要應(yīng)用于使用標準系統(tǒng)視圖和繪制技術(shù)的應(yīng)用。使用OpenGLES來繪制的應(yīng)用通常會配置一個單一的全屏視圖和直接繪制相關(guān)的OpenGL圖像上下文。你的視圖還是應(yīng)該處理觸屏事件,但是它是全屏的,毋需給子視圖布局或者實現(xiàn)drawRect:方法。獲取更多關(guān)于使用OpenGL
ES的信息,查看OpenGL ES Programming Guide for iOS.
二 .根據(jù)以上步驟,可將自己定制的視圖整合進去的方法:
1)事件處理方法:
(1)touchesBegan:withEvent:
(2)touchesMoved:withEvent:
(3)touchesEnded:withEvent:
(4)touchesCancelled:withEvent:
2)layoutSubviews方法
3)drawRect:方法
這些是視圖的最常用的覆蓋方法,但是你可能不需要覆蓋全部。如果你使用手勢識別來處理事件,你不需要覆蓋事件處理方法。相似的,如果你的視圖沒有包含子視圖或者它的尺寸不會改變,那就沒有理由去覆蓋layoutSubviews方法。最后,只有當視圖內(nèi)容會在運行時改變,同時你要用UIKit或者Core
Graphics等本地技術(shù)來繪制時才需要用到drawRect。
要記住這些是主要的整合點,但是不僅僅只有這些。UIView類中有些方法是專門設(shè)計來給子類覆蓋的。你應(yīng)該到UIView ClassReference中查看這些方法的描述,以便在定制時清楚哪些方法適合給你覆蓋。
三.ioslayout機制相關(guān)方法
1.-(CGSize)sizeThatFits:(CGSize)size
- (void)sizeToFit
-------------------刷新子對象布局------------------
- (void)layoutSubviews
- (void)layoutIfNeeded
- (void)setNeedsLayout
-------------------重繪--------------------------------
- (void)setNeedsDisplay
- (void)drawRect
2.方法解釋:
1)sizeToFit會自動調(diào)用sizeThatFits方法;
2)sizeToFit不應(yīng)該在子類中被重寫,應(yīng)該重寫sizeThatFits;
3)sizeThatFits傳入的參數(shù)是receiver當前的size,返回一個適合的size;
4)sizeToFit可以被手動直接調(diào)用;
5)sizeToFit和sizeThatFits方法都沒有遞歸,對subviews也不負責,只負責自己。
-----------------------------------
1)-layoutSubviews方法:這個方法,默認沒有做任何事情,需要子類進行重寫。
2)-setNeedsLayout方法:
標記為需要重新布局,異步調(diào)用layoutIfNeeded刷新布局,不立即刷新,但layoutSubviews一定會被調(diào)用。
setNeedsLayout在receiver標上一個需要被重新布局的標記,在系統(tǒng)runloop的下一個周期自動調(diào)用
layoutSubviews。
3)-layoutIfNeeded方
法:layoutIfNeeded方法如其名,UIKit會判斷該receiver是否需要layout.,根據(jù)Apple官方文
檔,layoutIfNeeded方法應(yīng)該是這樣的:?layoutIfNeeded遍歷的不是superview鏈,應(yīng)該是subviews鏈。如果有
需要刷新的標記,立即調(diào)用layoutSubviews進行布局(如果沒有標記,不會調(diào)用layoutSubviews)。如果要立即刷新,要先調(diào)用
[view?setNeedsLayout],把標記設(shè)為需要布局,然后馬上調(diào)用[view?layoutIfNeeded],實現(xiàn)布局。在視圖第一次顯
示之前,標記總是“需要刷新”的,可以直接調(diào)用[view
layoutIfNeeded]。
-------------------------------------
1)-drawRect:(CGRect)rect方法:重寫此方法,執(zhí)行重繪任務(wù)。
2)-setNeedsDisplay方法:標記為需
要重繪,異步調(diào)用drawRect。setNeedDisplay在receiver標上一個需要被重新繪圖的標記,在下一個draw周期自動重
繪,iphone device的刷新頻率是60hz,也就是1/60秒后重繪。
3)-setNeedsDisplayInRect:(CGRect)invalidRect方法:標記為需要局部重繪。
四 .layoutSubviews總結(jié)
1.layoutSubviews在以下情況下會被調(diào)用:
1)、init初始化不會觸發(fā)layoutSubviews
但是是用initWithFrame?進行初始化時,當rect的值不為CGRectZero時,也會觸發(fā)
2)、addSubview會觸發(fā)layoutSubviews
3)、設(shè)置view的Frame會觸發(fā)layoutSubviews,當然前提是frame的值設(shè)置前后發(fā)生了變化
4)、滾動一個UIScrollView會觸發(fā)layoutSubviews
5)、旋轉(zhuǎn)Screen會觸發(fā)父UIView上的layoutSubviews事件
6)、改變一個UIView大小的時候也會觸發(fā)父UIView上的layoutSubviews事件
7) 、直接調(diào)用setLayoutSubviews。
2.在蘋果的官方文檔中強調(diào):
You should override this method only if the autoresizingbehaviors of the subviews do not offer(提供) the behavior
you want.
3.layoutSubviews, 當我們在某個類的內(nèi)部調(diào)整子視圖位置時,需要調(diào)用。
反過來的意思就是說:如果你想要在外部設(shè)置subviews的位置,就不要重寫。
五.drawRect總結(jié)
1.drawRect在以下情況下會被調(diào)用:
1)、如果在
UIView初始化時沒有設(shè)置rect大小,將直接導(dǎo)致drawRect不被自動調(diào)用。drawRect調(diào)用是在
Controller->loadView,?Controller->viewDidLoad?兩方法之后掉用的.所以不用擔心在控制器
中,這些View的drawRect就開始畫了.這樣可以在控制器中設(shè)置一些值給View(如果這些View在?draw的時候需要用到某些變量值).
2)、該方法在調(diào)用sizeToFit后被調(diào)用,所以可以先調(diào)用sizeToFit計算出size。然后系統(tǒng)自動調(diào)用drawRect:方法。
3)、通過設(shè)置contentMode屬性值為UIViewContentModeRedraw。那么將在每次設(shè)置或更改frame的時候自動調(diào)用drawRect:。
4)、直接調(diào)用setNeedsDisplay,或者setNeedsDisplayInRect:觸發(fā)drawRect:,但是有個前提條件是rect不能為0。
以上1,2推薦;而3,4不提倡
2.drawRect方法使用注意點:
1)、若使用UIView繪圖,只能在drawRect:方法中獲取相應(yīng)的contextRef并繪圖。如果在其他方法中獲取將獲取到一個invalidate(無效)的ref并且不能用于畫圖。drawRect:方法不能手動顯示調(diào)用,必須通過調(diào)用setNeedsDisplay?或者?setNeedsDisplayInRect,讓系統(tǒng)自動調(diào)該方法。
2)、若使用calayer繪圖,只能在drawInContext:?中(類似于drawRect)繪制,或者在delegate中的相應(yīng)方法繪制。同樣也是調(diào)用setNeedDisplay等間接調(diào)用以上方法
3)、若要實時畫圖,不能使用gestureRecognizer,只能使用touchbegan等方法來掉用setNeedsDisplay實時刷新屏幕.
六.layoutSubviews /setNeedsLayOut,drawRect/setNeedsDisplay
1.layoutSubviews方法,此方法,從字面上就很好解釋“布局子視圖”。根據(jù)蘋果官方幫助文檔對layoutSubviews方法的解釋:此方法用來重新定義子元素的位置和大小;當子類重寫此方法,用來實現(xiàn)UI元素的更精確布局;如果要讓布局重新刷新,那么就調(diào)用setNeedsLayout,即setNeedsLayout方法會默認用layoutSubViews方法。
2.layoutSubviews對subviews重新布局;drawRect方法主要用來畫圖,drawRect是對receiver的重繪,能獲得context;layoutSubviews方法調(diào)用先于drawRect。
3.UIView的setNeedsDisplay和setNeedsLayout方法。首先兩個方法都是異步執(zhí)行的。setNeedsDisplay方法的作用是,告知需要重新顯示。這樣,界面就會重新被繪制,setNeedsDisplay
會自動調(diào)用drawRect方法來實現(xiàn)view的繪制,這樣可以拿到UIGraphicsGetCurrentContext,就可以畫畫了;而
setNeedsLayout會默認調(diào)用layoutSubViews來實現(xiàn)view中subView的重新布局,可以處理子視圖中的一些數(shù)據(jù)。
4.所以,當需要刷新布局時,用setNeedsLayOut方法;當需要重新繪畫時,調(diào)用setNeedsDisplay方法。
七.需要知道的UIview的一些事情
1、在Mac OS中NSWindow的父類是NSResponder,而在i OS 中UIWindow 的父類是UIVIew。程序一般只有一個窗口但是會又很多視圖。
2、UIView的作用:描畫和動畫,視圖負責對其所屬的矩形區(qū)域描畫、布局和子視圖管理、事件處理、可以接收觸摸事件、事件信息的載體等等。
3、UIViewController 負責創(chuàng)建其管理的視圖及在低內(nèi)存的時候?qū)⑺麄儚膬?nèi)存中移除。還為標準的系統(tǒng)行為進行響應(yīng)。
八.答疑
1.為什么不直接調(diào)用drawRect和layoutSubView,而要通過兩個set方法,這樣不是更麻煩嗎,何必多次一舉呢?
假
設(shè)我們采用的是直接調(diào)用drawRect的機制,先考慮這樣一個問題
,現(xiàn)在有兩個UIViewControllerA和B,A為當前view的viewController,如果此時在A中調(diào)用[B.view
drawRect],這樣B的view無論如何都會調(diào)用drawRect的方法重新繪制一遍,這樣問題就出來了,有必要嗎,畢竟現(xiàn)在顯示的是A中的
view!B重新繪制一遍就調(diào)用了drawRect中的方法,完全是在浪費系統(tǒng)資源啊,而通過setNeedsDisplay,ios就會很聰明的判斷出
不需要調(diào)用drawRect的方法,這樣就避免了資源的重復(fù)調(diào)用!(說白了,也就是ios底層的一種封裝優(yōu)化)
同理,setNeedsLayout也采用了同樣的機制來避免資源的重復(fù)利用!
由此我們可以推斷出這中設(shè)計的精妙之處,ios由很多精妙的設(shè)計來緩解手機資源不足的現(xiàn)狀,比如cell的重復(fù)利用等等。
九.官方文檔翻譯:
1.有效使用視圖的提示
當你需要繪制一些標準系統(tǒng)視圖不能提供的內(nèi)容時,定制視圖是很有用的。但是你要負責保證視圖的性能要足夠的高。UIKit會盡可能的優(yōu)化視圖相關(guān)的行為,也會幫助你提高性能。然而,考慮一些提示可以幫助到UIKit。
重要:在調(diào)整繪制代碼之前,你應(yīng)該一直收集與你視圖當前性能有關(guān)的數(shù)據(jù)。估量當前性能讓你可以確定是否真的有問題,同時如果真的有問題,它也提供一個基線,讓你在未來的優(yōu)化中可以比較。
2.視圖不會總是有一個相應(yīng)的視圖控制器
在應(yīng)用中,視圖和視圖控制器之間的一對一關(guān)系是很少見的。視圖控制器的工作是管理一個視圖層次,而視圖層次經(jīng)常是包含了多個視圖,它們都有自包含特性。對于iPhone應(yīng)用,每個視圖層次通常都填滿了整個屏幕,盡管對于iPad應(yīng)用來說不是。
當你設(shè)計用戶界面的時候,考慮到視圖控制器的所扮演的角色是很重要的。視圖控制器提供了很多重要的行為,像協(xié)調(diào)視圖的展示,協(xié)調(diào)視圖的剔除,釋放內(nèi)存以響應(yīng)低內(nèi)存警告,還有翻轉(zhuǎn)視圖以響應(yīng)界面的方向變更。逃避這些行為會導(dǎo)致應(yīng)用發(fā)生錯誤。
獲取更多關(guān)于視圖控制器的信息,查看View Controller Programming Guide for iOS
3.最小化定制的繪畫
盡管定制的繪畫有時是需要的,但是你也應(yīng)該盡量避免它。真正需要定制繪畫的時候是已有的視圖類無法提供足夠的表現(xiàn)和能力時。任何時候你的內(nèi)容都應(yīng)該可以被組裝到其他視圖,最好結(jié)果時組合那些視圖對象到定制的視圖層次
4.利用內(nèi)容模式
內(nèi)容模式可以最小化重畫視圖要花費的時間。默認的,視圖使用UIViewContentModeScaleToFill內(nèi)容模式,這個模式會放縮視圖的已有內(nèi)容來填充視圖的frame矩形。需要時你可以改變這個模式來調(diào)整你的內(nèi)容,但是應(yīng)該避免使用UIViewContentModeRedraw內(nèi)容模式。不管哪個內(nèi)容模式發(fā)生作用,你都可以調(diào)用setNeedsDisplay或者setNeedsDisplayInRect:方法來強制視圖重畫它的內(nèi)容。
5.可能的話將視圖聲明為不透明
UIKit使用opaque屬性來決定它是否可以優(yōu)化組合操作。將一個定制視圖的這個屬性設(shè)置為YES會告訴UIKit不需要解釋任何在該視圖后的內(nèi)容。這樣可以為你的繪制代碼提高性能并且是推薦的。當然,如果你將這個屬性設(shè)置為YES,你的視圖一定要用不透明的內(nèi)容完全填充它的bounds矩形。
6.滾動時調(diào)整視圖的繪制行為
滾
動會導(dǎo)致數(shù)個視圖在短時間內(nèi)更新。如果視圖的繪制代碼沒有被適當?shù)恼{(diào)整,滾動的性能會非常的緩慢。相對于總是保證視圖內(nèi)容的平庸,我們更傾向于考慮滾動操
作開始時改變視圖行為。例如,你可以暫時減少已解釋的內(nèi)容,或者在滾動的時候改變內(nèi)容模式。當滾動停止時,你可以將視圖返回到前一狀態(tài),同時需要時更新內(nèi)
容。
7.不要嵌入子視圖來定制控制
盡管在技術(shù)上增加子視圖到標準系統(tǒng)控制對象-繼承自UIControl的類-是可行的,你還是永遠不應(yīng)該用這種方法來定制它們。控制對象支持定制,它們有顯式并且良好歸檔的接口。例如,UIButton類包含了設(shè)置標題和背景圖片的方法。使用已定義好的定制點意味著你的代碼總是會正確的工作。不用這些方法,而嵌入一個定制的圖像視圖或者標簽到按鈕中去會導(dǎo)致應(yīng)用出現(xiàn)未預(yù)期的結(jié)果。
以上內(nèi)容為官方文檔翻譯(來源于網(wǎng)絡(luò)),如果你英文還可以,請看英文地址:英文地址