iOS視圖布局過程淺析

1. 相關(guān)API

基于觸發(fā)約束的布局 Triggering Constraint-Based Layout

-- (BOOL)needsUpdateConstraints
constraint-based layout system使用此返回值去決定是否需要 調(diào)用updateConstraints作為正常布局過程的一部分翁潘。

- (void)setNeedsUpdateConstraints
當(dāng)一個(gè)自定義view的某個(gè)屬性發(fā)生改變隔缀,并且可能影響到constraint時(shí)隧土,需要調(diào)用此方法去標(biāo)記constraints需要在未來的某個(gè)點(diǎn)更新岖免,系統(tǒng)然后調(diào)用updateConstraints.

- (void)updateConstraints
更新約束取刃,自定義view應(yīng)該重寫此方法在其中建立constraints. 注意:要在實(shí)現(xiàn)在最后調(diào)用 [super updateConstraints]

- (void)updateConstraintsIfNeeded
立即觸發(fā)約束更新饥努,自動(dòng)更新布局矛缨。

鋪設(shè)子視圖 Laying out Subviews

-(void)layoutSubviews 如果你需要更精確控制子view而昨,而不是使用限制或autoresizing行為帆焕,就需要實(shí)現(xiàn)該方法惭婿。

-(void)layoutIfNeeded 使用此方法強(qiáng)制立即進(jìn)行l(wèi)ayout,從當(dāng)前view開始,此方法會(huì)遍歷整個(gè)view層次(包括superviews)請(qǐng)求layout叶雹。因此财饥,調(diào)用此方法會(huì)強(qiáng)制整個(gè)view層次布局。

- (void)setNeedsLayout
此方法會(huì)將view當(dāng)前的layout設(shè)置為無效的折晦,并在下一個(gè)upadte cycle里去觸發(fā)layout更新钥星。

繪畫和更新視圖

-(void)drawRect:(CGRect)rect 如果你的View畫自定義的內(nèi)容,就要實(shí)現(xiàn)該方法满着,否則避免覆蓋該方法谦炒。

-void)setNeedsDisplay 標(biāo)記整個(gè)視圖的邊界矩形需要重繪

-(void)setNeedsDisplayInRect:(CGRect)invalidRect 標(biāo)記在指定區(qū)域內(nèi)的視圖的邊界需要重繪

相關(guān)聯(lián)的改變 Observing View-Related Changes

-(void)didAddSubview:(UIView *)subview
通知視圖指定子視圖已經(jīng)添加

-(void)willRemoveSubview:(UIView *)subview
通知視圖將要移除指定的子視圖

-(void)willMoveToSuperview:(UIView *)newSuperview
通知視圖將要移動(dòng)到一個(gè)新的父視圖中

-(void)didMoveToSuperview 通知視圖已經(jīng)移動(dòng)到一個(gè)新的父視圖中

-(void)willMoveToWindow:(UIWindow *)newWindow
通知視圖將要移動(dòng)到一個(gè)新的window中

-(void)didMoveToWindow 通知視圖已經(jīng)移動(dòng)到一個(gè)新的window中

配置自動(dòng)調(diào)整大小狀態(tài) Configuring the Resizing Behavior

-(void)sizeToFit 根據(jù)子視圖的大小位置,調(diào)整視圖风喇,使其恰好圍繞子視圖宁改, 也就是說自動(dòng)適應(yīng)子視圖的大小,只顯示子視圖

-(CGSize)sizeThatFits:(CGSize)size
讓視圖計(jì)算最適合子視圖的大小魂莫,即能把全部子視圖顯示出來所需要的最小的size

2. Auto Layout Process 自動(dòng)布局過程

布局過程

布局過程與使用springs and struts(autoresizingMask)比較还蹲,Auto layout在view顯示之前,多引入了兩個(gè)步驟:updating constraintslaying out views。每一個(gè)步驟都依賴于上一個(gè)谜喊。display依賴layout潭兽,而layout依賴updating constraintsupdating constraints->layout->display

第一步:updating constraints斗遏,被稱為測(cè)量階段山卦,其從下向上(from subview to super view),為下一步layout準(zhǔn)備信息∽钜祝可以通過調(diào)用方法setNeedUpdateConstraints去觸發(fā)此步怒坯。constraints的改變也會(huì)自動(dòng)的觸發(fā)此步。但是藻懒,當(dāng)你自定義view的時(shí)候,如果一些改變可能會(huì)影響到布局的時(shí)候视译,通常需要自己去通知Auto layout嬉荆,updateConstraintsIfNeeded。自定義view的話酷含,通潮稍纾可以重寫updateConstraints方法,在其中可以添加view需要的局部的contraints椅亚。

第二步:layout限番,其從上向下(from super view to subview),此步主要應(yīng)用上一步的信息去設(shè)置view的center和bounds呀舔∶峙埃可以通過調(diào)用setNeedsLayout去觸發(fā)此步驟,此方法不會(huì)立即應(yīng)用layout媚赖。如果想要系統(tǒng)立即的更新layout霜瘪,可以調(diào)用layoutIfNeeded。另外惧磺,自定義view可以重寫方法layoutSubViews來在layout的工程中得到更多的定制化效果颖对。

第三步:display,此步時(shí)把view渲染到屏幕上磨隘,它與你是否使用Auto layout無關(guān)缤底,其操作是從上向下(from super view to subview),通過調(diào)用setNeedsDisplay觸發(fā)番捂,setNeedsDisplay觸發(fā)系統(tǒng)會(huì)調(diào)用UIView 的 drawRect方法个唧。因?yàn)槊恳徊蕉家蕾嚽耙徊剑虼艘粋€(gè)display可能會(huì)觸發(fā)layout白嘁,當(dāng)有任何layout沒有被處理的時(shí)候坑鱼,同理,layout可能會(huì)觸發(fā)updating constraints,當(dāng)constraint system更新改變的時(shí)候鲁沥。

需要注意的是呼股,這三步不是單向的,constraint-based layout是一個(gè)迭代的過程画恰,layout過程中彭谁,可能去改變constraints,又一次觸發(fā)updating constraints允扇,進(jìn)行一輪layout過程缠局。

下面說說UIViewController的布局過程

VC的生命周期的部分過程viewDidLoad -> viewWillAppear -> updateViewConstraints -> viewWillLayoutSubviews -> viewDidLayoutSubviews -> viewDidAppear -> viewWillDisAppear -> updateViewConstraints -> viewDidDisAppear

對(duì)應(yīng)updateConstraints -> layoutSubViews -> drawRect

當(dāng)view修改約束(addConstraint, removeConstraint)會(huì)觸發(fā)setNeedsUpdateConstraints考润,而這個(gè)在layoutSubViews之前會(huì)觸發(fā)updateConstraints狭园,完成之后會(huì)調(diào)用layoutSubViews。
UIViewController在有個(gè)updateViewConstraints 方法糊治,這個(gè)方法實(shí)際是self.view 被設(shè)置了setNeedsUpdateConstraints(第一次展示的時(shí)候)唱矛,必然會(huì)調(diào)用這個(gè)方法(與上面的解釋保持一致了,第一次可以理解為為self.view增加了各種約束)井辜。而這個(gè)方法的默認(rèn)實(shí)現(xiàn)是調(diào)用子view的updateConstraints方法绎谦,這樣就自上而下的完成了布局。

此處需要注意的地方:

  • 不要忘記調(diào)用父類的方法粥脚,避免有時(shí)候出現(xiàn)一些莫名的問題窃肠。
  • 在view的layoutSubViews或者ViewController的viewDidLayoutSubviews方法里后可以拿到view的實(shí)際frame,所以當(dāng)我們真的需要frame的時(shí)候需要在這個(gè)時(shí)間點(diǎn)以后才能拿到刷允。
  • 下面我們可以解釋是為什么viewDidLoad里通過setFrame的方式改過原先在storyboard里拖動(dòng)的約束代碼無效了冤留。因?yàn)?code>updateViewConstraints在viewDidLoad后執(zhí)行,會(huì)覆蓋掉之前的設(shè)置的frame恃锉,所以無效搀菩。

3. 補(bǔ)充總結(jié)

以下情況下會(huì)調(diào)用layoutSubviews

  • init初始化不會(huì)觸發(fā)layoutSubviews,但是是用initWithFrame 進(jìn)行初始化時(shí)破托,當(dāng)rect的值不為CGRectZero時(shí),也會(huì)觸發(fā)肪跋。
  • addSubview會(huì)觸發(fā)layoutSubviews
  • 設(shè)置view的Frame會(huì)觸發(fā)layoutSubviews,當(dāng)然前提是frame的值設(shè)置前后發(fā)生了變化
  • 滾動(dòng)一個(gè)UIScrollView會(huì)觸發(fā)layoutSubviews
  • 旋轉(zhuǎn)Screen會(huì)觸發(fā)父UIView上的layoutSubviews事件
  • 改變一個(gè)UIView大小的時(shí)候也會(huì)觸發(fā)父UIView上的layoutSubviews事件

刷新子對(duì)象布局

-layoutSubviews方法:這個(gè)方法土砂,默認(rèn)沒有做任何事情州既,需要子類進(jìn)行重寫
-setNeedsLayout方法: 標(biāo)記為需要重新布局,異步調(diào)用layoutIfNeeded刷新布局萝映,不立即刷新吴叶,但layoutSubviews一定會(huì)被調(diào)用
-layoutIfNeeded方法:如果,有需要刷新的標(biāo)記序臂,立即調(diào)用layoutSubviews進(jìn)行布局(如果沒有標(biāo)記蚌卤,不會(huì)調(diào)用layoutSubviews)如果要立即刷新实束,要先調(diào)用[view setNeedsLayout],把標(biāo)記設(shè)為需要布局逊彭,然后馬上調(diào)用[view layoutIfNeeded]咸灿,實(shí)現(xiàn)布局在視圖第一次顯示之前,標(biāo)記總是“需要刷新”的侮叮,可以直接調(diào)用[view layoutIfNeeded]

重繪

-drawRect:(CGRect)rect方法:重寫此方法避矢,執(zhí)行重繪任務(wù)

-setNeedsDisplay方法:標(biāo)記為需要重繪,異步調(diào)用drawRect

-setNeedsDisplayInRect:(CGRect)invalidRect方法:標(biāo)記為需要局部重繪

sizeToFit會(huì)自動(dòng)調(diào)用sizeThatFits方法囊榜;

sizeToFit不應(yīng)該在子類中被重寫审胸,應(yīng)該重寫sizeThatFits

sizeThatFits傳入的參數(shù)是receiver當(dāng)前的size,返回一個(gè)適合的sizesizeToFit可以被手動(dòng)直接調(diào)用sizeToFitsizeThatFits方法都沒有遞歸卸勺,對(duì)subviews也不負(fù)責(zé)砂沛,只負(fù)責(zé)自己

layoutSubviews對(duì)subviews重新布局

layoutSubviews方法調(diào)用先于drawRectsetNeedsLayout在receiver標(biāo)上一個(gè)需要被重新布局的標(biāo)記,在系統(tǒng)runloop的下一個(gè)周期自動(dòng)調(diào)用layoutSubviews

layoutIfNeeded方法如其名曙求,UIKit會(huì)判斷該receiver是否需要layout尺上,根據(jù)Apple官方文檔,layoutIfNeeded方法應(yīng)該是這樣的layoutIfNeeded遍歷的不是superview鏈,應(yīng)該是subviews鏈drawRect是對(duì)receiver的重繪圆到,能獲得context。

總之卑吭,理解view布局的過程芽淡,可以幫助你理解View顯示的相關(guān)問題,解決一些界面問題豆赏,合理使用以上方法對(duì)你自定義控件也有很大的幫助挣菲。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市掷邦,隨后出現(xiàn)的幾起案子白胀,更是在濱河造成了極大的恐慌,老刑警劉巖抚岗,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件或杠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡宣蔚,警方通過查閱死者的電腦和手機(jī)向抢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胚委,“玉大人挟鸠,你說我怎么就攤上這事∧抖” “怎么了艘希?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我覆享,道長(zhǎng)佳遂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任淹真,我火速辦了婚禮讶迁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘核蘸。我一直安慰自己巍糯,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布客扎。 她就那樣靜靜地躺著祟峦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪徙鱼。 梳的紋絲不亂的頭發(fā)上宅楞,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音袱吆,去河邊找鬼厌衙。 笑死,一個(gè)胖子當(dāng)著我的面吹牛绞绒,可吹牛的內(nèi)容都是我干的婶希。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼蓬衡,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼喻杈!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起狰晚,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤筒饰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后壁晒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瓷们,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年讨衣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了换棚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡反镇,死狀恐怖固蚤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情歹茶,我是刑警寧澤夕玩,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布你弦,位于F島的核電站,受9級(jí)特大地震影響燎孟,放射性物質(zhì)發(fā)生泄漏禽作。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一揩页、第九天 我趴在偏房一處隱蔽的房頂上張望旷偿。 院中可真熱鬧,春花似錦爆侣、人聲如沸萍程。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽茫负。三九已至,卻和暖如春乎赴,著一層夾襖步出監(jiān)牢的瞬間忍法,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工榕吼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留饿序,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓羹蚣,卻偏偏與公主長(zhǎng)得像嗤堰,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子度宦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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