AutoLayout拾遺

最近開(kāi)始看WWDC的視頻恐仑,復(fù)習(xí)了一下AutoLayout的基礎(chǔ)內(nèi)容墓赴,寫(xiě)一點(diǎn)兒總結(jié)吧,免得看完又忘嘱丢。

AutoLayout

AutoLayout的定義

AutoLayout是基于約束的赚哗,描述性的布局系統(tǒng)她紫。使用約束來(lái)描述布局,view的frames會(huì)依據(jù)這些約束自動(dòng)進(jìn)行計(jì)算屿储。
AutoLayout是蘋(píng)果在iOS6中引入的用來(lái)替換之前的“Springs&Struts”布局模型的新布局系統(tǒng)。
“Spring&Struts”是基于frame的布局渐逃,它在大部分情況下還是有用的够掠,但是隨著4寸iPhone5的發(fā)布帶來(lái)的大屏適配的工作以及在橫豎屏切換時(shí)經(jīng)常需要在viewWillLayoutSubviews方法中編寫(xiě)大量布局代碼。相比之下茄菊,AutoLayout不僅可以完成“Spring&Struts”提供的功能疯潭,還提供了其所沒(méi)有的特性:

  • AutoLayout可以指定任意兩個(gè)view的相對(duì)位置赊堪,而不需要像Autoresizing Mask那樣需要兩個(gè)view在直系的view hierarchy中。
  • AutoLayout不必須指定相等關(guān)系的約束竖哩,它可以指定非相等約束(大于或者小于等)哭廉,而Autoresizing Mask所能做的布局只能是相等條件的。
  • AutoLayout可以指定約束的優(yōu)先級(jí)相叁,計(jì)算frame時(shí)將優(yōu)先按照滿足優(yōu)先級(jí)高的條件進(jìn)行計(jì)算遵绰。


    約束的優(yōu)點(diǎn)

如何添加約束

通常我們直接在IB中設(shè)置約束,這里著重記錄下如何用代碼添加約束:
當(dāng)在代碼中創(chuàng)建視圖和它們的約束條件時(shí)候增淹,一定要記得將 translatesAutoResizingMaskIntoConstraints
屬性設(shè)置為 NO椿访。

  1. 我們可以使用NSLayoutConstraint的類方法:+(instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullable id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c創(chuàng)建約束對(duì)象。
  • 注意:上面方程的等號(hào)表示的是相等關(guān)系虑润,而不是賦值成玫。當(dāng)自動(dòng)布局求解這些方程時(shí),并不是將等式右邊的值賦值給等式左邊拳喻。相反哭当,它同時(shí)計(jì)算屬性 1 和屬性 2 的值使它們之間的關(guān)系成立。
  1. 使用UIView對(duì)象的實(shí)例方法:-(void)addConstraint:(NSLayoutConstraint *)constraint 將約束添加到view上冗澈。
    將約束添加到view上時(shí)要注意:
  • 對(duì)于兩個(gè)同層級(jí)view之間的約束關(guān)系钦勘,添加到他們的父view上


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


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


  1. 可以通過(guò)-setNeedsUpdateConstraints(在下一次繪制循環(huán)中觸發(fā)重新布局)和-layoutIfNeeded(強(qiáng)制系統(tǒng)立即更新視圖樹(shù)的布局)兩個(gè)方法來(lái)刷新約束的改變个盆,使UIView重新布局。這和CoreGraphic的-setNeedsDisplay一套東西是一樣的朵栖。

布局過(guò)程

布局過(guò)程
  1. 更新約束:這是自下而上(從子視圖到父視圖)發(fā)生的颊亮,它為布局準(zhǔn)備好必要的信息,而這些布局將在實(shí)際設(shè)置視圖的 frame 時(shí)被傳遞過(guò)去并被使用陨溅。你可以通過(guò)調(diào)用-setNeedsUpdateConstraints來(lái)觸發(fā)這個(gè)操作终惑,同時(shí),你對(duì)約束條件系統(tǒng)做出的任何改變都將自動(dòng)觸發(fā)這個(gè)方法门扇。
  2. 布局:這是個(gè)自上而下(從父視圖到子視圖)的過(guò)程雹有,這種布局操作實(shí)際上是通過(guò)設(shè)置 frame(在 OS X 中)或者 center 和 bounds(在 iOS 中)將約束條件系統(tǒng)的解決方案應(yīng)用到視圖上。你可以通過(guò)調(diào)用-setNeedsLayout 來(lái)觸發(fā)一個(gè)操作請(qǐng)求臼寄,這并不會(huì)立刻應(yīng)用布局霸奕,而是在稍后再進(jìn)行處理。因?yàn)樗械牟季终?qǐng)求將會(huì)被合并到一個(gè)布局操作中去吉拳,所以你不需要為經(jīng)常調(diào)用這個(gè)方法而擔(dān)心质帅。
  3. 顯示:顯示器都會(huì)自上而下將渲染后的視圖傳遞到屏幕上,你也可以通過(guò)調(diào)用 -setNeedsDisplay來(lái)觸發(fā),這將會(huì)導(dǎo)致所有的調(diào)用都被合并到一起推遲重繪煤惩。重寫(xiě)熟悉的 drawRect:能夠讓我們獲得自定義視圖中顯示過(guò)程的所有權(quán)嫉嘀。

每一步都是依賴前一步操作的,如果有任何布局的變化還沒(méi)實(shí)行的話,顯示操作將會(huì)觸發(fā)一個(gè)布局行為魄揉。類似地剪侮,如果約束條件系統(tǒng)中存在沒(méi)有實(shí)行的改變,布局變化也將會(huì)觸發(fā)更新約束條件洛退。
但是這三步并不是單向的瓣俯。基于約束條件的布局是一個(gè)迭代的過(guò)程不狮,布局操作可以基于之前的布局方案來(lái)對(duì)約束做出更改降铸,而這將再次觸發(fā)約束的更新,并緊接另一個(gè)布局操作摇零。這可以被用來(lái)創(chuàng)建高級(jí)的自定義視圖布局推掸,但是如果你每一次調(diào)用的自定義 -layoutSubviews 都會(huì)導(dǎo)致另一個(gè)布局操作的話,你將會(huì)陷入到無(wú)限循環(huán)的麻煩中去驻仅。

控制布局

  1. 通常我們需要在-updateConstraints方法中集中添加約束谅畅,并且確保在你的實(shí)現(xiàn)中增加任何你需要布局子視圖的約束條件之后,調(diào)用一下 [super updateConstraints]噪服,在這個(gè)方法中毡泻,你不會(huì)被允許禁用何約束條件,因?yàn)槟阋呀?jīng)進(jìn)入上面所描述的布局過(guò)程的第一步了粘优。
    如果稍后一個(gè)失效的約束條件發(fā)生了改變的話仇味,你需要立刻移除這個(gè)約束并調(diào)用 -setNeedsUpdateConstraints事實(shí)上,僅在這種情況下你需要觸發(fā)更新約束條件的操作雹顺。
  2. 如果你不能利用布局約束條件達(dá)到子視圖預(yù)期的布局丹墨,你可以進(jìn)一步重寫(xiě) -layoutSubviews,通過(guò)這種方式當(dāng)約束條件系統(tǒng)得到解決并且結(jié)果將要被應(yīng)用到視圖中時(shí),你便已經(jīng)進(jìn)入到布局過(guò)程的第二步了嬉愧。

Intrinsic Content Size

一些視圖依據(jù)給定的內(nèi)容有一個(gè)原生的大小贩挣,這就稱為它們的固有內(nèi)容尺寸。并不是所有視圖都有固有內(nèi)容尺寸:


為了在自定義視圖中實(shí)現(xiàn)固有內(nèi)容尺寸没酣,你需要做兩件事:重寫(xiě) -intrinsicContentSize為內(nèi)容返回恰當(dāng)?shù)拇笮⊥醪疲瑹o(wú)論何時(shí)有任何會(huì)影響固有內(nèi)容尺寸的改變發(fā)生時(shí),調(diào)用 invalidateIntrinsicContentSize裕便。如果這個(gè)視圖只有一個(gè)方向的尺寸設(shè)置了固有尺寸绒净,那么為另一個(gè)方向的尺寸返回 UIViewNoIntrinsicMetric/NSViewNoIntrinsicMetric

注意當(dāng)為了填充一個(gè)空間需要拉伸所有視圖時(shí)偿衰,如垂直擺放的兩個(gè)textview疯溺,如果它們都有一個(gè)相同的內(nèi)容壓縮優(yōu)先級(jí)论颅,這個(gè)布局就會(huì)是有歧義的哎垦。自動(dòng)布局不知道該拉伸哪個(gè)視圖囱嫩,這時(shí)就應(yīng)該調(diào)整某個(gè)textview的content-hugging priority與 compression-resistance priority。

Alignment Rect


自動(dòng)布局并不會(huì)操作視圖的 frame漏设,但能作用于視圖的 alignment rect墨闲,在很多情況下,它們是相同的郑口。你可以通過(guò)重寫(xiě)alignmentRectForFrame:frameForAlignmentRect
這兩個(gè)方法在frame與alignment rect之間轉(zhuǎn)換鸳碧。
當(dāng)你要自定義一個(gè)控件的時(shí)候可以重寫(xiě)alignmentRectInsets方法,這個(gè)方法允許你返回相對(duì)于 frame 的 edge insets犬性。

Debugging


對(duì)于不能確定的布局瞻离,可以通過(guò)調(diào)試時(shí)暫停程序,在debugger中輸入(lldb)po [[UIWindow keyWindow] _autolayoutTrace]來(lái)遍歷視圖層乒裆,檢查錯(cuò)誤套利。
檢查是否有ambiguity:(lldb)po [view hasAmbiguousLayout]
哪里發(fā)生了ambiguous:(lldb)po [view excerciseAmbiguousInLayout]

視圖消失時(shí)如何檢查布局錯(cuò)誤

動(dòng)畫(huà)

使用AutoLayout來(lái)做動(dòng)畫(huà)
  • CoreAnimation:
[UIView animateWithDuration:0.5 animations:^{
    [view layoutIfNeeded];
}];

請(qǐng)注意,使用這種方法鹤耍,你可以對(duì)約束條件做出的改變并不局限于約束條件的常量肉迫。你可以刪除約束條件,增加約束條件稿黄,甚至使用臨時(shí)動(dòng)畫(huà)約束條件喊衫。由于新的約束只被解釋一次來(lái)決定新的 frames,所以更復(fù)雜的布局改變都是有可能的杆怕。
需要記住的是:Core Animation 和 Auto Layout 結(jié)合在一起產(chǎn)生視圖動(dòng)畫(huà)時(shí)族购,自己不要接觸視圖的 frame。一旦視圖使用自動(dòng)布局陵珍,那么你已經(jīng)將設(shè)置 frame 的責(zé)任交給了布局系統(tǒng)寝杖。你的干擾將造成怪異的行為。

  • 直接對(duì)約束條件做動(dòng)畫(huà)
    約束條件一旦創(chuàng)建后撑教,只有其常量可以被改變朝墩。

UIStackView

StackView是iOS9中提供的一種簡(jiǎn)單布局控件,剔除了復(fù)雜的約束伟姐,利用自動(dòng)布局的強(qiáng)大來(lái)布局界面收苏,單個(gè) StackView 由一行或者一列控件組成,StackView 根據(jù)設(shè)置的對(duì)齊愤兵,間距和大小屬性來(lái)決定subviews的位置鹿霸。
同時(shí),StackView也可嵌套來(lái)構(gòu)建更復(fù)雜的布局秆乳。


AutoLayout和UIScrollView

在使用scroll view時(shí)懦鼠,不僅要定義它的大小跟位置钻哩,還要確定它的內(nèi)容的大小肛冶!可按如下步驟來(lái)對(duì)scroll view進(jìn)行布局:


Your layout must fully define the size of the content view (except where defined in steps 5 and 6).To set the height based on the intrinsic size of your content, you must have an unbroken chain of constraints and views stretching from the content view’s top edge to its bottom edge.
If your content does not have an intrinsic content size, you must add the appropriate size constraints, either to the content view or to the content.

AutoLayout和Self-Sizing Table View Cells

在iOS8之后街氢,可以下面這種簡(jiǎn)單的方式來(lái)對(duì)動(dòng)態(tài)變化的Table View Cell的高度進(jìn)行自動(dòng)計(jì)算:

tableView.estimatedRowHeight = 85.0
tableView.rowHeight = UITableViewAutomaticDimension

當(dāng)然,前提是你的cell中的內(nèi)容的約束已經(jīng)從上至下形成一個(gè)閉環(huán)睦袖。AutoLayout可由此可以推算出cell的高度珊肃。

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市馅笙,隨后出現(xiàn)的幾起案子伦乔,更是在濱河造成了極大的恐慌,老刑警劉巖董习,帶你破解...
    沈念sama閱讀 212,686評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件烈和,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡皿淋,警方通過(guò)查閱死者的電腦和手機(jī)招刹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,668評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)沥匈,“玉大人蔗喂,你說(shuō)我怎么就攤上這事「咛” “怎么了缰儿?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,160評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)散址。 經(jīng)常有香客問(wèn)我乖阵,道長(zhǎng),這世上最難降的妖魔是什么预麸? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,736評(píng)論 1 284
  • 正文 為了忘掉前任瞪浸,我火速辦了婚禮,結(jié)果婚禮上吏祸,老公的妹妹穿的比我還像新娘对蒲。我一直安慰自己,他們只是感情好贡翘,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,847評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布蹈矮。 她就那樣靜靜地躺著,像睡著了一般鸣驱。 火紅的嫁衣襯著肌膚如雪泛鸟。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,043評(píng)論 1 291
  • 那天踊东,我揣著相機(jī)與錄音北滥,去河邊找鬼刚操。 笑死,一個(gè)胖子當(dāng)著我的面吹牛再芋,可吹牛的內(nèi)容都是我干的菊霜。 我是一名探鬼主播,決...
    沈念sama閱讀 39,129評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼祝闻,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼占卧!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起联喘,我...
    開(kāi)封第一講書(shū)人閱讀 37,872評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎辙纬,沒(méi)想到半個(gè)月后豁遭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,318評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贺拣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,645評(píng)論 2 327
  • 正文 我和宋清朗相戀三年蓖谢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片譬涡。...
    茶點(diǎn)故事閱讀 38,777評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡闪幽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出涡匀,到底是詐尸還是另有隱情盯腌,我是刑警寧澤,帶...
    沈念sama閱讀 34,470評(píng)論 4 333
  • 正文 年R本政府宣布陨瘩,位于F島的核電站腕够,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏舌劳。R本人自食惡果不足惜帚湘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,126評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望甚淡。 院中可真熱鬧大诸,春花似錦、人聲如沸贯卦。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,861評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)脸侥。三九已至建邓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間睁枕,已是汗流浹背官边。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,095評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工沸手, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人注簿。 一個(gè)月前我還...
    沈念sama閱讀 46,589評(píng)論 2 362
  • 正文 我出身青樓契吉,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親诡渴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子捐晶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,687評(píng)論 2 351

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