一、簡介
在以前的iOS程序中,經常編寫大量的坐標計算代碼,為了保證在3.5 inch和4.0 inch屏幕上都能有完美的UI界面效果,有時還需要分別為2種屏幕編寫不同的坐標計算代碼,即傳說中的“屏幕適配”。
那什么是Autolayout呢?
Autolayout是一種“自動布局”技術,專門用來布局UI界面的。Autolayout自iOS6開始引入,由于Xcode4的不給力,當時并沒有得到很大推廣。自iOS7(Xcode5)開始,Autolayout的開發效率得到很大的提升。蘋果官方也推薦開發者盡量使Autolayout來布局UI界面,它能很輕松地解決屏幕適配的問題。在Autolayout之前,有Autoresizing可以作屏幕適配,但局限性較大,有些任務根本無法完成,相比之下,Autolayout的功能比Autoresizing強大很多。
如何使用呢?
Autolayout有2個核心概念:參照和約束。它通過內定的Constraint(約束)和各項條件來計算出合理的布局.而這個合理的布局,符合我們的的預期和意圖。將我們想象中的結果展現出來。Constraint的設定非常靈活,實現一種布局的方法可以通過多Constraint來完成.
以下幾點是我們在開始使用之前必須弄清楚的事情:
- 我們要拋棄以往舊的布局方式不再去關注ViewFrame,center,和autoresizing,因為這些坐標和大小的定位都可以通過來Auto Layout完成。
- 理解每一種Constraint的含義,否則,當你去看別人的實現的Constraint時,就會有種看天書的感覺。
- 按意圖設計,一切按我們理想中的效果去布局,只要約束設定的合理,就一定能夠完成目標布局。
二、用xib或者storyboard來實現Autolayout
-
先從Interface Builder開始吧. 打開某個xib或者storyboard,
在右側Show in file inspector里面找到Ues Autolayout,將其勾選。如下圖:
自此,Autolayout便啟用成功,autoresizingMask被廢棄.其所有以往的功能和特性都被Autolayout取代.
注意:
現在我們定位控件位置的方式,不再像以前一樣,計算好每一個控件具體的位置,x是多少,y是多少。而是思考,這個控件離左邊是相隔多少距離,或者離頂部或底部相隔多少距離。
而有些規則性的事情還是類似的,比如我們定位一個控制的位置,一定要有x,y兩個坐標點同時有值,少一個都不能正常顯示。
同樣Autolayout在創建約束時也一樣,在思考完離頂部距離以后,還需要思考離頂部距離,否則控件的顯示位置一樣無法正常顯示.
換言之,要讓Autolayout計算出合理的位置,需要保證水平距離和垂直距離同時存在. 否則IDE,都會給出警告,提示這樣的布局Ambiguous Layout(模凌兩可) Interface Builder提供Autolayout的功能:
2.1如下圖:
詳解:
(選擇兩個view時可設置)
Leading Edges:左邊對齊
Trailing Edges:右邊對齊
Top Edges:頂部對齊
Bottom Edges:底部對齊
Horizontal Centers:水平居中
Vertical Centers:垂直居中
Baselines:文本底標線對齊
(單選擇一個view時可設置)
Horizontal Center in Container:對于父view的水平居中
Vertical Center in Container:對于父view的垂直居中
2.2如下圖:
(選擇框)
None:添加完約束后不進行任何操作,
Items of New Constraints:在添加約束后重新擺放約束涉及到的view
All Frames in Container:在添加約束后重新擺放所有這個容器內的view
2.3如下圖:
詳解:
上面的十字是"與最近的鄰居的約束", 填上數字, 單擊虛線變成實線就是要添加這個約束.
這里的"鄰居"是將一個包含子view的父view看做一個裝了一堆積木的盒子, 積木相對于盒子的邊框和其他的積木都作為"鄰居"。
(定義的寬高數據約束)
Widths:寬度指定,
Height:高度指定,
(定義多個view之間的寬高約束)
Equal Widths:寬度相同,
Equal Heights:高度相同,
(定義多個view之間的寬高約束)
Align:多個view之間的對齊約束
2.4如下圖
詳解:
(上半部分菜單的操作對象是當前選中的view, 下半部分的操作對象是選中view內的view)
Update Frames:刷新frame(使用當前已經設置的所有約束),
Update Constraints:刷新約束(根據當前的約束和frame, 更新約束的constant值),
Add Missing Constraints:添加缺失的約束(自動添加系統認為你應該添加卻忘記添加的約束, 測試中經常搞出沖突)
Reset to Suggested Constraints:重置為系統建議的約束(清理系統認為重復/沖突的約束, 測試中經常搞出問題)
Clear Constraints:清理所有約束(刪除對象上綁定的所有約束)
三、代碼創建AutoLayout
代碼創建的約束有兩種方式:
1:常規約束,寫法非常冗長,但能實現所有的約束方式以及非常特殊的約束方式,代碼如下:
// 添加兩個控件到父控件上添加藍色View
UIView *blueView = [[UIView alloc] init];
blueView.backgroundColor = [UIColor blueColor];
[self.view addSubview:blueView];
self.blueView = blueView;
// 添加紅色View
UIView *redView = [[UIView alloc] init];
redView.backgroundColor = [UIColor redColor];
[self.view addSubview:redView];
// 禁用auturezing
blueView.translatesAutoresizingMaskIntoConstraints = NO;
//注意, 這里設置父控件無效,要設置自己的translatesAutoresizingMaskIntoConstraints屬性為NO。
redView.translatesAutoresizingMaskIntoConstraints = NO;
// 添加約束
// 添加藍色VIew距離父控件左邊的距離固定為20 X
NSLayoutConstraint *leftCos = [NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:20];
[self.view addConstraint:leftCos];
// 紅色的頂部和藍色的底部距離固定 Y
NSLayoutConstraint *redTopCos = [NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:blueView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:20];
[self.view addConstraint:redTopCos];
注意:
如果是從代碼層面開始使用Autolayout,需要對使用的View的translatesAutoresizingMaskIntoConstraints的屬性設置為NO.
添加約束之前一定要將子視圖優先addSubview到父視圖中,否則在添加約束時會產生編譯器警告.而我們在理解的時候,可以通過這種方式來理解.
Item == first item 需要設置約束的控件
attribute == 需要設置的約束
relatedBy == relation 等于
toItem == second item 被參照的控件
attribute == 需要設置的約束
multiplier == multiplier 乘以
constant = constant 加上
2.可視化格式語言約束(VFL)
所謂可視化格式語言約束,是一種很直觀的理解方式,當然,前提是你已經熟練理解這套語言的規則.
VFL是蘋果公司為了簡化Autolayout的編碼而推出的抽象語言。
通過可視化語言可以一次性創建多個約束. 這對于第一次方式來說,是相當方面和容易理解的.但可視化語言不是所有約束都能滿足.
我們可以用正則表達式的學習方式來學習這項可視化格式語言.舉例代碼如下:
// 添加兩個控件到父控件上
// 添加藍色View
UIView *blueView = [[UIView alloc] init];
blueView.backgroundColor = [UIColor blueColor];
[self.view addSubview:blueView];
// 添加紅色View
UIView *redView = [[UIView alloc] init];
redView.backgroundColor = [UIColor redColor];
[self.view addSubview:redView];
// 禁用Autoresizing
blueView.translatesAutoresizingMaskIntoConstraints = NO;
redView.translatesAutoresizingMaskIntoConstraints = NO;
// 添加約束
// 設置藍色View距離左邊和右邊有20的的間距 X 和 寬度
NSArray *blueViewH = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-20-[blueView]-20-|" options:0 metrics:nil views:@{@"blueView" : blueView}];
[self.view addConstraints:blueViewH];
// 設置藍色View距離頂部有20的間距, 并且高度等于50 Y 和高度
// 設置紅色View距離藍色底部有20的間距, 并且紅色View的高度等于藍色View的高度 Y 和高度
// 并且設置紅色和藍色右對齊
NSArray *blueViewV = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-20-[blueView(50)]-20-[redView(==blueView)]" options:NSLayoutFormatAlignAllRight metrics:nil views:@{@"blueView" : blueView, @"redView": redView}];
[self.view addConstraints:blueViewV];
NSLayoutConstraint *redVeiwW = [NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:blueView attribute:NSLayoutAttributeWidth multiplier:0.5 constant:0];
[self.view addConstraint:redVeiwW];
注意: 在VFL語句中, 是不支持乘除法
VFL實例:
H:[cancelButton(72)]-12-[acceptButton(50)]
canelButton寬72,acceptButton寬50,它們之間間距12H:[wideView(>=60@700)]
wideView寬度大于等于60point,該約束條件優先級為700(優先級最大值為1000,優先級越高的約束越先被滿足)V:[redBox][yellowBox(==redBox)]
豎直方向上,先有一個redBox,其下方緊接一個高度等于redBox高度的yellowBoxH:|-10-[Find]-[FindNext]-[FindField(>=20)]-|
水平方向上,Find距離父view左邊緣默認間隔寬度,之后是FindNext距離Find間隔默認寬度;再之后是寬度不小于20的FindField,它和FindNext以及父view右邊緣的間距都是默認寬度。(豎線“|” 表示superview的邊緣)
四、VFL的使用
使用VFL來創建約束數組
+ (NSArray *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(NSDictionary *)metrics views:(NSDictionary *)views;
詳解:
lFormat: VFL語句
options: 對齊方式
metrics:VFL語句中用到的變量值
views:VFL語句中用到的控件
metrics:可以把VFL語句中的常量,抽取成為變量。由于是個字典,要包裝成對象類型
五、AutoLayout實現動畫
1.約束也可以在storyboard里面進行連線,只需要修改控件的約束,也能實現動畫。
[UIView animateWithDuration:1.0 animations:^{
[添加了約束的view layoutIfNeeded];
}];
- 可以在UIView中讓多個約束一起執行動畫
如果控件有默認的高度,只需要設置autoLayout的X值和Y值,autoLayout會自動計算出控件的寬高。
六、Autolayout的警告與錯誤
1.警告
控件的frame不匹配所添加的約束,比如約束控件的寬度為100,而控件現在的寬度是110.
2.錯誤
(1).缺乏必要的約束,只約束了寬度和高度,沒有約束具體的位置。
(2).兩個約束沖突,比如1個約束控件的寬度為100,一個約束控件的寬度為110
七、特殊情況
UILabel
UILabel不用約束寬度,會自動根據文字多少自動改變寬度——不用添加寬度約束(不會報錯)UISearchbar
searchbar添加到navigationBar的titleView中時,會自動伸長到整個navigationBar,對其添加的任何約束和frame屬性都沒有用。
解決方法:添加searchBar到另外一個普通的view中再設置為titleView