【iOS開發】多屏尺的自動適配 AutoLayout (純代碼方式)

關於AutoLayout,最先從iOS6開始引入使用。html

 
主要功能是使用約束,對視圖進行相對佈局,以適應不一樣屏尺的變換。
 
網上大量的資料都在介紹xib和storyboard,如何使用AutoLayout,說純代碼使用AutoLayout進行UI佈局的愈來愈少。對於我這個習慣了代碼UI佈局的人,寫個備忘:
 

AutoLayout是什麼?

使用一句Apple的官方定義的話
AutoLayout是一種基於約束的,描述性的佈局系統。 Auto Layout Is a Constraint-Based, Descriptive Layout System.
關鍵詞:
  • 基於約束 - 和以往定義frame的位置和尺寸不一樣,AutoLayout的位置肯定是以所謂相對位置的約束來定義的,好比x座標爲superView的中心,y座標爲屏幕底部上方10像素等
  • 描述性 - 約束的定義和各個view的關係使用接近天然語言或者可視化語言(稍後會提到)的方法來進行描述
  • 佈局系統 - 即字面意思,用來負責界面的各個元素的位置。
總而言之,AutoLayout爲開發者提供了一種不一樣於傳統對於UI元素位置指定的佈局方法。之前,不管是在IB裏拖放,仍是在代碼中寫,每一個UIView都會有本身的frame屬性,來定義其在當前視圖中的位置和尺寸。使用AutoLayout的話,就變爲了使用約束條件來定義view的位置和尺寸。這樣的最大好處是一舉解決了不一樣分辨率和屏幕尺寸下view的適配問題,另外也簡化了旋轉時view的位置的定義,原來在底部之上10像素居中的view,不論在旋轉屏幕或是更換設備(iPad或者iPhone5或者之後可能出現的mini iPad)的時候,始終還在底部之上10像素居中的位置,不會發生變化。 總結
使用約束條件來描述佈局,view的frame會依據這些約束來進行計算 Describe the layout with constraints, and frames are calculated automatically.

AutoLayout和Autoresizing Mask的區別

Autoresizing Mask是咱們的老朋友了…若是你之前一直是代碼寫UI的話,你確定寫過UIViewAutoresizingFlexibleWidth之類的枚舉;若是你之前用IB比較多的話,必定注意到過每一個view的size inspector中都有一個紅色線條的Autoresizing的指示器和相應的動畫縮放的示意圖,這就是Autoresizing Mask。在iOS6以前,關於屏幕旋轉的適配和iPhone,iPad屏幕的自動適配,基本都是由Autoresizing Mask來完成的。可是隨着你們對iOS app的要求愈來愈高,以及已經以及從此可能出現的多種屏幕和分辨率的設備來講,Autoresizing Mask顯得有些落伍和遲鈍了。AutoLayout能夠完成全部原來Autoresizing Mask能完成的工做,同時還可以勝任一些原來沒法完成的任務,其中包括:
  • AutoLayout能夠指定任意兩個view的相對位置,而不須要像Autoresizing Mask那樣須要兩個view在直系的view hierarchy中。
  • AutoLayout沒必要須指定相等關係的約束,它能夠指定非相等約束(大於或者小於等);而Autoresizing Mask所能作的佈局只能是相等條件的。
  • AutoLayout能夠指定約束的優先級,計算frame時將優先按照知足優先級高的條件進行計算。
總結
Autoresizing Mask是AutoLayout的子集,任何能夠用Autoresizing Mask完成的工做均可以用AutoLayout完成。AutoLayout還具有一些Autoresizing Mask不具有的優良特性,以幫助咱們更方便地構建界面。

AutoLayout基本使用方法

Interface Builder

這部分網上大量的教程,都是說的這個

手動使用API添加約束

建立

iOS6中新加入了一個類:NSLayoutConstraint,一個形如這樣的約束
  • item1.attribute = multiplier ? item2.attribute + constant
對應的代碼爲
1 [NSLayoutConstraint constraintWithItem:button
2                              attribute:NSLayoutAttributeBottom
3                              relatedBy:NSLayoutRelationEqua
4                                 toItem:superview
5                              attribute:NSLayoutAttributeBottom
6                             multiplier:1.0
7                               constant:-padding]
這對應的約束是「button的底部(y) = superview的底部 -10」。

添加

在建立約束以後,須要將其添加到做用的view上。UIView(固然NSView也同樣)加入了一個新的實例方法:
  • -(void)addConstraint:(NSLayoutConstraint *)constraint;
用來將約束添加到view。在添加時惟一要注意的是添加的目標view要遵循如下規則:
  • 對於兩個同層級view之間的約束關係,添加到他們的父view上
 
  • 對於兩個不一樣層級view之間的約束關係,添加到他們最近的共同父view上
 
  • 對於有層次關係的兩個view之間的約束關係,添加到層次較高的父view上
 

刷新

能夠經過-setNeedsUpdateConstraints和-layoutIfNeeded兩個方法來刷新約束的改變,使UIView從新佈局。這和CoreGraphic的-setNeedsDisplay一套東西是同樣的~

Visual Format Language 可視格式語言

UIKit團隊此次至關有愛,估計他們本身也以爲新加約束的API名字太長了,所以他們發明了一種新的方式來描述約束條件,十分有趣。這種語言是對視覺描述的一種抽象,大概過程看起來是這樣的: accept按鈕在cancel按鈕右側默認間距處 
 


最後使用VFL(Visual Format Language)描述變成這樣:
1 [NSLayoutConstraint constraintsWithVisualFormat:@\\"[cancelButton]-[acceptButton]\"
2                                         options:0
3                                         metrics:nil
4                                           views:viewsDictionary];
其中viewsDictionary是綁定了view的名字和對象的字典,對於這個例子能夠用如下方法獲得對應的字典:
1 UIButton *cancelButton = ...
2 UIButton *acceptButton = ...
3 viewsDictionary = NSDictionaryOfVariableBindings(cancelButton,acceptButton);
生成的字典爲
{ acceptButton = ""; cancelButton = ""; }
固然,不嫌累的話本身手寫也何嘗不可。如今字典啊數組啊寫法相對簡化了不少了,所以也不復雜。關於Objective-C的新語法,能夠參考我以前的一篇WWDC 2012筆記: WWDC 2012 Session筆記——405 Modern Objective-C。 在view名字後面添加括號以及鏈接處的數字能夠賦予表達式更多意義,如下進行一些舉例:
  • [cancelButton(72)]-12-[acceptButton(50)]
    • 取消按鈕寬72point,accept按鈕寬50point,它們之間間距12point
  • [wideView(>=60@700)]
    • wideView寬度大於等於60point,該約束條件優先級爲700(優先級最大值爲1000,優先級越高的約束越先被知足)
  • V:[redBox][yellowBox(==redBox)]
    • 豎直佈局,先是一個redBox,其下方緊接一個寬度等於redBox寬度的yellowBox
  • H:|-[Find]-[FindNext]-[FindField(>=20)]-|
    • 水平佈局,Find距離父view左邊緣默認間隔寬度,以後是FindNext距離Find間隔默認寬度;再以後是寬度不小於20的FindField,它和FindNext以及父view右邊緣的間距都是默認寬度。(豎線'|‘ 表示superview的邊緣)

容易出現的錯誤

由於涉及約束問題,所以約束模型下的全部可能出現的問題這裏都會出現,具體來講包括兩種:
  • Ambiguous Layout 佈局不能肯定
  • Unsatisfiable Constraints 沒法知足約束
佈局不能肯定指的是給出的約束條件沒法惟一肯定一種佈局,也即約束條件不足,沒法獲得惟一的佈局結果。這種狀況通常添加一些必要的約束或者調整優先級能夠解決。沒法知足約束的問題來源是有約束條件互相沖突,所以沒法同時知足,須要刪掉一些約束。兩種錯誤在出現時均會致使佈局的不穩定和錯誤,Ambiguous能夠被容忍而且選擇一種可行佈局呈如今UI上,Unsatisfiable的話會沒法獲得UI佈局並報錯。 對於不能肯定的佈局,能夠經過調試時暫停程序,在debugger中輸入
  • po [[UIWindow keyWindow] _autolayoutTrace]
來檢查是否存在Ambiguous Layout以及存在的位置,來幫助添加條件。另外還有一些檢查方法,來查看view的約束和約束狀態:
  • [view constraintsAffectingLayoutForOrientation/Axis: NSLayoutConstraintOrientationHorizontal/Vertical]
  • [view hasAmbiguousLayout]
    • [view exerciseAmbiguityInLayout]

佈局動畫

動畫是UI體驗的重要部分,更改佈局之後的動畫也很是關鍵。說到動畫,Core Animation又立功了..自從CA出現之後,全部的動畫效果都很是cheap,在auto layout中狀況也和collection view裏同樣,很簡單(能夠參考 WWDC 2012 Session筆記——219 Advanced Collection Views and Building Custom Layouts),只須要把layoutIfNeeded放到animation block中便可~
1 [UIView animateWithDuration:0.5 animations:^{
2     [view layoutIfNeeded];
3 }];
 
 

部分代碼

純淨代碼UI正常佈局後,添加autolayout就能夠了,調整至關方便
這是一段水平居中,垂直並列的4個按鈕 佈局代碼
setTranslatesAutoresizingMaskIntoConstraints  是爲no,開啓AutoLayou.

    //-----autoLayoutgit

    [_btn_1 setTranslatesAutoresizingMaskIntoConstraints:NO];github

    [_btn_2 setTranslatesAutoresizingMaskIntoConstraints:NO];objective-c

    [_btn_3 setTranslatesAutoresizingMaskIntoConstraints:NO];數組

    [_btn_4 setTranslatesAutoresizingMaskIntoConstraints:NO];app

    

    CGSize winSize = [[iHappySDKSingle shareSingle] getScreenSize];ide

    CGFloat tpo = _btn_1.frame.origin.y;函數

    CGFloat hpod = _btn_1.frame.origin.x;佈局

    CGFloat btnH = _btn_1.frame.size.height;動畫

    CGFloat vpod = winSize.width*0.15-btnH;

    

    NSNumber* tp = [NSNumber numberWithFloat:tpo];

    NSNumber* hd = [NSNumber numberWithFloat:hpod];

    NSNumber* vd = [NSNumber numberWithFloat:vpod];

    NSNumber* bh = [NSNumber numberWithFloat:btnH];

    NSNumber* btm = [NSNumber numberWithFloat:vpod*2];

    

    NSDictionary *dict1 = NSDictionaryOfVariableBindings(_btn_1,_btn_2,_btn_3,_btn_4);

    NSDictionary *metrics =@{@"hPadding":hd,@"vPadding":vd,@"top":tp,@"btm":btm,@"btnHeight":bh};

    NSString *vfl1 = @"|-hPadding-[_btn_1]-hPadding-|";

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl1

  options:0

  metrics:metrics

views:dict1]];

    NSString *vfl2 = @"|-hPadding-[_btn_2]-hPadding-|";

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl2

  options:0

  metrics:metrics

views:dict1]];

    NSString *vfl3 = @"|-hPadding-[_btn_3]-hPadding-|";

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl3

  options:0

  metrics:metrics

views:dict1]];

    NSString *vfl4 = @"|-hPadding-[_btn_4]-hPadding-|";

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl4

  options:0

  metrics:metrics

views:dict1]];

    NSString *vfl5 = @"V:|-(<=top)-[_btn_1(btnHeight)]-vPadding-[_btn_2(btnHeight)]-vPadding-[_btn_3(btnHeight)]-vPadding-[_btn_4(btnHeight)]-(>=btm)-|";

    if (_btn_1.hidden) {

        vfl5 = @"V:|-(<=top)-[_btn_2(btnHeight)]-vPadding-[_btn_3(btnHeight)]-vPadding-[_btn_4(btnHeight)]-(>=btm)-|";

    }

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl5

  options:0

  metrics:metrics

views:dict1]];

 
純淨代碼UI正常佈局後,增長一個函數,進行自動佈局
水平居中佈局:NSLayoutAttributeCenterX
垂直居中佈局:NSLayoutAttributeCenterY
以及後面的佈局切換動畫。

- (void)setAutoLayoutForKuang:(UIView*)imgv

{

    UIView * view = self;

    [imgv setTranslatesAutoresizingMaskIntoConstraints:NO];

    NSDictionary *dict1 = NSDictionaryOfVariableBindings(imgv);

    NSDictionary *metrics = @{@"width":[NSNumbernumberWithFloat:imgv.frame.size.width],

                              @"height":[NSNumber numberWithFloat:imgv.frame.size.height],

                              @"top":[NSNumber numberWithFloat:imgv.frame.origin.y]

                              };

    NSString *vfl1 = @"[imgv(width)]";

[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl1

  options:0

  metrics:metrics

views:dict1]];

    NSString *vfl2 = @"V:[imgv(height)]";

[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl2

  options:0

  metrics:metrics

views:dict1]];

    [view addConstraint:[NSLayoutConstraint constraintWithItem:imgvattribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:viewattribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];

    [view addConstraint:[NSLayoutConstraint constraintWithItem:imgvattribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:viewattribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];

    //animation

    [UIView animateWithDuration:0.25 animations:^{

        [imgv layoutIfNeeded];

    }];

}

 
-----------------------------------------
網上相關文章:
-----------------------------------------
一、 AutoLayout(自動佈局)入門    推薦
二、 Autolayout及VFL經驗分享      此文包含有一個demo( 快速訪問下載) 推薦
 
不是朋友在發愁手動些這些相似腳本的字符:
如今推薦一個開源庫給你們
相關文章
相關標籤/搜索