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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/457805.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/457805.shtml
英文地址,請注明出處:http://en.pswp.cn/news/457805.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

docker-compose下載慢_編寫Docker Compose時要注意的五大常見錯誤

在構建容器化的應用時,開發人員往往需要某種方法來引導啟動目標容器,以對其進行代碼級別的測試。盡管業界有許多方法可以實現該目的,但Docker Compose是目前最受歡迎的一種方法。它能夠讓如下兩個方面變得容易實現:指定在開發過程…

前端測試利器--Browser-Sync啟動命令

使用browser-sync啟動命令cmd切換到項目的根目錄下**1.browser-sync start --server --files "css/*.css"----------**使用兩個*檢測所有的目錄**轉載于:https://blog.51cto.com/1888512/1862054

VMware實現Android x86 8.1 從安裝到使用

VMware實現Android x86 8.1 從安裝到使用 虛擬機--Android 安裝 Android系統配置 安裝軟件 個性化設計 托坑指南 一些終端模擬器的指令 虛擬機–Android 發現現在安卓虛擬機已經到了8.1,我就試試能不能安裝并正常使用。由于版本過新,網上也沒有一些系統的…

frame越過另一個frame_擁抱swoole(三)之用php實現一個混合服務器

混合服務器,就是可以同時支持http,websocket,tcp等的服務器,用swoole就是這么簡單,分分鐘,就可以愉快地搞物聯網開發了,啥都支持,我采用官方的例子,創建一個混合服務器&a…

Hibernate學習系列————注解一對多單向實例

2019獨角獸企業重金招聘Python工程師標準>>> 開發環境:MysqlEclipse 一對多單向的列子原理:一個班級,多個學生,學生端為多的一端,他們擁有一個外鍵指向相同的班級。 項目結構 需要的jar包 hibernate.cfg.xm…

Spring學習筆記--自動裝配Bean屬性

Spring提供了四種類型的自動裝配策略: byName – 把與Bean的屬性具有相同名字(或者ID)的其他Bean自動裝配到Bean的對應屬性中。byType – 把與Bean的屬性具有相同類型的其他Bean自動裝配到Bean的對應屬性中。constructor – 把與Bean的構造器入參具有相同類型的其他…

sudo apt-get nmap 報錯鎖占用

在Ubuntu中用apt-get命令安裝軟件是出現如下錯誤: 網上搜了一下原因,說是有另外一個程序在運行,導致鎖不可用,原因可能是賞析運行更新或安裝沒有正常完成。這是因為上次更新或者安裝沒有正常完成。 網上的兩種解決方法&#xff1…

python逐行讀取txt寫入excel_用python從符合一定格式的txt文檔中逐行讀取數據并按一定規則寫入excel(openpyxl支持Excel 2007 .xlsx格式)...

前幾天接到一個任務,從gerrit上通過ssh命令獲取一些commit相關的數據到文本文檔中,隨后將這些數據存入Excel中。數據格式如下圖所示觀察上圖可知,存在文本文檔中的數據符合一定的格式,通過python讀取、正則表達式處理并寫入Excel文…

筋斗云newcloud錯誤碼列表

響應碼信息備注440Ip Error客戶送IP錯誤441Callee Number Error被叫號碼位數錯誤(標準11位正確,錯誤加前綴0,或其他前綴)442Called Operator Error被叫運營商錯誤(支持移動,不支持聯通電信)443N…

Extjs 之 initComponent 和 constructor的區別(轉)

在創建自定義類時,先構造(constructor)后初始化(initComponent)。如:(在舊的Extjs 版本中使用 Ext.extend 實現擴展) Ext.define(Btn,{ extend:Ext.button.Button, init…

hive遍歷_從Hive中的stored as file_foramt看hive調優

一、行式數據庫和列式數據庫的對比1、存儲比較行式數據庫存儲在hdfs上式按行進行存儲的,一個block存儲一或多行數據。而列式數據庫在hdfs上則是按照列進行存儲,一個block可能有一列或多列數據。2、壓縮比較對于行式數據庫,必然按行壓縮&#…

oracle sql語句 從指定條數查詢

現有表A 查詢從第10行之后的數據 select a from ( select a, rownum r from A ) where r > 10 order by r; 實際工作中例子 select account,acct_name from ( select account, acct_name, rownum r from pmctl_nonsleep_acct ) where r > 10 order by

幫助孩子學會感恩_頁數204_出版日期2015.03_完整版PDF電子書下載

幫助孩子學會感恩_頁數204_出版日期2015.03_完整版PDF電子書下載 帶索引書簽目錄高清版_13813212 下載鏈接http://pan.baidu.com/s/1geEmUeZ 【作 者】(英)蒂姆惠特尼(TimWhitney)著【叢書名】陪孩子成長系列叢書【形態項】 204 …

xwpftablecell設置字體樣式_HTML的文字樣式

font 屬性可以用來作為 font-style, font-variant, font-weight, font-size, line-height 和 font-family 屬性的簡寫,或將元素的字體設置為系統字體。字體修改font-family 屬性:設置HTML頁面中的字體font-size 屬性:設置字體大小font-weight…

將中文標點符號替換成英文標點符號

/// 轉全角的函數(SBC case) /// ///任意字符串 /// 全角字符串 /// ///全角空格為12288,半角空格為32 ///其他字符半角(33-126)與全角(65281-65374)的對應關系是:均相差65248 ///public string ToSBC(string input) { //半角轉全角:char[] cinput.ToCh…

Centos6.5升級GCC

由于CentOS自帶的gcc實在是老掉牙了,所以決定升級一下gcc,下面介紹如何進行源碼編譯,升級gcc。 從GNU網站下載你想要的gcc版本,鏈接:ftp://ftp.gnu.org/gnu/gcc/,選擇合適的gcc版本,然后下載&am…

oracle sql語句 exists

exists 這個關鍵字只是個查詢條件 用來判斷后面跟的查詢語句是否查找到記錄 查找到為真 反之為假 例子 select * from ammst_corp a where account 999999999999999999 and exists ( select 1 from pmrgt_unit where unit_code a.open_unit ) 查找 9999999999999999…

python金字塔_高斯金字塔與拉普拉斯金字塔的原理與python構建

高斯金字塔和拉普拉斯金字塔【1】在圖像相關領域應用廣泛,尤其是圖像融合和圖像分割方面。本文從理論和opencv實現兩個方面對兩種金字塔進行了介紹,并給出了二者的視覺效果。1、高斯金字塔在計算機視覺與圖像處理相關任務中,經常需要使用同一…

mongodb在32位機的連接

Windows 32bit版本安裝Mongodb時,會發生的下面問題 2016-05-09T00:09:45.1240800 I STORAGE [initandlisten] exception in initAndListen: 28663 Cannot start server. The default storage engine wiredTiger is not available with this build of mongod. Pleas…

oracle sql 語句 start with ...... connect by prior .......

這個查詢條件可以理解為遞歸查詢 select up_unit_code from pmctl_nuit START WITH unit_code 1188899Q CONNECT BY PRIOR up_unit_code unit_code 語句理解: 首先根據條件 START WITH unit_code 查詢到 up_unit_code 顯示 然后 CONNECT BY P…