一、
《AutoLayout(III):淺析動畫》
http://www.vienta.me/2015/05/18/AutoLayout-%E6%B5%85%E6%9E%90%E5%8A%A8%E7%94%BB%EF%BC%88III%EF%BC%89/
在講動畫之前,先講幾個常見的可能會混淆的方法。
1.AutoLayout相關的幾個易混淆的方法
setNeedsLayout
layoutIfNeeded
layoutSubViews
setNeedsUpdateConstraints
updateConstraitsIfNeed
updateConstraints
子視圖在界面上的顯示大概經過了:更新約束-通過約束依賴關係得到具體的frame-展示到界面。上面幾個是和autolayout相關的方法,有必要大概瞭解一下這些方法具體是怎麼用的以及在什麼情況下觸發。
1.[layoutView setNeedsUpdateConstraints]:告訴layoutView需要更新約束,在下次計算或者更新約束會更新約束
2.[layoutView updateConstraintsIfNeeded]:告訴layoutView立即更新約束,
3.updateConstraints:系統更新約束的實際方法
總結上面的3點就是,setNeedsUpdateConstraints
確保了在將來某一時刻調用updateConstraintsIfNeeded
之後會接着調用updateConstraints
,從而達到更新view的約束的目的。但是要注意的是,如果僅僅單獨調用2,不一定能夠保證會調用updateConstraints
,因爲如果view上的約束是沒有變動的且沒有標記需要update的,這時就不會調用updateConstraints
。
4.[layoutView setNeedsLayout]:告訴layoutView頁面需要更新,但不立即執行
5.[layoutView layoutIfNeeded]:告訴layoutView頁面佈局立即更新
6.layoutSubviews:系統重寫佈局的實際方法
總結以上3點,setNeedsLayout
確保了在將來某個時刻通過調用layoutIfNeeded
之後會調用系統的layoutSubviews
,從而重寫對view重新佈局。同樣的如果單獨調用5,不一定能夠保證調用layoutSubviews
。[注:筆者寫了個demo發現,調用setNeedsLayout
會直接調用layoutSubviews
]。如果想要每次都能立即更新佈局,那就要把兩個方法一起用,同樣也適用於1和2。
系統調用layoutSubViews
時,就會調用updateConstraintsIfNeeded
,通過更新約束,用superView到subView的層次順序,來計算frame,反向確定佈局。
stackoverflow上有關於上面幾個方法的深入解答並分享了作者的實用經驗:
- 如果僅想要立即改變約束,調用
setNeedsLayout
- 如果改變view的一些屬性(如
offsets
)可能會導致佈局的改變,那麼調用setNeedsUpdateConstraints
,更多的時候後面需要加setNeedsLayout
。 - 如果想要立即改變佈局,如會形成新的frame,那麼需要在調用
layoutIfNeeded
。
2.AutoLayout與動畫
###[UIView animateWithDuration]方法
傳統的動畫主要是通過計算frame來進行動畫,在autolayout下,主要是利用約束,動畫的本質實際上是從一種約束狀態變成另一種約束狀態,從而來達到動畫的目的。
這個例子的Demo在這裏。
ViewController.h文件中:
@property (weak, nonatomic) IBOutlet UIView *animateView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *leftConstraint;
通過改變leftConstraint
的值來實現具體的平移動畫,具體代碼如下:
self.leftConstraint.constant = 200;
[UIView animateWithDuration:2 animations:^{
[self.view layoutIfNeeded];
}];
對於這類簡單動畫,只需要在animation的block中調用layoutIfNeeded
即可,從經驗來看,只要調用這個方法即可。
二、
《iOS中AutoLayout相關方法及流程》 http://blog.csdn.net/u012703795/article/details/42969133
關於UIView的Layer,iOS提供了三個方法:
1、layoutSubviews
在iOS5.1和之前的版本,此方法的缺省實現不會做任何事情(實現爲空),iOS5.1之後(iOS6開始)的版本,此方法的缺省實現是使用你設置在此view上面的constraints(Autolayout)去決定subviews的position和size。
UIView的子類如果需要對其subviews進行更精確的佈局,則可以重寫此方法。只有在autoresizing
和constraint-based
behaviors of subviews
不能提供我們想要的佈局結果的時候,我們才應該重寫此方法。可以在此方法中直接設置subviews的frame。
我們不應該直接調用此方法,而應當用下面兩個方法。
2、setNeedsLayout
此方法會將view當前的layout設置爲無效的,並在下一個upadte cycle裏去觸發layout更新。
3、layoutIfNeeded
使用此方法強制立即進行layout,從當前view開始,此方法會遍歷整個view層次(包括superviews)請求layout。因此,調用此方法會強制整個view層次佈局。
基於約束的AutoLayer的方法:
1、setNeedsUpdateConstraints
當一個自定義view的某個屬性發生改變,並且可能影響到constraint時,需要調用此方法去標記constraints需要在未來的某個點更新,系統然後調用updateConstraints
.
2、needsUpdateConstraints
constraint-based layout system使用此返回值去決定是否需要調用updateConstraints
作爲正常佈局過程的一部分。
3、updateConstraintsIfNeeded
立即觸發約束更新,自動更新佈局。
4、updateConstraints
自定義view應該重寫此方法在其中建立constraints. 注意:要在實現在最後調用[super
updateConstraints]
Auto Layout Process 自動佈局過程
與使用springs and struts(autoresizingMask)比較,Auto layout在view顯示之前,多引入了兩個步驟:updating constraints 和laying out views。每一個步驟都依賴於上一個。display依賴layout,而layout依賴updating constraints。 updating
constraints->layout->display
第一步:updating constraints,被稱爲測量階段,其從下向上(from subview to super view),爲下一步layout準備信息。可以通過調用方法setNeedUpdateConstraints
去觸發此步。constraints的改變也會自動的觸發此步。但是,當你自定義view的時候,如果一些改變可能會影響到佈局的時候,通常需要自己去通知Auto
layout,updateConstraintsIfNeeded。
自定義view的話,通常可以重寫updateConstraints方法,在其中可以添加view需要的局部的contraints。
第二步:layout,其從上向下(from super view to subview),此步主要應用上一步的信息去設置view的center和bounds。可以通過調用setNeedsLayout去觸發此步驟,此方法不會立即應用layout。如果想要系統立即的更新layout,可以調用layoutIfNeeded。另外,自定義view可以重寫方法layoutSubViews來在layout的工程中得到更多的定製化效果。
第三步:display,此步時把view渲染到屏幕上,它與你是否使用Auto layout無關,其操作是從上向下(from super view to subview),通過調用setNeedsDisplay觸發,
因爲每一步都依賴前一步,因此一個display可能會觸發layout,當有任何layout沒有被處理的時候,同理,layout可能會觸發updating constraints,當constraint system更新改變的時候。
需要注意的是,這三步不是單向的,constraint-based layout是一個迭代的過程,layout過程中,可能去改變constraints,有一次觸發updating constraints,進行一輪layout過程。
注意:如果你每一次調用自定義layoutSubviews都會導致另一個佈局傳遞,那麼你將會陷入一個無限循環中。
三、
《UIView常用的setNeedsDisplay和setNeedsLayout》 http://www.jianshu.com/p/33a28bb14749
1.UIView的setNeedsDisplay和setNeedsLayout方法
首先兩個方法都是異步執行的。而setNeedsDisplay會調用自動調用drawRect方法,這樣可以拿到 UIGraphicsGetCurrentContext,就可以畫畫了。而setNeedsLayout會默認調用layoutSubViews,
就可以 處理子視圖中的一些數據。
綜上所訴,setNeedsDisplay方便繪圖,而layoutSubViews方便出來數據。
layoutSubviews在以下情況下會被調用:
- 1、init初始化不會觸發layoutSubviews。
- 2、addSubview會觸發layoutSubviews。
- 3、設置view的Frame會觸發layoutSubviews,當然前提是frame的值設置前後發生了變化。
- 4、滾動一個UIScrollView會觸發layoutSubviews。
- 5、旋轉Screen會觸發父UIView上的layoutSubviews事件。
- 6、改變一個UIView大小的時候也會觸發父UIView上的layoutSubviews事件。
- 7、直接調用setLayoutSubviews。
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實時刷新屏幕
參考 : http://ibloodline.com/articles/2016/03/02/autolayout-questions.html