細(xì)數(shù)AutoLayout以來UIView和UIViewController新增的相關(guān)API

細(xì)數(shù)AutoLayout以來UIView和UIViewController新增的相關(guān)API

2014-10-26 12:29

編輯:

suiling

分類:iOS開發(fā)

來源:chun.tips

2

45127

UIViewAutolayoutUIViewController

細(xì)數(shù)AutoLayout以來UIView和UIViewController新增的相關(guān)API – UIViewController篇

UILayoutSupport

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

  1. @property(nonatomic,readonly,retain) id<uilayoutsupport> topLayoutGuide NS_AVAILABLE_IOS(7_0); </uilayoutsupport>

  2. @property(nonatomic,readonly,retain) id<uilayoutsupport> bottomLayoutGuide NS_AVAILABLE_IOS(7_0); </uilayoutsupport>

  3. @protocol UILayoutSupport

  4. @property(nonatomic,readonly) CGFloat length;

  5. @end

</pre>

從iOS 7以來,當(dāng)我們的視圖控制器結(jié)構(gòu)中有NavigationBar瘾境,TabBar或者ToolBar的時候污尉,它們的translucent屬性的默認(rèn)值改為了YES簸喂,并且當(dāng)前的ViewController的高度會是整個屏幕的高度。(比如一個場景:拖動TableView的時候弃理,上面的NavigationBar能夠透過去看到TableView的內(nèi)容兽赁。)

為了確保我們的視圖不被這些Bar覆蓋,我們可以在我們AutoLayout布局中使用topLayoutGuide和bottomLayoutGuide這兩個屬性封寞。像這樣:

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

  1. NSDictionary *views = @{

    "topLayoutGuide"

    : self.topLayoutGuide, @

    "myLabel"

    : myLabel};

  2. [NSLayoutConstraint constraintsWithVisualFormat:@

    "V:[topLayoutGuide]-[myView]"

    options:0 metrics:nil views:views]

</pre>

這個時候我們的視圖就不會被Bar所覆蓋,顯示在了Bar下方:

[圖片上傳失敗...(image-e8d953-1513687006841)]

并且使用這個屬性布局時仅财,在traitCollection改變時(旋轉(zhuǎn)屏幕)狈究,它的值也會動態(tài)變化。上述代碼满着,在橫屏情況下谦炒,navigationbar高度變了之后贯莺,仍然能夠正確顯示风喇。

這兩個guides的計算方式如下:

topLayoutGuide

是通過計算 View Controller->View->Top 到 覆蓋這個View最下層的那個Bar(像Navigation Bar) -> Bottom 的距離

bottomLayoutGuide

是通過計算 View Controller->View->Bottom 到 覆蓋這個View上層那個Bar(像Tab bar) -> Top 的距離

如果我們不使用AutoLayout布局,我們也可以通過Guide的length屬性獲得相應(yīng)的距離缕探。我們應(yīng)該在-viewDidLayoutSubviews或者-layoutSubviews調(diào)用super之后魂莫,再去獲得length這個值,以確保正確爹耗。

UIConstraintBasedLayoutCoreMethods

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

    • (

    void

    )updateViewConstraints NS_AVAILABLE_IOS(6_0);

</pre>

UIViewController中也新增了一個更新布局約束的方法耙考,在AutoLayout UIView相關(guān)API的筆記中,詳細(xì)講述了UIView的一組更新布局約束的方法。

這個方法默認(rèn)的實(shí)現(xiàn)是調(diào)用對應(yīng)View的 -updateConstraints 潭兽。ViewController的View在更新視圖布局時倦始,會先調(diào)用ViewController的updateViewConstraints 方法。我們可以通過重寫這個方法去更新當(dāng)前View的內(nèi)部布局山卦,而不用再繼承這個View去重寫-updateConstraints方法鞋邑。我們在重寫這個方法時,務(wù)必要調(diào)用 super 或者 調(diào)用當(dāng)前View的 -updateConstraints 方法账蓉。

UITraitEnvironment

又一次看到了UITraitEnvironment協(xié)議枚碗,在UIKit Framework中,有四個類支持這個協(xié)議铸本,分別是UIScreen, UIViewController肮雨,UIView 和 UIPresentationController。所以當(dāng)視圖的traitCollection改變時箱玷,UIViewController能夠捕獲到這個消息怨规,并做對應(yīng)處理的陌宿。 更多解釋可以參考上一篇文章詳解UICoordinateSpace和UIScreen在iOS 8上的坐標(biāo)問題。

關(guān)于Size Class和UITraitCollection的概念可參考如下鏈接:

WWDC 2014 Session筆記 - iOS界面開發(fā)的大一統(tǒng)

From: onecat’s Blog

iOS8 Size Classes的理解與使用

From: Joywii’s Blog

另外波丰,UIViewController還另外提供了以下兩個方法:

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

    • (

    void

    )setOverrideTraitCollection:(UITraitCollection *)collection forChildViewController:(UIViewController *)childViewController NS_AVAILABLE_IOS(8_0);

    • (UITraitCollection *)overrideTraitCollectionForChildViewController:(UIViewController *)childViewController NS_AVAILABLE_IOS(8_0);

</pre>

我們可以通過調(diào)用ViewController的setOverrideTraitCollection方法為它的ChildViewController重新設(shè)置traitCollection的值限番。一般情況下traitCollection值從父controller傳到子controller是不做修改的。當(dāng)我們自己實(shí)現(xiàn)一個容器Controller的時候呀舔,我們可以使用這個方法進(jìn)行調(diào)整弥虐。

相對的,我們可以通過overrideTraitCollectionForChildViewController方法獲得ChildViewController的traitCollection值媚赖。

UIContentContainer

iOS 8上隨著Size Class概念的提出霜瘪,UIViewController支持了UIContentContainer這樣一組新的協(xié)議:

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

    • (

    void

    )systemLayoutFittingSizeDidChangeForChildContentContainer:(id <uicontentcontainer>)container NS_AVAILABLE_IOS(8_0); </uicontentcontainer>

    • (CGSize)sizeForChildContentContainer:(id <uicontentcontainer>)container withParentContainerSize:(CGSize)parentSize NS_AVAILABLE_IOS(8_0); </uicontentcontainer>
    • (

    void

    )viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <uiviewcontrollertransitioncoordinator>)coordinator NS_AVAILABLE_IOS(8_0); </uiviewcontrollertransitioncoordinator>

    • (

    void

    )willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id <uiviewcontrollertransitioncoordinator>)coordinator NS_AVAILABLE_IOS(8_0); </uiviewcontrollertransitioncoordinator>

</pre>

UIViewController對這組協(xié)議提供了默認(rèn)的實(shí)現(xiàn)。我們自定義ViewController的時候可以重寫這些方法來調(diào)整視圖布局惧磺,比如我們可以在這些方法里調(diào)整ChildViewControler的位置颖对。當(dāng)我們重寫這些協(xié)議方法時,我們通常都去調(diào)用 super磨隘。

viewWillTransitionToSize: ViewController的View的size被他的Parent Controller改變時缤底,會觸發(fā)這個方法。(比如rootViewController在它的window旋轉(zhuǎn)的時候)番捂。我們在重寫這個方法時个唧,確保要調(diào)用super,來保證size改變的這條消息能夠正常傳遞給它的Views或者ChildViewControllers。

willTransitionToTraitCollection: 當(dāng)ViewController的traitCollection的值將要改變時會調(diào)用這個方法设预。這個方法是在 UITraitEnvironment協(xié)議方法 traitCollectionDidChange:之前被調(diào)用徙歼。我們在重寫這個方法時,也要確保要調(diào)用super來保證消息的傳遞鳖枕。比如魄梯,我們可以像這樣在traitCollection值改變時,對視圖做對應(yīng)的動畫進(jìn)行調(diào)整:

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

    • (

    void

    )willTransitionToTraitCollection:(UITraitCollection *)newCollection

  1. withTransitionCoordinator:(id <uiviewcontrollertransitioncoordinator>)coordinator </uiviewcontrollertransitioncoordinator>

  2. {

  3. [

    super

    willTransitionToTraitCollection:newCollection

  4. withTransitionCoordinator:coordinator];

  5. [coordinator animateAlongsideTransition:^(id <uiviewcontrollertransitioncoordinatorcontext> context) { </uiviewcontrollertransitioncoordinatorcontext>

  6. if

    (newCollection.verticalSizeClass == UIUserInterfaceSizeClassCompact) {

  7. }

    else

    {

  8. }

  9. [self.view setNeedsLayout];

  10. } completion:nil];

  11. }

</pre>

sizeForChildContentContainer:一個容器ViewController可以使用這個方法設(shè)置ChildViewController的size宾符。當(dāng)容器ViewControllerviewWillTransitionToSize:withTransitionCoordinator:被調(diào)用時(我們重寫這個方法時要調(diào)用Super)酿秸,sizeForChildContentContainer方法將會被調(diào)用。然后我們可以把需要設(shè)置的size發(fā)送給ChildViewController魏烫。當(dāng)我們設(shè)置的這個size和當(dāng)前ChildViewController的size一樣辣苏,那么ChildViewController的viewWillTransitionToSize方法將不會被調(diào)用。

sizeForChildContentContainer默認(rèn)的實(shí)現(xiàn)是返回 parentSize则奥。

systemLayoutFittingSizeDidChangeForChildContentContainer:當(dāng)滿足如下情況考润,這個方法會被調(diào)用:

當(dāng)前ViewController沒有使用AutoLayout布局
ChildrenViewController的View使用了AutoLayout布局
ChildrenViewController View -systemLayoutSizeFittingSize:方法返回的值改變(View由于內(nèi)容的變化,size也出現(xiàn)了變化)

preferredContentSize

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

  1. // From UIContentContainer Protocol

  2. @property (nonatomic, readonly) CGSize preferredContentSize NS_AVAILABLE_IOS(8_0);

    • (

    void

    )preferredContentSizeDidChangeForChildContentContainer:(id <uicontentcontainer>)container NS_AVAILABLE_IOS(8_0); </uicontentcontainer>

  3. // From UIViewController

  4. @property (nonatomic) CGSize preferredContentSize NS_AVAILABLE_IOS(7_0);

</pre>

preferredContentSize在UIContentContainer協(xié)議中是只讀的读处,對應(yīng)的UIViewController有可寫的版本糊治。我們可以使用preferredContentSize來設(shè)置我們期望的ChildViewController的界面大小。舉個例子罚舱,如果應(yīng)用中使用的popOver大小會發(fā)生變化井辜,iOS7之前我們可以用contentSizeForViewInPopover來調(diào)整绎谦。iOS7開始這個API被廢棄,我們可以使用preferredContentSize來設(shè)置粥脚。

當(dāng)一個容器ViewController的ChildViewController的這個值改變時窃肠,UIKit會調(diào)用preferredContentSizeDidChangeForChildContentContainer這個方法告訴當(dāng)前容器ViewController。我們可以在這個方法里根據(jù)新的Size對界面進(jìn)行調(diào)整刷允。

總結(jié) UIViewController到目前為止(iOS 8.1), 關(guān)于布局的API最大的變化是iOS8中新增支持的兩組協(xié)議:UITraitEnvironment 和 UIContentContainer冤留。我們可以在學(xué)習(xí)中通過Demo實(shí)現(xiàn)這些協(xié)議,來觀察ViewController中這些方法最終被調(diào)用的時機(jī)树灶。


細(xì)數(shù)AutoLayout以來UIView和UIViewController新增的相關(guān)API--UIView篇

iOS8上關(guān)于UIView的Margin新增了3個APIs:

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

  1. @property (nonatomic) UIEdgeInsets layoutMargins NS_AVAILABLE_IOS(8_0);

  2. @property (nonatomic) BOOL preservesSuperviewLayoutMargins NS_AVAILABLE_IOS(8_0);

    • (

    void

    )layoutMarginsDidChange NS_AVAILABLE_IOS(8_0);

</pre>

在iOS 8中纤怒,可以使用layoutMargins去定義view之間的間距,該屬性只對AutoLayout布局生效。

因此AutoLayout中NSLayoutAttribute的枚舉值有了相應(yīng)的更新:

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

  1. NSLayoutAttributeLeftMargin NS_ENUM_AVAILABLE_IOS(8_0),

  2. NSLayoutAttributeRightMargin NS_ENUM_AVAILABLE_IOS(8_0),

  3. NSLayoutAttributeTopMargin NS_ENUM_AVAILABLE_IOS(8_0),

  4. NSLayoutAttributeBottomMargin NS_ENUM_AVAILABLE_IOS(8_0),

  5. NSLayoutAttributeLeadingMargin NS_ENUM_AVAILABLE_IOS(8_0),

  6. NSLayoutAttributeTrailingMargin NS_ENUM_AVAILABLE_IOS(8_0),

  7. NSLayoutAttributeCenterXWithinMargins NS_ENUM_AVAILABLE_IOS(8_0),

  8. NSLayoutAttributeCenterYWithinMargins NS_ENUM_AVAILABLE_IOS(8_0),

</pre>

通過在Xcode中測試打印天通,發(fā)現(xiàn)UIView默認(rèn)的layoutMargins的值為 {8, 8, 8, 8}泊窘,我們可以通過修改這個值來改變View之間的距離。

在我們改變View的layoutMargins這個屬性時像寒,會觸發(fā)- (void)layoutMarginsDidChange這個方法烘豹。我們在自己的View里面可以重寫這個方法來捕獲layoutMargins的變化。在大多數(shù)情況下诺祸,我們可以在這個方法里觸發(fā)drawing和layout的Update携悯。

preservesSuperviewLayoutMargins這個屬性默認(rèn)是NO。如果把它設(shè)為YES序臂,layoutMargins會根據(jù)屏幕中相關(guān)View的布局而改變蚌卤。舉個例子:

[圖片上傳失敗...(image-523070-1513687006841)]

如上圖,有三個View奥秆,其中藍(lán)色View的layoutMargins設(shè)為 UIEdgeInsetsMake(50, 50, 50, 50),黃色View的layoutMargins設(shè)為 UIEdgeInsetsMake(10, 10, 10, 10)咸灿。對黃色View的布局約束代碼如下:

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

  1. [NSLayoutConstraint constraintWithItem:yellowView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:blueView attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0];

  2. [NSLayoutConstraint constraintWithItem:yellowView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:blueView attribute:NSLayoutAttributeHeight multiplier:0.5 constant:0.0];

  3. [NSLayoutConstraint constraintWithItem:yellowView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:blueView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0];

  4. [NSLayoutConstraint constraintWithItem:yellowView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:blueView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0];

</pre>

對黑色View的布局代碼如下:

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

  1. [NSLayoutConstraint constraintWithItem:blackView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:yellowView attribute:NSLayoutAttributeTrailingMargin multiplier:1.0 constant:0.0];

  2. [NSLayoutConstraint constraintWithItem:blackView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:yellowView attribute:NSLayoutAttributeLeadingMargin multiplier:1.0 constant:0.0];

  3. [NSLayoutConstraint constraintWithItem:blackView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:yellowView attribute:NSLayoutAttributeTopMargin multiplier:1.0 constant:0.0];

  4. [NSLayoutConstraint constraintWithItem:blackView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:yellowView attribute:NSLayoutAttributeBottomMargin multiplier:1.0 constant:0.0];

</pre>

在preservesSuperviewLayoutMargins默認(rèn)為NO的情況下构订,顯示效果就和上圖一樣(間距為10)。當(dāng)設(shè)置黃色View的preservesSuperviewLayoutMargins為YES時避矢,將會獲得如下效果(間距為50):

[圖片上傳失敗...(image-1f985a-1513687006841)]

UIConstraintBasedLayoutInstallingConstraints

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

  1. @

    interface

    UIView (UIConstraintBasedLayoutInstallingConstraints)

    • (NSArray *)constraints NS_AVAILABLE_IOS(6_0);
    • (

    void

    )addConstraint:(NSLayoutConstraint *)constraint NS_AVAILABLE_IOS(6_0);

    • (

    void

    )addConstraints:(NSArray *)constraints NS_AVAILABLE_IOS(6_0);

    • (

    void

    )removeConstraint:(NSLayoutConstraint *)constraint NS_AVAILABLE_IOS(6_0);

    • (

    void

    )removeConstraints:(NSArray *)constraints NS_AVAILABLE_IOS(6_0);

  2. @end

</pre>

以上這五個API中悼瘾,第一個是返回當(dāng)前View中所有的constraints。后面四個方法即將被廢棄审胸,應(yīng)該使用NSLayoutConstraint類中activateConstraint相關(guān)方法替代亥宿。

UIConstraintBasedLayoutCoreMethods

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

  1. @

    interface

    UIView (UIConstraintBasedLayoutCoreMethods)

    • (

    void

    )updateConstraintsIfNeeded NS_AVAILABLE_IOS(6_0);

    • (

    void

    )updateConstraints NS_AVAILABLE_IOS(6_0);

    • (BOOL)needsUpdateConstraints NS_AVAILABLE_IOS(6_0);
    • (

    void

    )setNeedsUpdateConstraints NS_AVAILABLE_IOS(6_0);

  2. @end

</pre>

setNeedsUpdateConstraints : 當(dāng)一個自定義的View某一個屬性的改變可能影響到界面布局,我們應(yīng)該調(diào)用這個方法來告訴布局系統(tǒng)在未來某個時刻需要更新砂沛。系統(tǒng)會調(diào)用updateConstraints去更新布局烫扼。

updateConstraints :自定義View時,我們應(yīng)該重寫這個方法來設(shè)置當(dāng)前view局部的布局約束碍庵。重寫這個方法時映企,一定要調(diào)用[super updateConstraints]悟狱。

needsUpdateConstraints :布局系統(tǒng)使用這個返回值來確定是否調(diào)用updateConstraints

updateConstraintsIfNeeded :我們可以調(diào)用這個方法觸發(fā)update Constraints的操作。在needsUpdateConstraints返回YES時堰氓,才能成功觸發(fā)update Constraints的操作挤渐。我們不應(yīng)該重寫這個方法。

Auto Layout的布局過程是 update constraints(updateConstraints)-> layout Subviews(layoutSubViews)-> display(drawRect) 這三步不是單向的双絮,如果layout的過程中改變了constrait, 就會觸發(fā)update constraints浴麻,進(jìn)行新的一輪迭代。我們在實(shí)際代碼中囤攀,應(yīng)該避免在此造成死循環(huán)白胀。

UIConstraintBasedCompatibility

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

  1. @

    interface

    UIView (UIConstraintBasedCompatibility)

    • (BOOL)translatesAutoresizingMaskIntoConstraints NS_AVAILABLE_IOS(6_0);
    • (

    void

    )setTranslatesAutoresizingMaskIntoConstraints:(BOOL)flag NS_AVAILABLE_IOS(6_0);

    • (BOOL)requiresConstraintBasedLayout NS_AVAILABLE_IOS(6_0);
  2. @end

</pre>

默認(rèn)情況下,View的autoresizing工作會根據(jù)當(dāng)前位置自動設(shè)置約束抚岗。我們在使用代碼寫自己的約束布局代碼時或杠,必須設(shè)置當(dāng)前View的translatesAutoresizingMaskIntoConstraints為NO,否則無法正常運(yùn)作宣蔚。IB默認(rèn)是NO向抢。

requiresConstraintBasedLayout :我們應(yīng)該在自定義View中重寫這個方法。如果我們要使用Auto Layout布局當(dāng)前視圖胚委,應(yīng)該設(shè)置為返回YES挟鸠。

UIConstraintBasedLayoutLayering

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

    • (CGRect)alignmentRectForFrame:(CGRect)frame NS_AVAILABLE_IOS(6_0);
    • (CGRect)frameForAlignmentRect:(CGRect)alignmentRect NS_AVAILABLE_IOS(6_0);
    • (UIEdgeInsets)alignmentRectInsets NS_AVAILABLE_IOS(6_0);

</pre>

AutoLayout并不會直接操作View的Frame,但是視圖的alignment rect是起作用的亩冬。視圖的默認(rèn)alignmentRectInsets值就是(0,0,0,0)艘希。

我們可以簡單的對當(dāng)前View設(shè)置用來布局的矩形,比如:

我們有一個自定義icon類型的Button硅急,但是icon的大小比我們期望點(diǎn)擊的Button區(qū)域要小覆享。這個時候我們可以重寫alignmentRectInsets,把icon放在適當(dāng)?shù)奈恢谩?/p>

大多數(shù)情況下重寫alignmentRectInsets這個方法可以滿足我們的工作营袜。如果需要更加個性化的修改撒顿,我們可以重寫alignmentRectForFrame和frameForAlignmentRect這兩個方法。比如我們不想減去視圖固定的Insets荚板,而是需要基于當(dāng)前frame修改alignment rect凤壁。在重寫這兩個方法時,我們應(yīng)該確保是互為可逆的跪另。

Base line

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

    • (UIView *)viewForBaselineLayout NS_AVAILABLE_IOS(6_0);

</pre>

當(dāng)我們在使用布局約束中NSLayoutAttributeBaseline屬性時拧抖,系統(tǒng)會默認(rèn)返回當(dāng)前視圖的底部作為baseline。我們可以重寫上述方法免绿,但必須返回的是當(dāng)前視圖中的子視圖唧席。

Intrinsic Content Size

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

  1. UIKIT_EXTERN

    const

    CGFloat UIViewNoIntrinsicMetric NS_AVAILABLE_IOS(6_0);

    • (CGSize)intrinsicContentSize NS_AVAILABLE_IOS(6_0);
    • (

    void

    )invalidateIntrinsicContentSize NS_AVAILABLE_IOS(6_0);

    • (UILayoutPriority)contentHuggingPriorityForAxis:(UILayoutConstraintAxis)axis NS_AVAILABLE_IOS(6_0);
    • (

    void

    )setContentHuggingPriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis NS_AVAILABLE_IOS(6_0);

    • (UILayoutPriority)contentCompressionResistancePriorityForAxis:(UILayoutConstraintAxis)axis NS_AVAILABLE_IOS(6_0);
    • (

    void

    )setContentCompressionResistancePriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis NS_AVAILABLE_IOS(6_0);

</pre>

通過重寫intrinsicContentSize可以設(shè)置當(dāng)前視圖顯示特定內(nèi)容時的大小。比如我們設(shè)置一個自定義View,View里面包含一個Label顯示文字袱吆,為了設(shè)置當(dāng)前View在不同Size Class下內(nèi)容的大小厌衙,我們可以這樣:

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

    • (CGSize)intrinsicContentSize
  1. {

  2. CGSize size = [label intrinsicContentSize];

  3. if

    (self.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact) {

  4. size.width += 4.0f;

  5. }

    else

    {

  6. size.width += 40.0f;

  7. }

  8. if

    (self.traitCollection.verticalSizeClass == UIUserInterfaceSizeClassCompact) {

  9. size.height += 4.0;

  10. }

else

 {  
  1. size.height += 40.0;

  2. }

  3. return

    size;

  4. }

</pre>

當(dāng)有任何會影響這個Label內(nèi)容大小的事件發(fā)生時,我們應(yīng)該調(diào)用invalidateIntrinsicContentSize:

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

  1. label.text = @

    "content update"

  2. [self invalidateIntrinsicContentSize];

  3. // 或者比如當(dāng)前視圖Size Class改變的時候

    • (

    void

    )traitCollectionDidChange:(UITraitCollection *)previousTraitCollection

  4. {

  5. [

    super

    traitCollectionDidChange:previousTraitCollection];

  6. if

    ((self.traitCollection.verticalSizeClass != previousTraitCollection.verticalSizeClass)

  7. || (self.traitCollection.horizontalSizeClass != previousTraitCollection.horizontalSizeClass)) {

  8. [self invalidateIntrinsicContentSize];

  9. }

  10. }

</pre>

不是所有的視圖都有 intrinsicContentSize绞绒, UIView默認(rèn)情況下就返回的是 UIViewNoIntrinsicMetric婶希。只有當(dāng)視圖中需要根據(jù)內(nèi)部內(nèi)容進(jìn)行調(diào)整大小時,我們才需要用到 intrinsicContentSize蓬衡。

當(dāng)視圖大小在變化時喻杈,我們可以使用上面最后四個API來設(shè)置視圖的壓縮或者放大的方式。

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

  1. typedef NS_ENUM(NSInteger, UILayoutConstraintAxis) {

  2. UILayoutConstraintAxisHorizontal = 0,

  3. UILayoutConstraintAxisVertical = 1

  4. };

</pre>

上面最后四個API主要是通過修改水平或者垂直方向的優(yōu)先級來實(shí)現(xiàn)視圖是基于水平縮小(放大)還是垂直縮小(放大)狰晚。當(dāng)我們的視圖需要根據(jù)內(nèi)部內(nèi)容進(jìn)行調(diào)整大小時筒饰,我們應(yīng)該使用上述方法為當(dāng)前視圖設(shè)置初始值。而不應(yīng)該重寫這幾個方法壁晒。

UIConstraintBasedLayoutFittingSize

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

  1. UIKIT_EXTERN

    const

    CGSize UILayoutFittingCompressedSize NS_AVAILABLE_IOS(6_0);

  2. UIKIT_EXTERN

    const

    CGSize UILayoutFittingExpandedSize NS_AVAILABLE_IOS(6_0);

  3. @

    interface

    UIView (UIConstraintBasedLayoutFittingSize)

    • (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize NS_AVAILABLE_IOS(6_0);
    • (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize withHorizontalFittingPriority:(UILayoutPriority)horizontalFittingPriority verticalFittingPriority:(UILayoutPriority)verticalFittingPriority NS_AVAILABLE_IOS(8_0);
  4. @end

</pre>

上面兩個API可以獲得當(dāng)前使用AutoLayout視圖的size瓷们。其中targetSize可以傳入UILayoutFittingCompressedSize或者UILayoutFittingExpandedSize,分別對應(yīng)的是最小情況下可能的Size和最大情況下可能的Size秒咐。

UIConstraintBasedLayoutDebugging

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

    • (NSArray *)constraintsAffectingLayoutForAxis:(UILayoutConstraintAxis)axis NS_AVAILABLE_IOS(6_0);
    • (BOOL)hasAmbiguousLayout NS_AVAILABLE_IOS(6_0);
    • (

    void

    )exerciseAmbiguityInLayout NS_AVAILABLE_IOS(6_0);

</pre>

第一個API可以獲得視圖在不同方向上所有的布局約束谬晕。

hasAmbiguousLayout :可以知道當(dāng)前視圖的布局是否會有歧義。這里有一個私有API _autolayoutTrace可以獲得整個視圖樹的字符串携取。

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

  1. ifdef DEBUG

  2. NSLog(@

    "%@"

    , [self performSelector:@selector(_autolayoutTrace)]);

  3. endif

</pre>

exerciseAmbiguityInLayout :這個方法會隨機(jī)改變視圖的layout到另外一個有效的layout攒钳。這樣我們就可以很清楚的看到哪一個layout導(dǎo)致了整體的布局約束出現(xiàn)了錯誤,或者我們應(yīng)該增加更多的布局約束雷滋。

我們應(yīng)該讓上面的四個方法只在DEBUG環(huán)境下被調(diào)用不撑。

新增支持 UITraitEnvironment Protocol

<pre style="margin: 1.5em 0px; padding: 0px; font-size: 1em; width: 712.797px; overflow: auto; background: rgb(247, 247, 247); font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; line-height: 1.5em; font-family: "Andale Mono", "Lucida Console", monospace; white-space: pre;">

  1. @protocol UITraitEnvironment

  2. @property (nonatomic, readonly) UITraitCollection *traitCollection;

    • (

    void

    )traitCollectionDidChange:(UITraitCollection *)previousTraitCollection;

  3. @end

</pre>

iOS 8 上新增了Size Class的概念,其中UITraitCollection類用來描述不同Size大小晤斩。關(guān)于Size Class和UITraitCollection的概念可參考如下鏈接:http://joywii.github.io/blog/2014/09/24/ios8-size-classesde-li-jie-yu-shi-yong/

UIView實(shí)現(xiàn)了這個協(xié)議焕檬,我們可以獲得當(dāng)前View的traitCollection,從而得知當(dāng)前View處于什么樣的Size Class下尸昧。并且當(dāng)traitCollection有變化時揩页,我們可以通過重寫traitCollectionDidChange知道該事件的觸發(fā)。默認(rèn)情況下烹俗,這個方法什么都不執(zhí)行。

traitCollection的變化是從UIScreen開始被觸發(fā)萍程,并且逐層往下傳遞的幢妄,具體如下:

UIScreen -> UIWindow -> UIViewController -> ChildViewControllers -> View -> Subviews

關(guān)于這一點(diǎn),我在詳解UICoordinateSpace和UIScreen在iOS 8上的坐標(biāo)問題一文中有做詳細(xì)解釋茫负。

總結(jié) UIView到目前為止(iOS 8.1)蕉鸳,所有增加的關(guān)于AutoLayout的API請參考上述文章。進(jìn)一步對這些API理解可以讓我們寫出更健壯的布局代碼。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末潮尝,一起剝皮案震驚了整個濱河市榕吼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌勉失,老刑警劉巖羹蚣,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異乱凿,居然都是意外死亡顽素,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進(jìn)店門徒蟆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胁出,“玉大人,你說我怎么就攤上這事段审∪” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵寺枉,是天一觀的道長抑淫。 經(jīng)常有香客問我,道長型凳,這世上最難降的妖魔是什么丈冬? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮甘畅,結(jié)果婚禮上埂蕊,老公的妹妹穿的比我還像新娘。我一直安慰自己疏唾,他們只是感情好蓄氧,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著槐脏,像睡著了一般喉童。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上顿天,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天堂氯,我揣著相機(jī)與錄音,去河邊找鬼牌废。 笑死咽白,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的鸟缕。 我是一名探鬼主播晶框,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼排抬,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了授段?” 一聲冷哼從身側(cè)響起蹲蒲,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎侵贵,沒想到半個月后届搁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡模燥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年咖祭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蔫骂。...
    茶點(diǎn)故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡么翰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出辽旋,到底是詐尸還是另有隱情浩嫌,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布补胚,位于F島的核電站码耐,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏溶其。R本人自食惡果不足惜骚腥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瓶逃。 院中可真熱鬧束铭,春花似錦、人聲如沸厢绝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽昔汉。三九已至懈万,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間靶病,已是汗流浹背会通。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留娄周,地道東北人渴语。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像昆咽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評論 2 344

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