日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

AutoLayout的那些事儿

發布時間:2023/12/18 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 AutoLayout的那些事儿 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

AutoLayout非常強大也非常易用,可讀性也很強,加上各種第三方AutoLayout庫,讓你布起局來猶如繃掉鏈子的狗!根本停不下來!以前的

1 label.frame.origin.y?+?label.frame.size.height?+?10

如今只用:

1 2 3 button.snp_makeConstraints{ ????$0.top.equalTo(label.snp_bottom).offset(10) }

真是好用得不要不要。

可是,我在使用AutoLayout卻遇到不少坑,翻閱了不少博客網站才找到了我認為的比較不錯的解決方案。我把這些內容貼出來,如果其中有誤,可以在下方留言指出,希望大家能夠多多交流共同進步。

本文主要分四部分:

  • updateViewConstraints與updateConstraints篇

  • AutoLayout與Frame篇

  • AutoLayout動畫篇

  • AutoLayout比例設置

其中‘篇’字體現了本文作者對逼格的追求。

updateViewConstraints與updateConstraints篇

基本用法

updateViewConstraints與updateConstraints是AutoLayout出現后新增的api,updateConstraints主要功能是更新view的約束,并會調用其所有子視圖的該方法去更新約束。

而updateViewConstraints的出現方便了viewController,不用專門去重寫controller的view,當view的updateConstraints被調用時,該view若有controller,該controller的updateViewConstraints便會被調用。

兩個方法都需要在方法實現的最后調用父類的該方法。并且這兩個方法不建議直接調用。

在使用過程中我發現這兩個方法有時候不會被系統調用。后來我看到public class func requiresConstraintBasedLayout() -> Bool方法的描述:

constraint-based layout engages lazily when someone tries to use it (e.g., adds a constraint to a view). If you do all of your constraint set up in -updateConstraints, you might never even receive updateConstraints if no one makes a constraint. To fix this chicken and egg problem, override this method to return YES if your view needs the window to use constraint-based layout.

大意是說,視圖并不是主動采用constraint-based的。在非constraint-based的情況下-updateConstraints,可能一次都不會被調用,解決這個問題需要重寫該類方法并返回true。

這里要注意,如果一個view或controller是由interface builder初始化的,那么這個實例的updateViewConstraints或updateConstraints方法便會被系統自動調用,起原因應該就是對應的requiresConstraintBasedLayout方法返回true。而純代碼初始化的視圖requiresConstraintBasedLayout方法默認返回false。

所以在純代碼自定義一個view時,想把約束寫在updateConstraints方法中,就一定要重寫requiresConstraintBasedLayout方法,返回true。

至于純代碼寫的viewController如何讓其updateViewConstraints方法被調用。我自己的解決辦法是手動調用其view的setNeedsUpdateConstraints方法。

How to use updateConstraints?

文檔中對于這兩個方法提的最多的就是,重寫這兩個方法,在里面設置約束。所以一開始我認為這兩個方法是蘋果提供給我們專門寫約束的。于是便開始嘗試使用。

直到后來在UIView中看到這樣一句話:

You should only override this method when changing constraints in place is too slow, or when a view is producing a number of redundant changes.

“你只因該在添加約束過于慢的時候,或者一次要修改大量約束的情況下重寫次方法。”

簡直是讓人覺得又迷茫又坑爹。updateConstraints方法到底應該何時使用

后來看到how to use updateConstraints這篇文章。給出了一個合理的解釋:

  • 盡量將約束的添加寫到類似于viewDidLoad的方法中。

  • updateConstraints并不應該用來給視圖添加約束,它更適合用于周期性地更新視圖的約束,或者在添加約束過于消耗性能的情況下將約束寫到該方法中。

  • 當我們在響應事件時(例如點擊按鈕時)對約束的修改如果寫到updateConstraints中,會讓代碼的可讀性非常差。

關于性能,我也做了一個簡單的測試:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 class?MMView:?UIView?{ ????override?init(frame:?CGRect)?{ ????????super.init(frame:?frame) ????????self.backgroundColor?=?UIColor.grayColor() ????????initManyButton() ????????//初始化時添加約束 ????????test()?//每次只有一個test()不被注釋就好 ????} ????override?func?touchesBegan(touches:?Set,?withEvent?event:?UIEvent?)?{ ????????//響應事件時添加約束 ????????//test() ????} ????override?func?updateConstraints()?{ ????????//updateConstraints中添加約束 ????????//test() ????????super.updateConstraints() ????} ????func?test(){ ????????let?then?=?CFAbsoluteTimeGetCurrent() ????????addConstraintsToButton() ????????let?now?=?CFAbsoluteTimeGetCurrent() ????????print(now?-?then) ????} ????required?init?(coder?aDecoder:?NSCoder)?{ ????????fatalError("init(coder:)?has?not?been?implemented") ????} ????let?buttonTag?=?200 ????func?initManyButton(){ ????????for?index?in?0...1000{ ????????????let?button?=?UIButton(type:?.System) ????????????button.tag?=?buttonTag?+?index ????????????self.addSubview(button) ????????} ????} ????func?addConstraintsToButton(){ ????????for?index?in?0...1000{ ????????????if?let?button?=?self.viewWithTag(index+buttonTag){ ????????????????button.snp_makeConstraints{?make?in ????????????????????make.center.equalTo(self) ????????????????????make.size.equalTo(self) ????????????????} ????????????} ????????} ????} }

分別將設置約束寫在init中、寫在updateConstraints中、寫在事件響應方法中 的時間消耗進行測試,對1000個button添加約束,每個添加4個約束。

  • init中,時間消耗約為0.37秒

  • 寫在updateconstraints中,時間消耗約為0.52秒

  • 寫在事件響應方法中,時間消耗約為0.77秒

所以,結論,還是將約束的設置寫在viewDidLoad中或者init中。沒事兒盡量不去碰updateConstraints。除非對性能有要求。

關于UIView的translatesAutoresizingMaskIntoConstraints屬性

最近在對AutoLayout的學習中發現,很多人似乎對translatesAutoresizingMaskIntoConstraints的誤解非常大,很多時候遇到問題總有人會在下面回答到:把translatesAutoresizingMaskIntoConstraints設置成false就可以解決問題。。。實際上并沒有什么用。

那么這個屬性到底是做什么的呢?

其實這個屬性的命名已經把這個屬性的功能解釋的非常清楚了。

除了AutoLayout,AutoresizingMask也是一種布局方式。這個想必大家都有了解。默認情況下,translatesAutoresizingMaskIntoConstraints = true , 此時視圖的AutoresizingMask會被轉換成對應效果的約束。這樣很可能就會和我們手動添加的其它約束有沖突。此屬性設置成false時,AutoresizingMask就不會變成約束。也就是說 當前 視圖的 AutoresizingMask失效了。

那我們什么時候需要設置這個屬性呢?

當我們用代碼添加視圖時,視圖的translatesAutoresizingMaskIntoConstraints屬性默認為true,可是AutoresizingMask屬性默認會被設置成.None。也就是說如果我們不去動AutoresizingMask,那么AutoresizingMask就不會對約束產生影響。

當我們使用interface builder添加視圖時,AutoresizingMask雖然會被設置成非.None,但是translatesAutoresizingMaskIntoConstraints默認被設置成了false。所以也不會有沖突。

反而有的視圖是靠AutoresizingMask布局的,當我們修改了translatesAutoresizingMaskIntoConstraints后會讓視圖失去約束,走投無路。例如我自定義轉場時就遇到了這樣的問題,轉場后的視圖并不在視圖的正中間。

所以,這個屬性,基本上我們也不用設置它。

AutoLayout與Frame篇

在使用AutoLayout的時候你可能也會同時也會用到frame,比如需要用到layer的時候。

那么你可能會遇到這種情況,想讓layer的尺寸是由其它視圖尺寸設定的,而這個視圖又是由約束控制布局的。如果將layer的初始化與view的初始化放在一個方法中,類似于viewDidLoad的方法中

1 layer.bounds?=?CGRectMake(0,0,view.bounds.size.widith?*?0.5,50)

那么很可能最終layer的寬度是0。

這是因為約束被設置之后它并不會立即對view作出改變,而是要等到layout時,才會對視圖的尺寸進行修改。而layout通常是在視圖已經加載到父視上時。

所以我們如果在viewDidLoad中設置了約束,要等到viewDidAppear時view的尺寸才會真正改變。

那么,如果需要既用約束布局,又用frame布局,如果能讓它們很好的協作呢?

一個很好的解決辦法是:吧frame設置寫到layoutSubviews中或者寫到viewDidLayoutSubviews中即可。因為約束生效時view的center或者bounds就會被修改,center或者bounds被修改時layoutSubview,就會被調用,隨后viewDidLayoutSubviews就回被調用。這個時候,設置約束的視圖frame就不再是(0,0,0,0)了

如果我們必須要將約束和frame寫在同一方法中,寫完約束就設置frame,而不是想把frame的設置寫到layoutSubview中(比如我們設置好約束后馬上就想根據約束的結果計算高度),那么我們還可以在設置完約束之后手動調用layoutIfNeeded方法,讓視圖立即layout,更新frame。在這之后就可以拿到設置約束的視圖的尺寸了。

AutoLayout動畫篇

這篇的內容非常簡單,就是介紹約束布局的視圖如何進行位移動畫。

如果我們的一個視圖是通過設置frame來布局的,那么我們在位移動畫時直接改變frame就可以了。很簡單。

可是在約束布局的視圖中,設置frame這個辦法就無效了。那我們怎么辦?

網上有很多人的辦法就是:拿到想要做動畫的約束,在動畫之前對約束進行修改,在動畫的block中調用setNeedsLayout方法。

這個方法我覺得非常的麻煩,為了方便地拿到約束,我們通常還需要把約束設置成屬性,動畫一多那豈不就是完蛋了?

一種更好的方法就是設置視圖的transform屬性。

比如我想要讓視圖做一個x軸+50的位移,

1 self.view.transform?=?CGAffineTransformMakeTranslation(50,?0)

這樣設置即可。CGAffineTransformMakeTranslation這個方法就是設置位置。

AutoLayout比例設置

如果我們用autoLayout想把一個視圖的中心設置到屏幕橫向和縱向的1/4處:

1 2 3 4 button.snp_makeConstraints{?make?in ????make.centerX.equalTo(self.view).multipliedBy(0.25) ????make.centerY.equalTo(self.view).multipliedBy(0.25) }

這就相當于

1 button.center?=?CGPointMake(self.view.bounds.size.width?*?0.25?,self.view.bounds.size.height?*?0.25)

那么AutoLayout中的倍數,具體表示什么呢?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 let?view?=?UIView() self.view.addSubview(view) var?bottomConstraint?:?Constraint! view.snp_makeConstraints?{?(make)?in ????make.height.equalTo(50) ????make.width.equalTo(50) ????make.centerX.equalTo(self.view.snp_centerX) ????bottomConstraint?=?make.bottom.equalTo(self.view.snp_centerY).constraint } self.view.layoutIfNeeded() print(view.frame) //打印結果?y:318?height:50?和為368 bottomConstraint.uninstall() view.snp_makeConstraints?{?(make)?in ????make.bottom.equalTo(self.view.snp_centerY).multipliedBy(1.5) } self.view.layoutIfNeeded() print(view.frame) //打印結果?y:318?height:50?和為552,剛好是368的1.5倍 //所以我們可以得出結論:某條邊的約束的倍數代表著這條邊到相對邊的距離的倍數 //上面代碼中的1.5倍讓bottom邊到y?=?0邊的距離變成了1.5倍

轉載于:https://www.cnblogs.com/guiyangxueyuan/p/5567176.html

總結

以上是生活随笔為你收集整理的AutoLayout的那些事儿的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。