這兩天自學的時候,復習了下 autolayout。本來想來寫一篇文章記錄下學習內容,搜了一下寫的人真不少,也寫得挺不錯的。照理我就不用寫了,但心裡總有那麼一點點遺憾,這麼流行的東西,我博客裡怎麼能沒有呢?既然如此,那就多寫點基礎內容。
警告:博主為博文貼了十幾張圖片,查克拉耗盡,生命垂危,關注 MicroCai 或者送香吻一個就能喚醒博主,好人一生平安。
在 storyboard 界面的右下角,有這麼一排圖標
鼠標放上去停留一小段時間,就會告訴你它們的作用,從左至右依次是:
下面這些是兩個視圖層次中同一級的 View 的對齊。
Leading Edges:頭對齊
Trailing Edges:尾對齊
Top Edges:頂部對齊
Bottom Edges:底部對齊
Horizontal Centers:水平中心對齊
Vertical Centers:垂直中心對齊
BaseLines:基准線(默認 View 底部位置)水平對齊,用來對齊有文字的控件,如 UILabel、UIButton 等
Horizontal Center in Container:View 的水平中心和容器的水平中心的相對距離
Vertical Center in Container:View 的垂直中心和容器的垂直中心的相對距離
在對齊數值的白色輸入框內,點擊右側下拉框可以選擇“Use Current Canvas Value”,意思是使用當前 Xib/Storyboard 內的差值。
最後一個 Update Frames 表示如何更新 frame,有三個選項,默認為 None 不更新
None:不更新 frame
Items of New Constraints:更新新添加的 frame
All Frames in Container:更新容器內所有的 frame
最上面有四個矩形框和四條虛線,原來用過 auto resizing 的童鞋應該會比較眼熟。矩形框裡的數字表示當前的 View 到最近的 View (注意:不是 SuperView)邊緣的距離。
在矩形框下面有一行灰色字的可選項“Constrain to margins”,意思是在設置上述約束是相對於 margins 設置的,而 margin 默認距離是 16。如和上邊緣距離 306,加上 16,所以 View 的頂部和它上邊最近的 View 的距離是 312。
其他的選項
Width:設置寬度 Height:設置高度 Equal Widths:設置兩個同級 View 的寬度關系 Equal Heights:設置兩個同級 View 的高度關系 Aspect Ratio:設置 View 自身寬高比例 Align:和前面所講的 **Align** 一致。那為什麼 **Align** 還會出現在這邊呢?估計和 **Pin** 有關系,故而也放到這邊。
可以選擇要處理的 Views:當前選中的 Views 或 Controller 內所有的 Views
Update Frames:更新 frame Update Constraints:更新約束 Add Missing Constraints:添加遺漏的約束 Reset to Suggested Constraints:重置約束 Clear Constraints:清除約束
設置重置大小會如何影響其他對象,有兩個選項(默認已勾選)
Sublings and Ancestors:影響同級兄弟 Views 和祖先 Views Descendants:影響後代 Views
(這個地方我也沒弄明白,我查了文檔和一些博客,都只是做了簡單文字說明,然後自己試了下勾選和未勾選的情況,還是找不到有什麼區別,所以也沒明白具體是如何影響。)
SizeClass 中文意思可以理解為尺寸等級,就是在 autolayout 的基礎上,加上屏幕尺寸類型的定義。SizeClass 的寬高有三種類型:Compact(緊湊)、Any(任意)、Regular(普通)
不同設備屏幕的寬高類型
當你具體選擇尺寸時, IB 會顯示出當前選擇的屏幕尺寸的相關信息,如寬高類型,屏幕尺寸類型,這個尺寸適用於哪些設備等。
如果你設置的某種類型屏幕的約束布局,在其他類型屏幕下出現不符合意圖的布局時,可以重寫布局,即重新設置該屏幕類型下的布局。
如圖的 UIView 設置了 wAny|hAny 上下左右四個約束
點擊這個 UIView,查看 Attribute Inspector 有個 install 選項被勾選上了。
Installed/UnInstalled 表示的意思是當前布局是否被安裝在什麼類型的屏幕上。Installed/UnInstalled 前面如果沒有東西,表示布局是安裝在 wAny|hAny 類型的屏幕上。現在如果我們要單獨設置某種類型屏幕的布局可以點擊加號,選擇屏幕類型
將新的屏幕類型的勾選取消掉,則當前布局在該類型下不起作用,此時就可以切換類型,重新設置所有約束。
若只想修改一條約束,也可以點擊 Size Inspector
選擇 Constraints —— All 顯示所有約束,雙擊任意一條約束,會出現一個和之前類似的界面
Installed 前面也有個加號,估計你也該猜到了,這裡的修改和之前也是類似的。
如果 Xcode 的 Inspector 一些選項前面有加號,就表明它可以被重寫,比如剛剛這幅圖中,Installed
選項上方的 Constant 前面也有加號,說明它也是可以被重寫的。類似的還有 UILabel 的 font,但是:與重寫布局不同,在不同的 size class 中改變文字的屬性始終會影響基礎布局中的文字。它不能像布局一樣,在不同的size class中設置不同的屬性值。我們通過下面的方法來解決這一問題。 參考:Swift自適應布局(Adaptive Layout)教程(二)。
在屏幕類型多了這麼多之後,做不同類型的屏幕適配也是需要花不少功夫。如果每次適配一種類型屏幕後,都要運行後才能查看效果,效率簡直太低下了。Xcode 6 提供了 preview 預覽功能,針對這個問題可以節省不少時間。
點擊 Xcode Tool Bar 的 Assistant Editor 按鈕,顯示另一個窗口
選擇 Preview 展示預覽界面
如果想在預覽中同時顯示不同類型的屏幕,可以在預覽界面的左下角點擊加號,選擇更多設備
如果想要查看橫屏/豎屏,點擊設備下方的旋轉按鈕即可
不得不說,VFL 的語法比手寫 Constraints 的量少了很多,也有象形文字的感覺,但是和普通的 Swift 語法風格比起來實在不搭調。這個內容直接看 官方文檔 吧,都用圖表示出來,說明的挺清楚的。
Masonry 是目前為止公認的 autolayout 最好的替代方案,語法簡潔、直觀,不會出現各種意外之外的布局。根據網上了解到的一些情況,Masonry 在大型項目中的效果比 autolayout 好很多。
舉個簡單的例子:要使一個 subview 填充 superview,但和 superview 的邊界間距(inset) 10 個像素。使用 NSLayoutConstraints 是這麼寫的
UIView *superview = self; UIView *view1 = [[UIView alloc] init]; view1.translatesAutoresizingMaskIntoConstraints = NO; view1.backgroundColor = [UIColor greenColor]; [superview addSubview:view1]; UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10); [superview addConstraints:@[ //view1 constraints [NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeTop multiplier:1.0 constant:padding.top], [NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeLeft multiplier:1.0 constant:padding.left], [NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-padding.bottom], [NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeRight multiplier:1 constant:-padding.right], ]]; 一個簡單的布局竟然寫了這麼多代碼,有種要死的感覺。用 Masonry 又是什麼樣子呢? UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10); [view1 mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler make.left.equalTo(superview.mas_left).with.offset(padding.left); make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom); make.right.equalTo(superview.mas_right).with.offset(-padding.right); }]; 代碼少了至少一半以上,終於能緩過氣來了。但是還不夠,還能更少。 [view1 mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(superview).with.insets(padding); }];至尊寶:整個世界都清淨了!!!
Masonry 的 Github 地址