比較layoutIfNeeded,layoutSubviews和setNeedsLayout
1、layoutIfNeeded 方法一旦被調用,主線程會立即強制重新布局,它從當前視圖開始,一直到完成所有子視圖的布局。
2、layoutSubviews 用來自定義視圖尺寸。它是系統自動調用的,開發者不能手動調用。我們能做的就是重寫該方法,讓系統在調整尺寸時能按照我們希望的效果進行布局。這個方法主要在屏幕旋轉、滑動或觸摸界面、修改子視圖時被觸發。
3、setNeedsLayout 與 layoutIfNeeded 相似,唯一不同的就是它不會立刻強制視圖重新布局,而是在下一個布局周期才會觸發更新。它主要用在多個視圖布局先后更新的場景下。例如,要在相關位置不斷變化的點之間連一條線,這個線的布局就可以調用setNeedsLayout 方法。
iOS之layout方法-layoutSubviews、layoutIfNeeded、setNeedsLayout
ios layout機制相關方法
- (CGSize)sizeThatFits:(CGSize)size
- (void)sizeToFit
——————-
- (void)layoutSubviews
- (void)layoutIfNeeded
- (void)setNeedsLayout
——————–
- (void)setNeedsDisplay
- (void)drawRect
---------------------
大概常用的上面幾個 , 具體的應該還有別的。
layoutSubviews
這個方法,默認沒有做任何事情,需要子類進行重寫 。 系統在很多時候會去調用這個方法:
1.初始化不會觸發layoutSubviews,但是如果設置了不為CGRectZero的frame的時候就會觸發。
2.addSubview會觸發layoutSubviews
3.設置view的Frame會觸發layoutSubviews,當然前提是frame的值設置前后發生了變化
4.滾動一個UIScrollView會觸發layoutSubviews
5.旋轉Screen會觸發父UIView上的layoutSubviews事件
6.改變一個UIView大小的時候也會觸發父UIView上的layoutSubviews事件
在蘋果的官方文檔中強調: You should override this method only if the autoresizing behaviors of the subviews do not offer the behavior you want.layoutSubviews, 當我們在某個類的內部調整子視圖位置時,需要調用。反過來的意思就是說:如果你想要在外部設置subviews的位置,就不要重寫。
-layoutSubviews方法:這個方法,默認沒有做任何事情,需要子類進行重寫
-setNeedsLayout方法: 標記為需要重新布局,異步調用layoutIfNeeded刷新布局,不立即刷新,但layoutSubviews一定會被調用
-layoutIfNeeded方法:如果,有需要刷新的標記,立即調用layoutSubviews進行布局(如果沒有標記,不會調用layoutSubviews)
如果要立即刷新,要先調用[view setNeedsLayout],把標記設為需要布局,然后馬上調用[view layoutIfNeeded],實現布局
在視圖第一次顯示之前,標記總是“需要刷新”的,可以直接調用[view layoutIfNeeded]
這個動畫中有用到 舉個栗子?? 。
如圖 , 上面有個label ,中間有個按鈕 , label已經被自動布局到左上角 。 然后我們那個left的constraint
@IBOutletweakvar leftContrain:NSLayoutConstraint!
在viewDidLoad中聲明好,然后在Main.storyboard中進行連線。點擊按鈕的時候 ,我們把左邊的距離改成100 。
在按鈕的點擊事件里加上這句。
leftContrain.constant =100
然后我們想要一個動畫的效果。
如果這么做
UIView.animateWithDuration(0.8, delay:0, usingSpringWithDamping:0.5, initialSpringVelocity:0.5, options: UIViewAnimationOptions.AllowAnimatedContent, animations: {? ? ? ? ? ? ? ? self.leftContrain.constant =100
? ? ? ? ? ? }, completion: nil)
你會發現然并卵 。其實這句話self.leftContrain.constant = 100只是執行了setNeedsLayout?標記了需要重新布局,但是沒有立即執行。所以我們需要在動畫中調用這個方法layoutIfNeeded
所以代碼應該這么寫
leftContrain.constant =100? ? ? ? UIView.animateWithDuration(0.8, delay:0, usingSpringWithDamping:0.5, initialSpringVelocity:0.5, options: UIViewAnimationOptions.AllowAnimatedContent, animations: {? ? ? ? ? ? ? ? self.view.layoutIfNeeded()//立即實現布局
? ? ? ? ? ? }, completion: nil)
所以上面不管寫多少約束的改變,只需要在動畫里動用 一次self.view.layoutIfNeeded()?,所有的都會已動畫的方式 。如果一些變化不想動畫 。在動畫前執行self.view.layoutIfNeeded()
drawRect
這個方法是用來重繪的。
drawRect在以下情況下會被調用:
1、如果在UIView初始化時沒有設置rect大小,將直接導致drawRect不被自動調用。drawRect調用是在Controller->loadView, Controller->viewDidLoad 兩方法之后掉用的.所以不用擔心在控制器中,這些View的drawRect就開始畫了.這樣可以在控制器中設置一些值給View(如果這些View draw的時候需要用到某些變量值).
2、該方法在調用sizeToFit后被調用,所以可以先調用sizeToFit計算出size。然后系統自動調用drawRect:方法。3、通過設置contentMode屬性值為UIViewContentModeRedraw。那么將在每次設置或更改frame的時候自動調用drawRect:。4、直接調用setNeedsDisplay,或者setNeedsDisplayInRect:觸發drawRect:,但是有個前提條件是rect不能為0。以上1,2推薦;而3,4不提倡
drawRect方法使用注意點:
1、若使用UIView繪圖,只能在drawRect:方法中獲取相應的contextRef并繪圖。如果在其他方法中獲取將獲取到一個invalidate的ref并且不能用于畫圖。drawRect:方法不能手動顯示調用,必須通過調用setNeedsDisplay 或者 setNeedsDisplayInRect,讓系統自動調該方法。2、若使用calayer繪圖,只能在drawInContext: 中(類似于drawRect)繪制,或者在delegate中的相應方法繪制。同樣也是調用setNeedDisplay等間接調用以上方法3、若要實時畫圖,不能使用gestureRecognizer,只能使用touchbegan等方法來掉用setNeedsDisplay實時刷新屏幕?
sizeToFit
sizeToFit會自動調用sizeThatFits方法;
sizeToFit不應該在子類中被重寫,應該重寫sizeThatFits
sizeThatFits傳入的參數是receiver當前的size,返回一個適合的size
sizeToFit可以被手動直接調用sizeToFit和sizeThatFits方法都沒有遞歸,對subviews也不負責,只負責自己
------------------------------------------------------------------------------------------------------
重繪
-drawRect:(CGRect)rect方法:重寫此方法,執行重繪任務
-setNeedsDisplay方法:標記為需要重繪,異步調用drawRect
-setNeedsDisplayInRect:(CGRect)invalidRect方法:標記為需要局部重繪
sizeToFit會自動調用sizeThatFits方法;
sizeToFit不應該在子類中被重寫,應該重寫sizeThatFits
sizeThatFits傳入的參數是receiver當前的size,返回一個適合的size
sizeToFit可以被手動直接調用
sizeToFit和sizeThatFits方法都沒有遞歸,對subviews也不負責,只負責自己
———————————-
layoutSubviews對subviews重新布局
layoutSubviews方法調用先于drawRect
setNeedsLayout在receiver標上一個需要被重新布局的標記,在系統runloop的下一個周期自動調用layoutSubviews
layoutIfNeeded方法如其名,UIKit會判斷該receiver是否需要layout.根據Apple官方文檔,layoutIfNeeded方法應該是這樣的
layoutIfNeeded遍歷的不是superview鏈,應該是subviews鏈
drawRect是對receiver的重繪,能獲得context
setNeedDisplay在receiver標上一個需要被重新繪圖的標記,在下一個draw周期自動重繪,iphone device的刷新頻率是60hz,也就是1/60秒后重繪?