UIView的layout(布局)與draw(繪圖)

轉(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ò)),如果你英文還可以,請看英文地址:英文地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內(nèi)容