iOS 經常使用佈局方式之Constraint

級別: ★★☆☆☆
標籤:「iOS AutoLayout」「iOS 自動佈局」「NSLayoutConstraint」
做者: Xs·H
審校: QiShare團隊php

沐靈洛 線下分享iOS UIButton根據內容自動佈局時,有和前端同窗討論到iOS的經常使用佈局方式。討論過程十分熱鬧,不容易記錄,但做者認爲討論結果有必要記錄一下,但願能幫助到一些同窗。 做者將iOS經常使用佈局方式概括爲Frame、Autoresizing、Constraint、StackView和Masonry五種,並將逐一介紹。 本篇文章介紹Constraint。前端

Constraint相較於Autoresizing要更加靈活和強大,能夠說是一種替代方案。Constraint的全稱是NSLayoutConstraint,也常被稱做AutoLayout,配合着Storyboard能夠很是方便地構建頁面。好比做者在上篇文章中沒有實現的同級視圖之間約束問題,使用NSLayoutConstraint將迎刃而解,而且不須要編寫代碼。在Storyboard中構建的約束關係以下。git

固然,開發者也可使用代碼的形式利用NSLayoutConstraint佈局視圖。好比,做者在4等分視圖的基礎上,在淺灰色contentView上添加一個藏青色(cyanColor)的subView5,使其始終以固定的寬高居中顯示,也就是實現下圖中的效果。github

實現上述效果的代碼以下。編程

- (void)viewDidLoad {
    
    [super viewDidLoad];
    
    UIView *subView5 = [[UIView alloc] initWithFrame:CGRectZero];
    subView5.backgroundColor = [[UIColor cyanColor] colorWithAlphaComponent:.6];
    subView5.translatesAutoresizingMaskIntoConstraints = NO;
    [_contentView addSubview:subView5];
    
    NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:subView5 attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:100.0];
    NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:subView5 attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:200.0];
    NSLayoutConstraint *centerXConstraint = [NSLayoutConstraint constraintWithItem:subView5 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:_contentView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:.0];
    NSLayoutConstraint *centerYConstraint = [NSLayoutConstraint constraintWithItem:subView5 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:_contentView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:.0];
    
    [_contentView addConstraints:@[widthConstraint, heightConstraint, centerXConstraint, centerYConstraint]];
}
複製代碼

經過上述代碼,看一下NSLayoutConstraint的用法。 首先,在使用代碼利用NSLayoutConstraint佈局視圖時,要先指明該視圖不被Autoresizing所控制(代碼以下)。不然,會出現約束衝突的狀況。bash

subView5.translatesAutoresizingMaskIntoConstraints = NO;
複製代碼

而後,約束視圖是經過「設定約束」和「添加約束」兩個步驟來完成的。微信

設定約束

NSLayoutConstraint有標準的API來設定約束,以下。佈局

/* Create constraints explicitly.  Constraints are of the form "view1.attr1 = view2.attr2 * multiplier + constant" 
 If your equation does not have a second view and attribute, use nil and NSLayoutAttributeNotAnAttribute.
 */
+ (instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullable id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;
複製代碼

通俗地解釋一下上面的API:對view1的attr1屬性和view2的attr2屬性以relation這種關係和multiplier這種倍數進行c數值的約束。 好比,約束view1和view2等寬能夠這樣寫:spa

[NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:view2 attribute:NSLayoutAttributeWidth multiplier:1.0 constant:.0];
複製代碼

其中,attr1和attr2都是從NSLayoutAttribute枚舉中取值。3d

typedef NS_ENUM(NSInteger, NSLayoutAttribute) {
    NSLayoutAttributeLeft = 1,
    NSLayoutAttributeRight,
    NSLayoutAttributeTop,
    NSLayoutAttributeBottom,
    NSLayoutAttributeLeading,
    NSLayoutAttributeTrailing,
    NSLayoutAttributeWidth,
    NSLayoutAttributeHeight,
    NSLayoutAttributeCenterX,
    NSLayoutAttributeCenterY,
    NSLayoutAttributeLastBaseline,
    NSLayoutAttributeBaseline NS_SWIFT_UNAVAILABLE("Use 'lastBaseline' instead") = NSLayoutAttributeLastBaseline,
    NSLayoutAttributeFirstBaseline NS_ENUM_AVAILABLE_IOS(8_0),
    
    NSLayoutAttributeLeftMargin NS_ENUM_AVAILABLE_IOS(8_0),
    NSLayoutAttributeRightMargin NS_ENUM_AVAILABLE_IOS(8_0),
    NSLayoutAttributeTopMargin NS_ENUM_AVAILABLE_IOS(8_0),
    NSLayoutAttributeBottomMargin NS_ENUM_AVAILABLE_IOS(8_0),
    NSLayoutAttributeLeadingMargin NS_ENUM_AVAILABLE_IOS(8_0),
    NSLayoutAttributeTrailingMargin NS_ENUM_AVAILABLE_IOS(8_0),
    NSLayoutAttributeCenterXWithinMargins NS_ENUM_AVAILABLE_IOS(8_0),
    NSLayoutAttributeCenterYWithinMargins NS_ENUM_AVAILABLE_IOS(8_0),
    
    NSLayoutAttributeNotAnAttribute = 0
};
複製代碼

relation是從NSLayoutRelation枚舉中取值。

typedef NS_ENUM(NSInteger, NSLayoutRelation) {
    NSLayoutRelationLessThanOrEqual = -1,
    NSLayoutRelationEqual = 0,
    NSLayoutRelationGreaterThanOrEqual = 1,
};
複製代碼

綜上,在理解API各參數含義的基礎上將會更容易讀懂上述subView5的4個約束。

添加約束

先看一下例子中添加約束的代碼,以下。

[_contentView addConstraints:@[widthConstraint, heightConstraint, centerXConstraint, centerYConstraint]];
複製代碼

做者將4個約束都添加到了contentView上面,固然,運行效果證實這樣添加沒有問題。但有的同窗問道:「 由於subView5的位置被約束到了contentView上,因此centerXConstraint和centerYConstraint被添加到contentView上是容易被理解的,可是widthConstraint和heightConstraint只和subView5有關係,爲何也添加到了contentView上,不是應該添加到subView5上嗎?」。是的,按照這種說法,widthConstraint和heightConstraint添加到subView5的確更容易理解,因而做者作了以下修改。

[subView5 addConstraints:@[widthConstraint, heightConstraint]];
[_contentView addConstraints:@[centerXConstraint, centerYConstraint]];
複製代碼

從修改後的運行效果看也是沒有問題的。之因此將4個constraint都添加到了contentView上,是由於**無論是對哪一個視圖的約束,只要添加到該視圖或者該視圖的父視圖以及更高層級的父視圖上,都是沒有問題的。**因此,在編程中,開發者經常會將多個約束統一添加到某個比較靠近用戶的父視圖上。

關於本篇文章的具體實現細節能夠在QiLayoutDemo中查看。


小編微信:可加並拉入《QiShare技術交流羣》。

關注咱們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公衆號)

推薦文章:
iOS UIButton根據內容自動佈局
iOS 指定初始化方法
UIView中的hitTest方法
奇舞週刊

相關文章
相關標籤/搜索