3-4 Views--先進的自動布局工具箱

自動布局在 OS X 10.7 中被引進,一年后在 iOS 6 中也可以用了。不久在 iOS 7 中的程序將會有望設置全局字體大小,因此除了不同的屏幕大小和方向,用戶界面布局也需要更大的靈活性。Apple 也在自動布局上花了很大功夫,所以如果你還沒做過這一塊,那么現在就是接觸這個技術的好時機。

很多開發者在第一次嘗試使用這個技術時都非常掙扎,因為用 Xcode 4 的 Interface Builder 建立 constraint-based layouts 體驗非常糟糕,但不要因為這個而灰心。自動布局其實比現在 Interface Builder 所支持的要好很多。Xcode 5 在這塊中將會帶來重要的變化。

這篇文章不是用來介紹 Auto Layout 的。如果你還沒用過它,那還是先去 WWDC 2012 看看基礎教程吧。(202 – Introduction to Auto Layout for iOS and OS X,228 – Best Practices for Mastering Auto Layout,232 – Auto Layout by Example)。

相反我們會專注于一些高級的使用技巧和方法,這將會讓你使用自動布局的時候效率更高,(開發)生活更幸福。大多數內容在 WWDC 會議中都有提到,但它們都是在日常工作中容易被審查或遺忘的。

布局過程

首先我們總結一下自動布局將視圖顯示到屏幕上的步驟。當你根據自動布局盡力寫出你想要的布局種類時,特別是高級的使用情況和動畫,這有利于后退一步,并回憶布局過程是怎么工作的。

和 springs,struts 比起來,在視圖被顯示之前,自動布局引入了兩個額外的步驟:更新約束 (updating constraints) 和布局視圖 (laying out views)。每一步都是依賴前一步操作的;顯示依賴于布局視圖,布局視圖依賴于更新約束。

第一步:更新約束,可以被認為是一個“計量傳遞 (measurement pass)”。這是自下而上(從子視圖到父視圖)發生的,它為布局準備好必要的信息,而這些布局將在實際設置視圖的 frame 時被傳遞過去并被使用。你可以通過調用 setNeedsUpdateConstraints 來觸發這個操作,同時,你對約束條件系統做出的任何改變都將自動觸發這個方法。無論如何,通知自動布局關于自定義視圖中任何可能影響布局的改變是非常有用的。談到自定義視圖,你可以在這個階段重寫 updateConstraints 來為你的視圖增加需要的本地約束。

第二步:布局,這是個自上而下(從父視圖到子視圖)的過程,這種布局操作實際上是通過設置 frame(在 OS X 中)或者 center 和 bounds(在 iOS 中)將約束條件系統的解決方案應用到視圖上。你可以通過調用 setNeedsLayout 來觸發一個操作請求,這并不會立刻應用布局,而是在稍后再進行處理。因為所有的布局請求將會被合并到一個布局操作中去,所以你不需要為經常調用這個方法而擔心。

你可以調用layoutIfNeeded/layoutSubtreeIfNeeded(分別針對 iOS / OS X)來強制系統立即更新視圖樹的布局。如果你下一步操作依賴于更新后視圖的 frame,這將非常有用。在你自定義的視圖中,你可以重寫layoutSubviews/layout來獲得控制布局變化的所有權,我們稍后將展示使用方法。

最終,不管你是否用了自動布局,顯示器都會自上而下將渲染后的視圖傳遞到屏幕上,你也可以通過調用setNeedsDisplay來觸發,這將會導致所有的調用都被合并到一起推遲重繪。重寫熟悉的drawRect:能夠讓我們獲得自定義視圖中顯示過程的所有權。

既然每一步都是依賴前一步操作的,如果有任何布局的變化還沒實行的話,顯示操作將會觸發一個布局行為。類似地,如果約束條件系統中存在沒有實行的改變,布局變化也將會觸發更新約束條件。

需要牢記的是,這三步并不是單向的。基于約束條件的布局是一個迭代的過程,布局操作可以基于之前的布局方案來對約束做出更改,而這將再次觸發約束的更新,并緊接另一個布局操作。這可以被用來創建高級的自定義視圖布局,但是如果你每一次調用的自定義layoutSubviews都會導致另一個布局操作的話,你將會陷入到無限循環的麻煩中去。

為自定義視圖啟動自動布局

當創建一個自定義視圖時,你需要知道關于自動布局的這些事情:具體指定一個恰當的固有內容尺寸 (intrinsic content size),區分開視圖的 frame 和 alignment rect,啟動 baseline-aligned 布局,如何 hook 到布局過程中,我們將會逐一了解這些部分。

固有內容尺寸(Intrinsic Content Size

固有內容尺寸是一個視圖期望為其顯示特定內容得到的大小。比如,UILabel有一個基于字體的首選高度,一個基于字體和顯示文本的首選寬度。UIProgressView僅有一個基于其插圖的首選高度,但沒有首選寬度。一個沒有格式的UIView既沒有首選寬度也沒有首選高度。

你需要根據想要顯示的內容來決定你的自定義視圖是否具有一個固有內容尺寸,如果有的話,它是在哪個尺度上固有。

為了在自定義視圖中實現固有內容尺寸,你需要做兩件事:重寫intrinsicContentSize為內容返回恰當的大小,無論何時有任何會影響固有內容尺寸的改變發生時,調用invalidateIntrinsicContentSize。如果這個視圖只有一個方向的尺寸設置了固有尺寸,那么為另一個方向的尺寸返回UIViewNoIntrinsicMetric/NSViewNoIntrinsicMetric。

需要注意的是,固有內容尺寸必須是獨立于視圖 frame 的。例如,不可能返回一個基于 frame 高度或寬度的特定高寬比的固有內容尺寸。

壓縮阻力 (Compression Resistance) 和 內容吸附 (Content Hugging)

!!!!譯者注:我理解為壓縮阻力和內容吸附性,實在是想不到更貼切的名稱了。壓縮阻力是控制視圖在兩個方向上的收縮性,內容吸附性是當視圖的大小改變時,它會盡量讓視圖靠近它的固有內容尺寸

每個視圖在兩個方向上都分配有內容壓縮阻力優先級和內容吸附性優先級。只有當視圖定義了固有內容尺寸時這些屬性才能起作用,如果沒有定義內容大小,那就沒法阻止被壓縮或者吸附了。

在后臺中,固有內容尺寸和這些優先值被轉換為約束條件。一個固有內容尺寸為{100,30}的 label,水平/垂直壓縮阻力優先值為750,水平/垂直的內容吸附性優先值為250,這四個約束條件將會生成:


如果你不熟悉上面約束條件所使用的可視格式語言,你可以到Apple 文檔中了解。記住,這些額外的約束條件對了解自動布局的行為產生了隱式的幫助,同時也更好理解它的錯誤信息。

Frame 和 Alignment Rect

自動布局并不會操作視圖的 frame,但能作用于視圖的 alignment rect。大家很容易忘記它們之間細微的差別,因為在很多情況下,它們是相同的。但是alignment rect 實際上是一個強大的新概念:從一個視圖的視覺外觀解耦出視圖的 layout alignment edges。

比如,一個自定義 icon 類型的按鈕比我們期望點擊目標還要小的時候,這將會很難布局。當插圖顯示在一個更大的 frame 中時,我們將不得不了解它顯示的大小,并且相應調整按鈕的 frame,這樣 icon 才會和其他界面元素排列好。當我們想要在內容的周圍繪制像 badges,陰影,倒影的裝飾時,也會發生同樣的情況。

我們可以使用 alignment rect 簡單的定義需要用來布局的矩形。在大多數情況下,你僅需要重寫alignmentRectInsets方法,這個方法允許你返回相對于 frame 的 edge insets。如果你需要更多控制權,你可以重寫alignmentRectForFrame:frameForAlignmentRect:。如果你不想減去固定的 insets,而是計算基于當前 frame 的 alignment rect,那么這兩個方法將會非常有用。但是你需要確保這兩個方法是互為可逆的。

關于這點,回憶上面提及到的視圖固有內容尺寸引用它的 alignment rect,而不是 frame。這是有道理的,因為自動布局直接根據固有內容尺寸產生壓縮阻力和內容吸附約束條件。

基線對齊 (Baseline Alignment)

為了讓使用NSLayoutAttributeBaseline屬性的約束條件對自定義視圖奏效,我們需要做一些額外的工作。當然,這只有我們討論的自定義視圖中有類似基準線的東西時,才有意義。

在 iOS 中,可以通過實現viewForBaselineLayout來激活基線對齊。在這里返回的視圖底邊緣將會作為 基線。默認實現只是簡單的返回自己,然而自定義的實現可以返回任何子視圖。在 OS X 中,你不需要返回一個子視圖,而是重新定義baselineOffsetFromBottom返回一個從視圖底部邊緣開始的 offset,這和在 iOS 中一樣,默認實現都是返回 0。

控制布局

在自定義視圖中,你能完全控制它子視圖的布局。你可以增加本地約束;根據內容變化需要,你可以改變本地約束;你可以為子視圖調整布局操作的結果;或者你可以選擇拋棄自動布局。

但確保你明智的使用這個權利。大多數情況下可以簡單地通過為你的子視圖簡單的增加本地約束來處理。

本地約束

如果我們想用幾個子視圖組成一個自定義視圖,我們需要以某種方式布局這些子視圖。在自動布局的環境中,自然會想到為這些視圖增加本地約束。然而,需要注意的是,這將會使你自定義的視圖是基于自動布局的,這個視圖不能再被使用于未啟用自動布局的 windows 中。最好通過實現requiresConstraintBasedLayout返回 YES 明確這個依賴。

添加本地約束的地方是updateConstraints。確保在你的實現中增加任何你需要布局子視圖的約束條件之后,調用一下[super updateConstraints]。在這個方法中,你不會被允許禁用何約束條件,因為你已經進入上面所描述的布局過程的第一步了。如果嘗試著這樣做,將會產生一個友好的錯誤信息 “programming error”。

如果稍后一個失效的約束條件發生了改變的話,你需要立刻移除這個約束并調用setNeedsUpdateConstraints。事實上,僅在這種情況下你需要觸發更新約束條件的操作。

控制子視圖布局

如果你不能利用布局約束條件達到子視圖預期的布局,你可以進一步在 iOS 里重寫layoutSubviews或者在 OS X 里面重寫layout。通過這種方式,當約束條件系統得到解決并且結果將要被應用到視圖中時,你便已經進入到布局過程的第二步。

最極端的情況是不調用父類的實現,自己重寫全部的layoutSubviews / layout。這就意味著你在這個視圖里的視圖樹里拋棄了自動布局。從現在起,你可以按喜歡的方式手動放置子視圖。

如果你仍然想使用約束條件布局子視圖,你需要調用[super layoutSubviews]/[super layout],然后對布局進行微調。你可以通過這種方式創建那些通過定于約束無法實現的布,比如,由到視圖大小之間的關系或是視圖之間間距的關系來定義的布局。

這方面另一個有趣的使用案例就是創建一個布局依賴的視圖樹。當自動布局完成第一次傳遞并且為自定義視圖的子視圖設置好 frame 后,你便可以檢查子視圖的位置和大小,并為視圖層級和(或)約束條件做出調整。WWDC session 228 – Best Practices for Mastering Auto Layout有一個很好的例子。

你也可以在第一次布局操作完成后再決定改變約束條件。比如,如果視圖變得太窄的話,將原來排成一行的子視圖轉變成兩行。


多行文本的固有內容尺寸

UILabel和NSTextField對于多行文本的固有內容尺寸是模糊不清的。文本的高度取決于行的寬度,這也是解決約束條件時需要弄清的問題。為了解決這個問題,這兩個類都有一個叫做preferredMaxLayoutWidth的新屬性,這個屬性指定了行寬度的最大值,以便計算固有內容尺寸。

因為我們通常不能提前知道這個值,為了獲得正確的值我們需要先做兩步操作。首先,我們讓自動布局做它的工作,然后用布局操作結果的 frame 更新給首選最大寬度,并且再次觸發布局。


第一次調用[super layoutSubviews]是為了獲得 label 的 frame,而第二次調用是為了改變后更新布局。如果省略第二個調用我們將會得到一個NSInternalInconsistencyException的錯誤,因為我們改變了更新約束條件的布局操作,但我們并沒有再次觸發布局。

我們也可以在 label 子類本身中這樣做:


在這種情況下,我們不需要先調用[super layoutSubviews],因為當layoutSubviews被調用時,label 就已經有一個 frame 了。

為了在視圖控制器層級做出這樣的調整,我們用掛鉤到 viewDidLayoutSubviews。這時候第一個自動布局操作的 frame 已經被設置,我們可以用它們來設置首選最大寬度。


最后,確保你沒有給 label 設置一個比 label 內容壓縮阻力優先級還要高的具體高度約束。否則它將會取代根據內容計算出的高度。

動畫

說到根據自動布局的視圖動畫,有兩個不同的基本策略:約束條件自身動態化;以及改變約束條件重新計算 frame,并使用 Core Animation 將 frame 插入到新舊位置之間。

這兩種處理方法不同的是:約束條件自身動態化產生的布局結果總是符合約束條件系統。與此相反,使用 Core Animation 插入值到新舊 frame 之間會臨時違反約束條件。

直接使用約束條件動態化只是在 OS X 上的一種可行策略,并且這對你能使用的動畫有局限性,因為約束條件一旦創建后,只有其常量可以被改變。在 OS X 中你可以在約束條件的常量中使用動畫代理來驅動動畫,而在 iOS 中,你只能手動進行控制。另外,這種方法明顯比 Core Animation 方法慢得多,這也使得它暫時不適合移動平臺。

當使用 Core Animation 方法時,即使不使用自動布局,動畫的工作方式在概念上也是一樣的。不同的是,你不需要手動設置視圖的目標 frames,取而代之的是修改約束條件并觸發一個布局操作為你設置 frames。在 iOS 中,代替:


你現在需要寫:


請注意,使用這種方法,你可以對約束條件做出的改變并不局限于約束條件的常量。你可以刪除約束條件,增加約束條件,甚至使用臨時動畫約束條件。由于新的約束只被解釋一次來決定新的 frames,所以更復雜的布局改變都是有可能的。

需要記住的是:Core Animation 和 Auto Layout 結合在一起產生視圖動畫時,自己不要接觸視圖的 frame。一旦視圖使用自動布局,那么你已經將設置 frame 的責任交給了布局系統。你的干擾將造成怪異的行為。

這也意味著,如果使用的視圖變換 (transform) 改變了視圖的 frame 的話,它和自動布局是無法一起正常使用的。考慮下面這個例子:


通常我們期望這個方法在保持視圖的中心時,將它的大小縮小到原來的一半。但是自動布局的行為是根據我們建立的約束條件種類來放置視圖的。如果我們將其居中于它的父視圖,結果便像我們預想的一樣,因為應用視圖變換會觸發一個在父視圖內居中新 frame 的布局操作。然而,如果我們將視圖的左邊緣對齊到另一個視圖,那么這個 alignment 將會粘連住,并且中心點將會移動。

不管怎么樣,即使最初的結果跟我們預想的一樣,像這樣通過約束條件將轉換應用到視圖布局上并不是一個好主意。視圖的 frame 沒有和約束條件同步,也將導致怪異的行為。

如果你想使用 transform 來產生視圖動畫或者直接使它的 frame 動態化,最干凈利索的技術是將這個視圖嵌入到一個視圖容器內,然后你可以在容器內重寫 layoutSubviews,要么選擇完全脫離自動布局,要么僅僅調整它的結果。舉個例子,如果我們在我們的容器內建立一個子視圖,它根據容器的頂部和左邊緣自動布局,當布局根據以上的設置縮放轉換后我們可以調整它的中心:


如果我們將 animatedView 屬性暴露為 IBOutlet,我們甚至可以使用 Interface Builder 里面的容器,并且使用約束條件放置它的的子視圖,同時還能夠根據固定的中心應用縮放轉換。

調試

當談到調試自動布局,OS X 比 iOS 還有一個重要的優勢。在 OS X 中,你可以利用 Instrument 的 Cocoa Layout 模板,或者是NSWindow的visualizeConstraints:方法。而且NSView有一個identifier屬性,為了獲得更多可讀的自動布局錯誤信息,你可以在 Interface Builder 或代碼里面設置這個屬性。

不可滿足的約束條件

如果我們在 iOS 中遇到不可滿足的約束條件,我們只能在輸出的日志中看到視圖的內存地址。尤其是在更復雜的布局中,有時很難辨別出視圖的哪一部分出了問題。然而,在這種情況下,還有幾種方法可以幫到我們。

首先,當你在不可滿足的約束條件錯誤信息中看到NSLayoutResizingMaskConstraints時,你肯定忘了為你某一個視圖設定translatesAutoResizingMaskIntoConstraints為 NO。Interface Builder 中會自動設置,但是使用代碼時,你需要為所有的視圖手動設置。

如果不是很明確是哪個視圖導致的問題,你就需要通過內存地址來辨認視圖。最簡單的方法是使用調試控制臺。你可以打印視圖本身或它父視圖的描述,甚至遞歸描述的樹視圖。這通常會提示你需要處理哪個視圖。


一個更直觀的方法是在控制臺修改有問題的視圖,這樣你可以在屏幕上標注出來。比如,你可以改變它的背景顏色:


確保重新執行你的程序,否則改變不會在屏幕上顯示出來。還要注意將內存地址轉換為(UIView *),以及額外的圓括號,這樣我們就可以使用點操作。另外,你當然也可以通過發送消息來實現:


確保重新執行你的程序,否則改變不會在屏幕上顯示出來。還要注意將內存地址轉換為(UIView *),以及額外的圓括號,這樣我們就可以使用點操作。另外,你當然也可以通過發送消息來實現:

另一種方法是使用 Instrument 的 allocation 模板,根據圖表分析。一旦你從錯誤消息中得到內存地址(運行 Instruments 時,你從 Console 應用中獲得的錯誤消息),你可以將 Instrument 的詳細視圖切換到 Objects List 頁面,并且用 Cmd-F 搜索那個內存地址。這將會為你顯示分配視圖對象的方法,這通常是一個很好的暗示(至少對那些由代碼創建的視圖來說是這樣的)。

你也可以通過改進錯誤信息本身,來更容易地在 iOS 中弄懂不可滿足的約束條件錯誤到底在哪里。我們可以在一個 category 中重寫NSLayoutConstraint的描述,并且將視圖的 tags 包含進去:


如果整數的tag屬性信息不夠的話,我們還可以得到更多新奇的東西,并且在視圖類中增加我們自己命名的屬性,然后可以打印到錯誤消息中。我們甚至可以在 Interface Builder 中,使用 identity 檢查器中的 “User Defined Runtime Attributes” 為自定義屬性分配值。


通過這種方法錯誤消息變得更可讀,并且你不需要找出內存地址對應的視圖。然而,對你而言,你需要做一些額外的工作以確保每次為視圖分配的名字都是有意義。

Daniel提出了另一個很巧妙的方法,可以為你提供更好的錯誤消息并且不需要額外的工作:對于每個布局約束條件,都需要將調用棧的標志融入到錯誤消息中。這樣就很容易看出來問題涉及到的約束了。要做到這一點,你需要 swizzle UIView 或者 NSView 的addConstraint:/addConstraints:方法,以及布局約束的description方法。在添加約束的方法中,你需要為每個約束條件關聯一個對象,這個對象描述了當前調用棧堆棧的第一個棧頂信息(或者任何你從中得到的信息)


一旦你為每個約束對象提供這些信息,你可以簡單的修改UILayoutConstraint的描述方法將其包含到輸出日志中。


檢出這個GitHub倉庫,了解這一技術的代碼示例。

另一個常見的問題就是有歧義的布局。如果我們忘記添加一個約束條件,我們經常會想為什么布局看起來不像我們所期望的那樣。UIView和NSView提供三種方式來查明有歧義的布局:

hasAmbiguousLayout

exerciseAmbiguityInLayout

,和私有方法_autolayoutTrace。

顧名思義,如果視圖存在有歧義的布局,那么hasAmbiguousLayout返回YES。如果我們不想自己遍歷視圖層并記錄這個值,可以使用私有方法 _autolayoutTrace。這將返回一個描述整個視圖樹的字符串:類似于recursiveDescription的輸出(當視圖存在有歧義的布局時,這個方法會告訴你)。

由于這個方法是私有的,確保正式產品里面不要包含調用這個方法的任何代碼。為了防止你犯這種錯誤,你可以在視圖的category中這樣做:


_autolayoutTrace打印的結果如下:


正如不可滿足約束條件的錯誤消息一樣,我們仍然需要弄明白打印出的內存地址所對應的視圖。

另一個標識出有歧義布局更直觀的方法就是使用exerciseAmbiguityInLayout。這將會在有效值之間隨機改變視圖的 frame。然而,每次調用這個方法只會改變 frame 一次。所以當你啟動程序的時候,你根本不會看到改變。創建一個遍歷所有視圖層級的輔助方法是一個不錯的主意,并且讓所有的視圖都有一個歧義的布局“晃動 (jiggle)”。


NSUserDefault選項

有幾個有用的NSUserDefault選項可以幫助我們調試、測試自動布局。你可以在代碼中設定,或者你也可以在scheme editor中指定它們作為啟動參數。

顧名思義,UIViewShowAlignmentRects和NSViewShowAlignmentRects設置視圖可見的 alignment rects。NSDoubleLocalizedStrings簡單的獲取并復制每個本地化的字符串。這是一個測試更長語言布局的好方法。最后,設置AppleTextDirection和NSForceRightToLeftWritingDirection為YES,來模擬從右到左的語言。

編者注如果你不知道怎么在 scheme 中設置類似NSDoubleLocalizedStrings,這里有一張圖來說明;

約束條件代碼

當在代碼中設置視圖和它們的約束條件時候,一定要記得將translatesAutoResizingMaskIntoConstraints設置為 NO。如果忘記設置這個屬性幾乎肯定會導致不可滿足的約束條件錯誤。即使你已經用自動布局一段時間了,但還是要小心這個問題,因為很容易在不經意間發生產生這個錯誤。

當你使用可視化結構語言 (visual format language, VFL)設置約束條件時,constraintsWithVisualFormat:options:metrics:views:方法有一個很有用的option參數。如果你還沒有用過,請參見文檔。這不同于格式化字符串只能影響一個視圖,它允許你調整在一定范圍內的視圖。舉個例子,如果用可視格式語言指定水平布局,那么你可以使用NSLayoutFormatAlignAllTop排列可視語言里所有視圖為上邊緣對齊。

還有一個使用可視格式語言在父視圖中居中子視圖的小技巧,這技巧利用了不均等約束和可選參數。下面的代碼在父視圖中水平排列了一個視圖:

UIView*superview = theSuperView;NSDictionary*views = NSDictionaryOfVariableBindings(superview, subview);NSArray*c = [NSLayoutConstraint? ? ? ? ? ? ? ? constraintsWithVisualFormat:@"V:[superview]-(<=1)-[subview]"]? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? options:NSLayoutFormatAlignAllCenterX? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? metrics:nilviews:views];[superview addConstraints:c];

這利用了NSLayoutFormatAlignAllCenterX選項在父視圖和子視圖間創建了居中約束。格式化字符串本身只是一個虛擬的東西,它會產生一個指定的約束,通常情況下只要子視圖是可見的,那么父視圖底部和子視圖頂部邊緣之間的空間就應該小于等于1點。你可以顛倒示例中的方向達到垂直居中的效果。

使用可視格式語言另一個方便的輔助方法就是我們在上面例子中已經使用過的 NSDictionaryFromVariableBindings 宏指令,你傳遞一個可變數量的變量過去,返回得到一個鍵為變量名的字典。

為了布局任務,你需要一遍一遍的調試,你可以方便的創建自己的輔助方法。比如,你想要垂直地排列一系列視圖,想要它們垂直方向間距一致,水平方向上所有視圖以它們的左邊緣對齊,用下面的方法將會方便很多:


同時也有許多不同的自動布局的庫采用了不同的方法來簡化約束條件代碼。

性能

自動布局是布局過程中額外的一個步驟。它需要一組約束條件,并把這些約束條件轉換成 frame。因此這自然會產生一些性能的影響。你需要知道的是,在絕大數情況下,用來解決約束條件系統的時間是可以忽略不計的。但是如果你正好在處理一些性能關鍵的視圖代碼時,最好還是對這一點有所了解。

例如,有一個 collection view,當新出現一行時,你需要在屏幕上呈現幾個新的 cell,并且每個 cell 包含幾個基于自動布局的子視圖,這時你需要注意你的性能了。幸運的是,我們不需要用直覺來感受上下滾動的性能。啟動 Instruments 真實的測量一下自動布局消耗的時間。當心NSISEngine類的方法。

另一種情況就是當你一次顯示大量視圖時可能會有性能問題。將約束條件轉換成視圖的 frame 時,用來計算約束的算法超線性復雜的。這意味著當有一定數量的視圖時,性能將會變得非常低下。而這確切的數目取決于你具體使用情況和視圖配置。但是,給你一個粗略的概念,在當前 iOS 設備下,這個數字大概是 100。你可以讀這兩個博客帖子了解更多的細節。

記住,這些都是極端的情況,不要過早的優化,并且避免自動布局潛在的性能影響。這樣大多數情況便不會有問題。但是如果你懷疑這花費了你決定性的幾十毫秒,從而導致用戶界面不完全流暢的話,分析你的代碼,然后你再去考慮用回手動設置 frame 有沒有意義。此外,硬件將會變得越來越能干,并且Apple也會繼續調整自動布局的性能。所以現實世界中極端情況的性能問題也將隨著時間減少。

結論

自動布局是一個創建靈活用戶界面的強大功能,這種技術不會消失。剛開始使用自動布局時可能會有點困難,但總會有柳暗花明的一天。一旦你掌握了這種技術,并且掌握了排錯的小技巧,便可庖丁解牛,恍然大悟:這太符合邏輯了。


翻譯作者:answer-huang

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,844評論 2 372

推薦閱讀更多精彩內容