iOS 常用布局方式之Constraint

级别: ★☆☆☆☆
标签:「iOS AutoLayout」「iOS 自动布局」「NSLayoutConstraint」
作者: Xs·H
审校: QiShare团队

沐灵洛 线下分享iOS UIButton根据内容自动布局时,有和前端同学讨论到iOS的常用布局方式。讨论过程十分热闹,不容易记录,但作者认为讨论结果有必要记录一下,希望能帮助到一些同学。
作者将iOS常用布局方式归纳为Frame、Autoresizing、Constraint、StackView和Masonry五种,并将逐一介绍。
本篇文章介绍Constraint。

Constraint相较于Autoresizing要更加灵活和强大,可以说是一种替代方案。Constraint的全称是NSLayoutConstraint,也常被称作AutoLayout,配合着Storyboard可以非常方便地构建页面。比如作者在上篇文章中没有实现的同级视图之间约束问题,使用NSLayoutConstraint将迎刃而解,并且不需要编写代码。在Storyboard中构建的约束关系如下。

当然,开发者也可以使用代码的形式利用NSLayoutConstraint布局视图。比如,作者在4等分视图的基础上,在浅灰色contentView上添加一个藏青色(cyanColor)的subView5,使其始终以固定的宽高居中显示,也就是实现下图中的效果。

实现上述效果的代码如下。

- (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所控制(代码如下)。否则,会出现约束冲突的情况。

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等宽可以这样写:

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

其中,attr1和attr2都是从NSLayoutAttribute枚举中取值。

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中查看。


推荐文章:
iOS UIButton根据内容自动布局
iOS 指定初始化方法
UIView中的hitTest方法

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,718评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,683评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,207评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,755评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,862评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,050评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,136评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,882评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,330评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,651评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,789评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,477评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,135评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,864评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,099评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,598评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,697评论 2 351

推荐阅读更多精彩内容