前言:這篇文章是筆者在項目中對佈局技術進行技術選型和應用的相關介紹,供你們參考。
html
Question1:什麼是autoLayout?android
Answer1: autolayout是蘋果從iOS6開始推出的旨在優化、簡化UI佈局相關工做的新框架,其理念是抽象出約束的概念,將其做用於view,而再也不須要手動設置其frame。ios
我的理解其中的分別就好像面向對象編程和麪向過程編程之間的分別;可以體會面向對象編程的好處,咱們也不難領會autolayout帶來的變化:咱們惟一須要處理的,就是約束。其後的一切工做,蘋果幫你搞定。git
Question2:爲什麼要引入自動佈局?github
Answer2: 傳統的frame設置方式在面對碎片化的屏幕分辨率顯得力不從心;也難以應付旋轉屏等高階場景。約束的抽象讓你從分辨率的適配工做中解脫出來,只須要了解(或者說抽象出)view所須要的約束,就能夠得到一個理想的佈局。不然,你須要計算不少難以理解和維護的硬編碼,這不只不優雅,並且很容易出錯,可讀性和可維護性都相對較差。編程
當前iphone開發分辨率:api
iphone4/4s :320*480安全
iphone5/5c/5s: 320*568微信
iphone6: 375*667app
iphone 6 plus: 414*736
Autolayout技術:
從iOS6問世起(2012年下半年),蘋果推出了autolayout技術,並開始大力推廣其應用。Autolayout的api隨着版本更迭不斷的完善,業界也出現了不少第三方封裝的自動佈局api框架。咱們產品支持iOS7+,在系統支持方面是沒有任何障礙的(注意不要使用iOS8+的api便可,後面會進一步說明)。
1. autoLayout相關技術概覽:
4. Apple Interface Builder;
5. 第三方框架:masonry, purelayout, sdautolayout 等。
蘋果推出的基礎api,只要理解了autolayout的概念,也很容易理解其使用,但其缺點是接口很是冗長,使用麻煩,也缺乏組合的快捷接口,對於開發者來講不夠友好。
例(設置一個頁面水平居中的約束):
[self.view addConstraint:[NSLayoutConstraint
constraintWithItem:btn
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0]];
蘋果推出的一種格式標記語言,專門用於描述佈局關係。
例:
//水平方向
NSString *hVFL=@"H:|-20-[redView]-30-[blueView(==redView)]-20-|";
NSArray *hCons =[NSLayoutConstraintconstraintsWithVisualFormat:hVFL options:NSLayoutFormatAlignAllTop |NSLayoutFormatAlignAllBottom metrics:nilviews:@{@"redView":redView,@"blueView":blueView}]; [self.view addConstraints:hCons];
//垂直方向
NSString *vVFL=@"V:|-20-[redView(50)]";
NSArray *vCons =[NSLayoutConstraintconstraintsWithVisualFormat:vVFL options:0 metrics:nilviews:@{@"redView":redView}];
[self.view addConstraints:vCons];
該方式看上去比較科幻,須要對其格式標記語言有深刻了解以後才能夠熟練使用;這種代碼閱讀起來主要靠腦補,畫面太美。
根據資料顯示,VFL語言對於某一些場景的自動佈局不支持,且調試比較困難,可讀性不好,學習成本高。
不推薦在項目中使用該技術(有興趣的話能夠用來學習和研究)。
AppleInterface Builder:
蘋果的圖形化編輯器。在其中能夠方便、快速的設定約束(多用ctrl鍵)。
對於一次成型的view來講,此方式的優點仍是顯而易見的;推薦和xib一塊兒使用。
Tips:
1. 建議對view進行命名,不然界面稍微複雜起來很容易將約束搞混。
2. 約束也能夠做爲outlet,用於後續變動,也能夠用來作動畫,很方便。
第三方框架:
第三方框架封裝的出發點主要是但願可以快速、可靠的使用代碼來進行autolayout佈局;在封裝的過程當中儘量保持原生autolayout的所有能力。
最有名的框架是masonry,在github上1W+ stars.
https://github.com/SnapKit/Masonry
我在當前項目中引入的框架:pureLayout
https://github.com/PureLayout/PureLayout
masonry的特色是其簡潔的鏈式語法;而pureLayout則更加保持了oc的語法習慣。這兩個庫都很是成熟。
之因此選擇pureLayout,更多的是出於我的喜愛和習慣;在我一年多的使用過程和學習經驗來看,該庫的學習成本很是低,可以快速上手使用,api風格接近原生,也比較可靠穩定。
iOS 9.0+。
API對比示例:
// Creating constraints usingNSLayoutConstraint
[NSLayoutConstraint
constraintWithItem:subview
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeadingMargin
multiplier:1.0
constant:0.0].active = YES;
[NSLayoutConstraint
constraintWithItem:subview
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTrailingMargin
multiplier:1.0
constant:0.0].active = YES;
// Creating the same constraints usingLayout Anchors
UILayoutGuide *margin =self.view.layoutMarginsGuide;
[subview.leadingAnchorconstraintEqualToAnchor:margin.leadingAnchor].active = YES;
[subview.trailingAnchorconstraintEqualToAnchor:margin.trailingAnchor].active = YES;
能夠看到,這裏的api調用簡單了不少。看來蘋果也已經認識到了其api的冗長,對於開發者不夠友好,所以對其作了二次封裝。但此api 是iOS9+,所以咱們當前項目中沒法使用。不過這不影響咱們自行去體驗和學習其api。
官方文檔:
務實的選擇
羅列了上面的這些技術,咱們就能夠大體的選擇咱們的技術脈絡了。NSLayoutAnchorSDK API由於系統限制,當前還不能使用;
NSLayoutConstraintSDK API雖然是原生,但開發成本太高,也不適合選擇;
VFL語言有其固有缺陷(主要是可讀性和維護性方面),也不建議在正式項目中使用;
咱們能夠在項目中根據實際狀況,結合使用第三方的框架(適用於代碼編寫的view)和Interface Builder(xib的view)這兩種方式。而具體對於某一個view來講,只使用一種方式(代碼或者xib)來進行自動佈局是相對安全且易於維護的方式。
約束類型介紹:
1. 長/寬,也就是其size
2. 設置距離:好比a view的最右邊和b view的最左邊之間的距離。
3. 設置居中(或居中的距離),好比 a view的x center和b view的 x center相等,或者二者之間的差值是多少。
4. iOS 8+蘋果對view增長了margin(邊緣)的概念,所以autolayout的2和3也相應的增長了邊緣相關的api,即將以前的實際邊界替換成邊緣,而後再取理解就ok. 咱們的系統支持iOS7+,所以請務必不要使用此類api.
簡單佈局示例(使用purelayout):
- (void)setupButtonsConstraints
{
if ([self.buttonscount] < 2 && [self.buttonscount] > 0)
{
UIButton *button = self.buttons[0];
[button autoPinEdgesToSuperviewEdges];
}
else
{
[self.buttonsautoMatchViewsDimension:ALDimensionWidth];
[[self.buttonsfirstObject] autoPinEdgeToSuperviewEdge:ALEdgeLeft];
UIButton *previousView = nil;
for (UIButton * btn inself.buttons)
{
[btn autoAlignAxisToSuperviewAxis:ALAxisHorizontal];
[btn autoPinEdgeToSuperviewEdge:ALEdgeTop];
[btn autoPinEdgeToSuperviewEdge:ALEdgeBottom];
if (previousView)
{
[btn autoPinEdge:ALEdgeLefttoEdge:ALEdgeRightofView:previousView];
}
previousView = btn;
}
[[self.buttonslastObject] autoPinEdgeToSuperviewEdge:ALEdgeRight];
}
}
這裏對一組button(其數量是變化的)設置了約束,它們寬度固定,水平排列,兩兩相連,左右都頂到了view的最邊緣。
對於通常的view來講,4個約束能夠固定一個view。能夠想象一下,你的view可否同時知足這幾個條件,知足這幾個條件之後它是否是就動不了了。
框架使用最佳實踐:
學習和研究第三方庫中的示例代碼和api說明,可以幫助咱們快速的掌握其api使用;若是有必要能夠深刻其源代碼瞭解其中的細節。通常來講,其示例代碼中的api調用方式是相對標準和安全的,值得咱們參考和借鑑。
One more thing(關於autolayout的錯誤調試:)
autolayout錯誤會打印以下:
Unable to simultaneously satisfy constraints. Probably at least one of the constraintsin the following list is one you don't want. Try this: (1) look at eachconstraint and try to figure out which you don't expect; (2) find the code thatadded the unwanted constraint or constraints and fix it. (Note: If you'reseeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer tothe documentation for the UIView propertytranslatesAutoresizingMaskIntoConstraints) ( "<NSAutoresizingMaskLayoutConstraint:0x887d630 h=--&v=--& V:[UIButtonLabel:0x886ed80(19)]>", "<NSAutoresizingMaskLayoutConstraint:0x887d5f0 h=--&v=--& UIButtonLabel:0x886ed80.midY == + 37.5>", "<NSAutoresizingMaskLayoutConstraint:0x887b4b0h=--& v=--& V:[UIButtonLabel:0x72bb9b0(19)]>", "<NSAutoresizingMaskLayoutConstraint:0x887b470h=--& v=--& UIButtonLabel:0x72bb9b0.midY == - 0.5>", "<NSLayoutConstraint:0x72bf860V:[UILabel:0x72bf7c0(17)]>", "<NSLayoutConstraint:0x72c2430UILabel:0x72bfad0.top == UILabel:0x72bf7c0.top>", "<NSLayoutConstraint:0x72c2370UILabel:0x72c0270.top == UILabel:0x72bfad0.top>", "<NSLayoutConstraint:0x72c22b0V:[UILabel:0x72bf7c0]-(NSSpace(8))-[UIButton:0x886efe0]>", "<NSLayoutConstraint:0x72c15b0V:[UILabel:0x72c0270]-(NSSpace(8))-[UIRoundedRectButton:0x72bbc10]>", "<NSLayoutConstraint:0x72c1570UIRoundedRectButton:0x72bbc10.baseline ==UIRoundedRectButton:0x7571170.baseline>", "<NSLayoutConstraint:0x72c21f0 UIRoundedRectButton:0x7571170.top== UIButton:0x886efe0.top>" )
這裏的約束看起來很難理解,應該是打印了相似VFL的約束表示。
我在項目中引入了NSLayoutConstraint+Description,重寫了其description方法,這樣在出錯時能夠容易的閱讀當前出錯的約束狀況,加以鑑別。
附錄:Autolayout出現前的iOS佈局技術
關於旋轉屏:
對於iphone應用開發者來講,絕大部分應用都是不須要支持旋轉屏幕的;蘋果的系統應用卻是有幾個支持轉屏(不過也主要是簡單列表的佈局,支持起來比較容易)。Iphone的長寬比例相差較大,所以若是要獲得一個比較良好的體驗,橫屏和豎屏的佈局通常都須要從新設計,由此帶來了巨大的工做量;從用戶體驗來講也看不到支持橫屏的必要性。所以,對於iphone應用來講,絕大部分應用都是不支持旋轉屏幕的。你們能夠看一下手機裏的流行應用(微信、支付寶、網易新聞等),可能只有一些視頻類應用的個別頁面會支持轉屏(播放視頻橫屏體驗更佳)。
而對於iPad來講,其長寬比例較爲接近(4:3),且蘋果一開始就建議iPad應用開發者適配橫豎屏以提高用戶體驗(橫屏使用iPad對用戶來講很日常,這點和iphone差異較大)。所以,對於iPad開發者來講,由於轉屏場景的存在,佈局適配一直是開發中須要注意的一個重點問題。
固定frame寫法:
寫死frame來佈局,在只有320*480一套分辨率的時候很是happy; frame無需計算; 在iphone5推出以前(2012年9月發佈),寫死frame來佈局UI是很是方便快速的;而對於iphone開發者來講,通常不須要考慮屏幕旋轉的問題(上面咱們已經說了,大部分app是鎖定屏幕方向的)。這個時代的iphone 開發者是很是幸福的(android開發者須要適配碎片化的分辨率)。但iphone五、iphone六、iphone6plus帶來的分辨率碎片化讓固定 frame寫法增長了很大的工做量,且代碼難以閱讀和維護。
Autoresizing技術:
就是在建立視圖的同時給出其相對於父視圖的「對齊方式與縮放係數」,即autoresizingMask。當父視圖發生變化時,經過每一個子視圖的 autoresizingMask便可自動得出子視圖的位置。
然而autoresizingMask的問題在於:
1. 其描述界面變化規則不夠靈活,不少變化規則根本沒法精確描述。autoresizingMask縮放比例是UIKit內部計算的,開發者沒法指定縮放比例的精確值;且通常狀況下,距離每每比比例更加精確(這點你們和設計同窗溝通的時候應該有體會)。
2. 變化規則只能基於父視圖與子視圖之間,沒法創建同級視圖或者跨級視圖之間的關係。
總結:Autoresizing用來簡單的指定和父view之間的關係仍是比較方便的。其在咱們當前的項目中使用的也比較多,但它的侷限性決定了它只能做爲frame佈局的輔助,應用於一些簡單場景,對於複雜多樣化的佈局力不從心。在autolayout問世之前,經過autoresizing也能夠處理一些簡單頁面的旋轉屏佈局(在細節要求不高的狀況下)。