IOS-多屏尺的自動(dòng)適配 AutoLayout (純代碼方式)

主要功能是使用約束址否,對視圖進(jìn)行相對布局榕莺,以適應(yīng)不同屏尺的變換。

網(wǎng)上大量的資料都在介紹xib和storyboard麸祷,如何使用AutoLayout,說純代碼使用AutoLayout進(jìn)行UI布局的越來越少褒搔。對于我這個(gè)習(xí)慣了代碼UI布局的人阶牍,寫個(gè)備忘:

AutoLayout是什么?

使用一句Apple的官方定義的話

AutoLayout是一種基于約束的星瘾,描述性的布局系統(tǒng)走孽。 Auto Layout Is a Constraint-Based, Descriptive Layout System.

關(guān)鍵詞:

基于約束 - 和以往定義frame的位置和尺寸不同,AutoLayout的位置確定是以所謂相對位置的約束來定義的琳状,比如x坐標(biāo)為superView的中心磕瓷,y坐標(biāo)為屏幕底部上方10像素等

描述性 - 約束的定義和各個(gè)view的關(guān)系使用接近自然語言或者可視化語言(稍后會提到)的方法來進(jìn)行描述

布局系統(tǒng) - 即字面意思,用來負(fù)責(zé)界面的各個(gè)元素的位置念逞。

總而言之困食,AutoLayout為開發(fā)者提供了一種不同于傳統(tǒng)對于UI元素位置指定的布局方法。以前翎承,不論是在IB里拖放硕盹,還是在代碼中寫,每個(gè)UIView都會有自己的frame屬性叨咖,來定義其在當(dāng)前視圖中的位置和尺寸瘩例。使用AutoLayout的話啊胶,就變?yōu)榱耸褂眉s束條件來定義view的位置和尺寸。這樣的最大好處是一舉解決了不同分辨率和屏幕尺寸下view的適配問題垛贤,另外也簡化了旋轉(zhuǎn)時(shí)view的位置的定義焰坪,原來在底部之上10像素居中的view,不論在旋轉(zhuǎn)屏幕或是更換設(shè)備(iPad或者iPhone5或者以后可能出現(xiàn)的mini iPad)的時(shí)候聘惦,始終還在底部之上10像素居中的位置某饰,不會發(fā)生變化。 總結(jié)

使用約束條件來描述布局善绎,view的frame會依據(jù)這些約束來進(jìn)行計(jì)算 Describe the layout with constraints, and frames are calculated automatically.

AutoLayout和Autoresizing Mask的區(qū)別

Autoresizing Mask是我們的老朋友了…如果你以前一直是代碼寫UI的話露乏,你肯定寫過UIViewAutoresizingFlexibleWidth之類的枚舉;如果你以前用IB比較多的話涂邀,一定注意到過每個(gè)view的size inspector中都有一個(gè)紅色線條的Autoresizing的指示器和相應(yīng)的動(dòng)畫縮放的示意圖瘟仿,這就是Autoresizing Mask。在iOS6之前比勉,關(guān)于屏幕旋轉(zhuǎn)的適配和iPhone劳较,iPad屏幕的自動(dòng)適配,基本都是由Autoresizing Mask來完成的浩聋。但是隨著大家對iOS app的要求越來越高观蜗,以及已經(jīng)以及今后可能出現(xiàn)的多種屏幕和分辨率的設(shè)備來說,Autoresizing Mask顯得有些落伍和遲鈍了衣洁。AutoLayout可以完成所有原來Autoresizing Mask能完成的工作墓捻,同時(shí)還能夠勝任一些原來無法完成的任務(wù),其中包括:

AutoLayout可以指定任意兩個(gè)view的相對位置坊夫,而不需要像Autoresizing Mask那樣需要兩個(gè)view在直系的view hierarchy中砖第。

AutoLayout不必須指定相等關(guān)系的約束,它可以指定非相等約束(大于或者小于等)环凿;而Autoresizing Mask所能做的布局只能是相等條件的梧兼。

AutoLayout可以指定約束的優(yōu)先級,計(jì)算frame時(shí)將優(yōu)先按照滿足優(yōu)先級高的條件進(jìn)行計(jì)算智听。

總結(jié)

Autoresizing Mask是AutoLayout的子集羽杰,任何可以用Autoresizing Mask完成的工作都可以用AutoLayout完成。AutoLayout還具備一些Autoresizing Mask不具備的優(yōu)良特性到推,以幫助我們更方便地構(gòu)建界面考赛。

AutoLayout基本使用方法

Interface Builder

這部分網(wǎng)上大量的教程,都是說的這個(gè)

手動(dòng)使用API添加約束

創(chuàng)建

iOS6中新加入了一個(gè)類:NSLayoutConstraint莉测,一個(gè)形如這樣的約束

item1.attribute = multiplier ? item2.attribute + constant

對應(yīng)的代碼為

[NSLayoutConstraint constraintWithItem:button

attribute:NSLayoutAttributeBottom

relatedBy:NSLayoutRelationEqua

toItem:superview

attribute:NSLayoutAttributeBottom

multiplier:1.0

constant:-padding]

這對應(yīng)的約束是“button的底部(y) = superview的底部 -10”颜骤。

添加

在創(chuàng)建約束之后,需要將其添加到作用的view上悔雹。UIView(當(dāng)然NSView也一樣)加入了一個(gè)新的實(shí)例方法:

-(void)addConstraint:(NSLayoutConstraint *)constraint;

用來將約束添加到view复哆。在添加時(shí)唯一要注意的是添加的目標(biāo)view要遵循以下規(guī)則:

對于兩個(gè)同層級view之間的約束關(guān)系,添加到他們的父view上

對于兩個(gè)不同層級view之間的約束關(guān)系腌零,添加到他們最近的共同父view上

對于有層次關(guān)系的兩個(gè)view之間的約束關(guān)系梯找,添加到層次較高的父view上

刷新

可以通過-setNeedsUpdateConstraints和-layoutIfNeeded兩個(gè)方法來刷新約束的改變,使UIView重新布局益涧。這和CoreGraphic的-setNeedsDisplay一套東西是一樣的~

Visual Format Language 可視格式語言

UIKit團(tuán)隊(duì)這次相當(dāng)有愛锈锤,估計(jì)他們自己也覺得新加約束的API名字太長了,因此他們發(fā)明了一種新的方式來描述約束條件闲询,十分有趣久免。這種語言是對視覺描述的一種抽象,大概過程看起來是這樣的: accept按鈕在cancel按鈕右側(cè)默認(rèn)間距處

最后使用VFL(Visual Format Language)描述變成這樣:

[NSLayoutConstraint constraintsWithVisualFormat:@\\"[cancelButton]-[acceptButton]\"

options:0

metrics:nil

views:viewsDictionary];

其中viewsDictionary是綁定了view的名字和對象的字典扭弧,對于這個(gè)例子可以用以下方法得到對應(yīng)的字典:

UIButton *cancelButton = ...

UIButton *acceptButton = ...

viewsDictionary = NSDictionaryOfVariableBindings(cancelButton,acceptButton);生成的字典為

{ acceptButton = ""; cancelButton = ""; }

當(dāng)然阎姥,不嫌累的話自己手寫也未嘗不可。現(xiàn)在字典啊數(shù)組啊寫法相對簡化了很多了鸽捻,因此也不復(fù)雜呼巴。 在view名字后面添加括號以及連接處的數(shù)字可以賦予表達(dá)式更多意義,以下進(jìn)行一些舉例:

[cancelButton(72)]-12-[acceptButton(50)]

取消按鈕寬72point御蒲,accept按鈕寬50point衣赶,它們之間間距12point

[wideView(>=60@700)]

wideView寬度大于等于60point,該約束條件優(yōu)先級為700(優(yōu)先級最大值為1000厚满,優(yōu)先級越高的約束越先被滿足)

V:[redBox][yellowBox(==redBox)]

豎直布局府瞄,先是一個(gè)redBox,其下方緊接一個(gè)寬度等于redBox寬度的yellowBox

H:|-[Find]-[FindNext]-[FindField(>=20)]-|

水平布局碘箍,F(xiàn)ind距離父view左邊緣默認(rèn)間隔寬度遵馆,之后是FindNext距離Find間隔默認(rèn)寬度;再之后是寬度不小于20的FindField丰榴,它和FindNext以及父view右邊緣的間距都是默認(rèn)寬度团搞。(豎線'|‘ 表示superview的邊緣)

容易出現(xiàn)的錯(cuò)誤

因?yàn)樯婕凹s束問題,因此約束模型下的所有可能出現(xiàn)的問題這里都會出現(xiàn)多艇,具體來說包括兩種:

Ambiguous Layout 布局不能確定

Unsatisfiable Constraints 無法滿足約束

布局不能確定指的是給出的約束條件無法唯一確定一種布局逻恐,也即約束條件不足,無法得到唯一的布局結(jié)果峻黍。這種情況一般添加一些必要的約束或者調(diào)整優(yōu)先級可以解決复隆。無法滿足約束的問題來源是有約束條件互相沖突,因此無法同時(shí)滿足姆涩,需要?jiǎng)h掉一些約束挽拂。兩種錯(cuò)誤在出現(xiàn)時(shí)均會導(dǎo)致布局的不穩(wěn)定和錯(cuò)誤,Ambiguous可以被容忍并且選擇一種可行布局呈現(xiàn)在UI上骨饿,Unsatisfiable的話會無法得到UI布局并報(bào)錯(cuò)亏栈。 對于不能確定的布局台腥,可以通過調(diào)試時(shí)暫停程序,在debugger中輸入

po [[UIWindow keyWindow] _autolayoutTrace]

來檢查是否存在Ambiguous Layout以及存在的位置绒北,來幫助添加條件黎侈。另外還有一些檢查方法,來查看view的約束和約束狀態(tài):

[view constraintsAffectingLayoutForOrientation/Axis: NSLayoutConstraintOrientationHorizontal/Vertical]

[view hasAmbiguousLayout]

[view exerciseAmbiguityInLayout]

布局動(dòng)畫

動(dòng)畫是UI體驗(yàn)的重要部分闷游,更改布局以后的動(dòng)畫也非常關(guān)鍵峻汉。說到動(dòng)畫,Core Animation又立功了..自從CA出現(xiàn)以后脐往,所有的動(dòng)畫效果都非常cheap休吠,在auto layout中情況也和collection view里一樣,很簡單业簿,只需要把layoutIfNeeded放到animation block中即可~

[UIView animateWithDuration:0.5 animations:^{

[view layoutIfNeeded];

}];

部分代碼

純凈代碼UI正常布局后瘤礁,添加autolayout就可以了,調(diào)整相當(dāng)方便

這是一段水平居中梅尤,垂直并列的4個(gè)按鈕 布局代碼

setTranslatesAutoresizingMaskIntoConstraints? 是為no蔚携,開啟AutoLayou.

//-----autoLayout

[_btn_1 setTranslatesAutoresizingMaskIntoConstraints:NO];

[_btn_2 setTranslatesAutoresizingMaskIntoConstraints:NO];

[_btn_3 setTranslatesAutoresizingMaskIntoConstraints:NO];

[_btn_4 setTranslatesAutoresizingMaskIntoConstraints:NO];

CGSize winSize = [[iHappySDKSingle shareSingle] getScreenSize];

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正常布局后,增加一個(gè)函數(shù)克饶,進(jìn)行自動(dòng)布局

水平居中布局:NSLayoutAttributeCenterX

垂直居中布局:NSLayoutAttributeCenterY

以及后面的布局切換動(dòng)畫酝蜒。

- (void)setAutoLayoutForKuang:(UIView*)imgv

{

UIView * view = self;

[imgv setTranslatesAutoresizingMaskIntoConstraints:NO];

NSDictionary *dict1 = NSDictionaryOfVariableBindings(imgv);

NSDictionary *metrics = @{@"width":[NSNumber numberWithFloat: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:imgv attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];

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

//animation

[UIView animateWithDuration:0.25 animations:^{

[imgv layoutIfNeeded];

}];

}

通過借鑒前輩的經(jīng)驗(yàn)自己領(lǐng)悟并加上自己的見解,希望對新人們有所幫助矾湃。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末亡脑,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子邀跃,更是在濱河造成了極大的恐慌霉咨,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拍屑,死亡現(xiàn)場離奇詭異途戒,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)僵驰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門喷斋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蒜茴,你說我怎么就攤上這事星爪。” “怎么了粉私?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵顽腾,是天一觀的道長。 經(jīng)常有香客問我诺核,道長抄肖,這世上最難降的妖魔是什么久信? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮漓摩,結(jié)果婚禮上裙士,老公的妹妹穿的比我還像新娘。我一直安慰自己幌甘,他們只是感情好潮售,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布痊项。 她就那樣靜靜地躺著锅风,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鞍泉。 梳的紋絲不亂的頭發(fā)上皱埠,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機(jī)與錄音咖驮,去河邊找鬼边器。 笑死,一個(gè)胖子當(dāng)著我的面吹牛托修,可吹牛的內(nèi)容都是我干的忘巧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼睦刃,長吁一口氣:“原來是場噩夢啊……” “哼砚嘴!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起涩拙,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤际长,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后兴泥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體工育,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年搓彻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了如绸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡旭贬,死狀恐怖竭沫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情骑篙,我是刑警寧澤蜕提,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布案训,位于F島的核電站布轿,受9級特大地震影響球凰,放射性物質(zhì)發(fā)生泄漏虐沥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一脏榆、第九天 我趴在偏房一處隱蔽的房頂上張望猖毫。 院中可真熱鬧,春花似錦须喂、人聲如沸吁断。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽仔役。三九已至,卻和暖如春是己,著一層夾襖步出監(jiān)牢的瞬間又兵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工卒废, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留沛厨,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓摔认,卻偏偏與公主長得像逆皮,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子参袱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容