AutoLayout && SizeClasses && Masonry

Autolayout


Auto Layout Guide

Content Compression Resistance和Content Hugging? -- ?感覺笨笨講的更好

都是相較 intrinsic size

Content Compression Resistance ?-- ?反壓縮

Content Hugging ?-- ?反擴張


有趣的Autolayout示例-Masonry實現123


Autolayout的第一次親密接觸??-- ?不錯不錯

(1)IntrinsicContentSize? --? 自己根據內容調整大小

自定義的View擁有默認Size -> 重寫intrinsicContentSize。

ContentHugging:表示View的寬度和高度緊靠內容,不讓其擴展的力量。-- ?對自身 ? ? ? ? ? ? ? ? ? CompressionResistance:優先級越高的,顯示的內容越完整 ? -- ?兩個對象相互

(2)流程:

對于同一個View,ContentHugging和CompressionResistance不會同時起作用。當一個Label有文字的時候,label會存在一個內容的Size。

<1> 如果有外力讓其size擴張ContentHugging會起作用,外力大于ContentHugging的力量,label的size由外力決定,反之,label的Size由內容決定。

<2> 如果有外力讓其size壓縮CompressionResistance會起作用,外力大于CompressionResistance的力量,label的size由外力決定,反之,label的Size由內容決定。

所以先判斷對自身來說是擴張還是壓縮,如果content Hugging = 1000,那不可能被擴張。

(3)Priorities ?-- ?4種 -- 1000,750,250, 50

(4)調試:

<1> masonry重寫了constriant的-description方法

<2> Debug View Hierarchy

(5)Autolayout深層次的探索

<1> 什么時候計算frame

根據最初的介紹,Autolayout是在設置constraint的時候,將constraints存放在View的屬性中,在真正布局的時候去計算出view的frame,完成布局。

由于Autolayout不通過frame布局,而是直接設置center和bounds

由于Autolayout是延遲布局的,并不是約束更新之后就立刻布局

Autolayout是在父view的-layoutSubview中更新Frame的 --setNeedsLayout,layoutIfNeeded

(6)當View約束發生變化時,是怎么調整布局的

view1 <-> view2 -> view3

view2 -> view1的setNeedsLayout,如果自身要改變 -> 父View setNeedsLayout...

如果沒改變 -> 自身layoutSubviews(中改變view2布局)

-> view2布局改變 -> view2的layoutSubviews(中改變view3沒改變,所停止,否則繼續調用view3的layoutSubviews)

(7)Autolayout生效之前使用frame ?-- ?系統會根據約束重新計算Frame。

Autolayout生效之后 ?-- ?setFrame

(8)初始化constraint的代碼放在viewDidLoad等初始化方法中更好。

updateConstraints方法僅用于提升性能。


AutoLayout Tips

UIScrollview與Autolayout的那點事??--? 自己理解才最重要啊。。。

UIScrollView依靠與其subViews之間的約束來確定ContentSize的大小

這是因為UIScrollView是個非常特殊的view UIScrollView與其subView之間相對位置的約束并不會直接用于frame的計算 而是會轉化為對ContentSize的計算

scrollView自身只是一個窗口,設定x,y,w,h(frame)就好。但是,它中的subViews的frame并不是基于scrollView固定的frame來確定的。它的subViews之間的相互約束來共同確定contentSize。所以subViews不能只是設定frame,還要設定top,left,bottom,right。因為contentSize是動態的。

純代碼 和 IB 中都先給scrollView加一個contentView(container View)來避免這種在各個subView之間要協同確定contentSize的繁瑣且容易出錯的方式。




Masonry介紹與使用實踐(快速上手Autolayout)

1. ?Masonry中能夠添加autolayout約束有三個函數

(1)- (NSArray*)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block;

(2)- (NSArray*)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block;

(3)- (NSArray*)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block;

mas_makeConstraints只負責新增約束 Autolayout不能同時存在兩條針對于同一對象的約束 否則會報錯

mas_updateConstraints針對上面的情況 會更新在block中出現的約束 不會導致出現兩個相同約束的情況

mas_remakeConstraints則會清除之前的所有約束 僅保留最新的約束

2. ?其次 equalTo 和 mas_equalTo的區別在哪里呢? 其實 mas_equalTo是一個MACRO ?mas_equalTo只是對其參數進行了一個BOX操作(裝箱)。所支持的類型 除了NSNumber支持的那些數值類型之外 就只支持CGPointCGSizeUIEdgeInsets

mas_equalTo適用數值元素,equalTo適合多屬性的,比如make.left.and.right.equalTo(self.view)

對于top,left,bottom,right來說,top 和 left 能分別確定 y 和 x 的坐標,而left 和 right 共同決定 width,top 和 bottom共同確定著 height。

設置約束前,請先加入view。

3. UIScrollView && AutoLayout

(1) 正常設置scrollView,通過 edgeInsets

(2) 加一個containerView,用于方便計算 contentSize,edgeInsets + w + h。

(3) 造subView加入containerView,設置 left,right,height,top約束

(4) 設置 containerView的bottom為lastSubView的bottoms,為了計算contentSize。

4. 橫向或者縱向等間隙的排列一組view

Category: -- ?橫縱向同理

(1) n 個View有n + 1個間隙,初始化 n + 1個間隙,設置w,h相等

(2) 第一個間隙 left, centerY。間隙與View相互設置約束(left = right之類)

(3) last間隙 = UIView.right。


追求Masonry? -- ?源碼源碼

?(1)

MASConstraintMaker? --? 22種Attribute類型

MASConstraint? --? 19種Attribute

MAS_VIEW(UIView)? --? 與MASConstraint一樣,加了mas_前綴防止沖突。

(2)

insets: 用來設置left, right, top, bottom。接受MASEdgeInsets類型值

sizeOffset: 用來設置width, height。接受CGSize類型的值

centerOffset: 用來設置centerX, centerY。接受CGPoint類型的值

offset: 可以用來設置所有的東西。接受CGFloat類型的值

(3)小技巧(源碼做了兼容) ?-- ?這里的小技巧或許就是casa說的業務工程師亂來吧,本身很好的可讀性非要炫技毀了一切,反正我是老老實實的敲全。

(4) mas_key

批量設置的宏? MASAttachKeys(self.view,view1);?

(5) Macros

MAS_SHORTHAND? &&? MAS_SHORTHAND_GLOBALS

// 定義這個常量,就可以在使用Masonry不必總帶著前綴 `mas_`:

#define MAS_SHORTHAND

// 定義這個常量,以支持在 Masonry 語法中自動將基本類型轉換為 object 類型:

#define MAS_SHORTHAND_GLOBALS

(6) Masonry一共13個類,源碼確實解析的不錯!



AutoLayout框架Masonry使用心得? -- ?一般般

(1)setContentHuggingPriority: forAxis:

setContentCompressionResistancePriority: forAxis:

multipliedBy()

(2)AutoLayout下UILabel設置多行計算需要設置preferredMaxLayoutWidth

label.preferredMaxWidth= [UIScreenmainScreen].bounds.size.width- margin - padding;

(3)

preferredMaxLayoutWidth用來制定最大的寬,一般用在多行的UILabel中

systemLayoutSizeFittingSize方法能夠獲得view的高度

iOS7有兩個很有用的屬性,topLayoutGuide和bottomLayoutGuide,這個兩個主要是方便獲取UINavigationController和UITabBarController的頭部視圖區域和底部視圖區域。

masonry:make.top.equalTo(self.mas_topLayoutGuide);


(4)AutoLayout情況如何計算UITableView的變高高度

每次拿model填充單例cell,然后通過cell的systemLayoutSizeFittingSizes:返回高度。和FD思路一樣。


實時顯示iOS編寫UI代碼效果

Classy


Instruments -- CocoaLayout

Caller:哪一個類和方法觸發了一個約束的改變

Constraint:這一欄是回溯(trace)真正的血肉組成。通過約束(Constraint)的內存地址或者通過標識符屬性(如果可用的話),你能夠辨別出哪個約束被影響了。Constraint欄也包含了VFL字符串,這個字符串闡述了坐標軸所對應的約束線和約束定義的關系。

Constant:定義約束的常量值。

Event:詳細說明某個約束被加載了什么樣的操作。這個約束可能被創建,移除,或者添加到window上面了,或者屬性例如標識符(identifier)被修改或者移除了。這些改變事件包括:


AutoLayout好多好多不懂的 終于明白了? -- ?UIStackView,AutoLayout與Frame,動畫


先進的自動布局工具箱? -- ?objc的部分文章不適合入門

布局過程


更新約束(自下而上(從子視圖到父視圖)) (updating constraints) 和布局視圖 (自上而下(從父視圖到子視圖))(laying out views),更新約束 -> 布局視圖 -> 顯示。

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


Intrinsic Content Size

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

Compression Resistance && Content Hugging

Frame 和 Alignment Rect

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

基線對齊 (Baseline Alignment)

在 iOS 中,可以通過實現viewForBaselineLayout來激活基線對齊。在這里返回的視圖底邊緣將會作為 基線。默認實現只是簡單的返回自己,然而自定義的實現可以返回任何子視圖。

控制布局


本地約束

最好通過實現requiresConstraintBasedLayout返回 YES 明確這個依賴。

添加本地約束的地方是updateConstraints。確保在你的實現中增加任何你需要布局子視圖的約束條件之后,調用一下[super updateConstraints]。

控制子視圖布局

你可以進一步在 iOS 里重寫layoutSubviews,如果你仍然想使用約束條件布局子視圖,你需要調用[super layoutSubviews],然后對布局進行微調。


多行文本的固有內容尺寸


UILabel 和 NSTextField? --? 文本的高度取決于行的寬度 --preferredMaxLayoutWidth

動畫


兩個不同的基本策略:約束條件自身動態化? &&? UIView + layoutIfNeeded

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

如果你想使用 transform 來產生視圖動畫或者直接使它的 frame 動態化,最干凈利索的技術是將這個視圖嵌入到一個視圖容器內,然后你可以在容器內重寫 layoutSubviews,要么選擇完全脫離自動布局,要么僅僅調整它的結果。


調試


當你在不可滿足的約束條件錯誤信息中看到NSLayoutResizingMaskConstraints時,你肯定忘了為你某一個視圖設定translatesAutoResizingMaskIntoConstraints為 NO。

(1)如果不是很明確是哪個視圖導致的問題,你就需要通過內存地址來辨認視圖。(或者用masonry的mas_key)。 po,recursiveDescription

(2)你可以改變它的背景顏色:

(lldb) expr ((UIView*)0x7731880).backgroundColor= [UIColorpurpleColor]

(lldb) expr [(UIView*)0x7731880setBackgroundColor:[UIColorpurpleColor]]

(3) 另一種方法是使用 Instrument 的 allocation 模板,根據圖表分析。錯誤消息(內存地址)Instrument 的詳細視圖切換到 Objects List 頁面,并且用 Cmd-F 搜索那個內存地址。

(4) 我們可以在一個 category 中重寫NSLayoutConstraint的描述,并且將視圖的 tags 包含進去:-- ?用關聯對象添加對象的tag,用于標識View,全是Masonry的思路。

(5) swizzle UIView 的 addConstraint:/addConstraints:方法,以及布局約束的description方法。

有歧義的布局


(1)UIView 提供三種方式來查明有歧義的布局: ?hasAmbiguousLayoutexerciseAmbiguityInLayout,和私有方法_autolayoutTrace

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

#ifdef ?DEBUG

NSLog(@"%@", [selfperformSelector:@selector(_autolayoutTrace)]);

#endif

(2) 另一個標識出有歧義布局更直觀的方法就是使用exerciseAmbiguityInLayout。


NSUserDefault選項

UIViewShowAlignmentRects,NSDoubleLocalizedStrings


約束條件代碼


一定要記得將?translatesAutoResizingMaskIntoConstraints?設置為 NO。

VFL:constraintsWithVisualFormat:options:metrics:views:方法有一個很有用的option參數。


性能


你可以讀這兩個博客帖子了解更多的細節


關于 Cocoa Auto Layout,你需要知道10件事? -- ?mark

當我們寫約束時,應該多留意Debugger控制臺。我發現Apple關于模棱兩可的約束或未滿足的約束的錯誤日志總是可以幫助我們快速定位問題。這個可以參考Apple’s debugging tips in the Cocoa Auto Layout Guide


iOS 8 Size Classes初探

總的來說,iOS對UI這塊的改動是跨時代性的,Autolayout的出現使得布局的復雜度減少到了View與View的關系上,再由根 View(也就是屏幕)指定frame,隨后所有子View相對布局,把frame的概念歸一化到根View的frame上;但有了Size Class后,根視圖的frame概念也被移除了,這下整個app的UI和frame這個單詞已然脫離關系,這也正是apple想要達到的目的。


iOS 8 AutoLayout與Size Class自悟? -- ?圖圖圖



WWDC 2014 Session筆記 - iOS界面開發的大一統

UITraitCollection 和 UITraitEnvironment:

(1)UIScreen,UIWindow,UIViewController和UIView 都實現了UITraitEnvironment.traitCollection == UITraitCollection

(2)和 UIKit 中的響應者鏈正好相反,traitCollection將會在 view hierarchy 中自上而下地進行傳遞。

(3)在實際操作時,我們往往會在 ViewController 中重寫-traitCollectionDidChange:或者-willTransitionToTraitCollection:withTransitionCoordinator:方法 (對于 ViewController 來說的話,后者也許是更好的選擇,因為提供了轉場上下文方便進行動畫;但是對于普通的 View 來說就只有前面一個方法了)

UIViewController 的表現方式

UISplitViewController?

UIPresentationController -> UIAlertController && UISearchController

這種很輕很明確的使用邏輯block handler 才是最好的選擇

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容