前言:
開發(fā)得跟view打交道,我們也經(jīng)??吹教O果官方代碼有l(wèi)ayout方法的相關(guān)調(diào)用,但是大家可知道什么時候調(diào)用,什么時候需要嗎?針對網(wǎng)上大部分資料講得不夠清晰,我決定用Demo來講解
一、layoutSubviews
不能直接調(diào)用這個方法。如果強制刷新布局,請調(diào)用setNeedsLayout,如果想馬上刷新界面,請調(diào)用layoutIfNeeded
二、setNeedsLayout跟layoutIfNeded
setNeedsLayout調(diào)整視圖的子視圖的布局時,是在應(yīng)用程序的主線程調(diào)用此方法的。此方法記錄調(diào)整的布局請求并立即返回。注意,此方法不會強制立即更新,而是等待下一個更新周期才進行刷新頁面布局。此行為允許你將所有的布局更新合并到一個更新周期,這很適合用來優(yōu)化性能。
首先我們在Main.storyboard拖取出一個adjust Height按鈕以及帶有約束的一個viewRed
如圖:

把viewRed的Constraints拖到ViewController.swift,并作為屬性
下面我用一段代碼驗證下
@IBAction func adjustedBtnClick(_ sender: Any) {? ? ? ? view.layoutIfNeeded()ifself.redViewHeight.constant ==30.0{self.redViewHeight.constant =self.view.frame.height -90}else{self.redViewHeight.constant =30.0}UIView.animate(withDuration:2.0) {self.view.layoutIfNeeded()? ? ? ? }? ? }
當按下按鈕時,你看到的第一件事是調(diào)用view.layoutIfNeeded()。請記住,此方法強制立即布局并顯示更新。你可能會想知道為什么在我們進行約束更改之前調(diào)用它呢,因為蘋果認為這是一個最佳做法,以確保任何以前的更新等待更新周期的完成,所以我已經(jīng)添加了它。
當這樣的約束被更新時,它會自動執(zhí)行相當于setNeedsLayout的操作,因此在下一個更新周期內(nèi)不需要更新視圖,所以不用額外添加代碼,我們就會看到視圖約束更新,但它不會有動畫效果。但在我們的例子中,添加了2秒的動畫塊UIView.animate,在該塊內(nèi),我們通過layoutIfNeeded方法強制立即布局。由于此布局同步發(fā)生,因此在動畫塊中捕獲來自約束更改的幀移動,因此如果你現(xiàn)在運行應(yīng)用程序,則可以看到紅色視圖在2秒鐘內(nèi)如何變大變小。
效果如下:

當我們把動畫塊代碼替換成
UIView.animate(withDuration:2.0) {self.view.setNeedsLayout()? }
現(xiàn)在我們在動畫塊中正在做的是將視圖標記為需要布局更新,但不會立即強制執(zhí)行。相反,setNeedsLayout方法返回,視圖只是在列表中,以在下一個更新周期中進行更新。凈效果是在動畫塊中不會發(fā)生動畫,因為該塊中的視圖沒有更改。
效果如下:

在這種情況下單擊按鈕將立即根據(jù)更新的約束更新視圖大小,而不是動畫更新。等一下,如果我們沒有使用layoutIfNeeded,為什么會立即?
對我們而言,紅色視圖的大小變化立即發(fā)生。起初它似乎是反直覺的,因為我們沒有使用layoutIfNeeded強制立即更新。但是,我們沒有在動畫塊的上下文中進行視圖更新,也就是說剛好是一個周期,所以它似乎是立即的,跟沒有UIView.animate這段代碼效果是一樣的。因為我們標記為更新,更新周期的發(fā)生的地方也是動畫開始的地方。
因此,由于我們的代碼已經(jīng)標記該視圖需要通過setNeedsLayout進行布局更新,所以是在更新周期中立即啟動視圖更新,而不是從更新周期開始約束更改和幀移動的動畫。
刷新子對象布局:
-layoutSubviews方法:這個方法,默認沒有做任何事情,需要子類進行重寫
------------ 2018.8.25 更新 ------------
這個方法在iOS 5.1和更早版本上沒有任何作用。子類可以根據(jù)需要重寫此方法,以執(zhí)行更精確的子視圖布局。只有當子視圖的自動調(diào)整、約束的行為不能滿足你時,你才應(yīng)該重寫此方法
-setNeedsLayout方法: 標記為需要重新布局,異步調(diào)用layoutIfNeeded刷新布局,不立即刷新,但layoutSubviews一定會被調(diào)用
-layoutIfNeeded方法:如果有需要刷新的標記,立即調(diào)用layoutSubviews進行布局(如果沒有標記,不會調(diào)用layoutSubviews)關(guān)于在什么的情景會使用該方法,stackoverflow回答的比較詳細
關(guān)鍵點
layoutIfNeeded不一定會調(diào)用layoutSubviews方法。
setNeedsLayout一定會調(diào)用layoutSubviews方法(有延遲,在下一輪runloop結(jié)束前)。
如果想在當前runloop中立即刷新,調(diào)用順序應(yīng)該是
[selfsetNeedsLayout];
[selflayoutIfNeeded];
反之可能會出現(xiàn)布局錯誤的問題。先調(diào)用[view setNeedsLayout],把標記設(shè)為需要布局,然后馬上調(diào)用[view layoutIfNeeded],實現(xiàn)布局
在視圖第一次顯示之前,標記肯定是“需要刷新”的,所以直接調(diào)用[view layoutIfNeeded]就會進行立即更新
作者:Dwyane_Coding
鏈接:http://www.itdecent.cn/p/a84f85729952
來源:簡書
簡書著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請聯(lián)系作者獲得授權(quán)并注明出處。