iOS基础(一) - AutoLayout入门

前言

苹果在iOS6就出了自动化布局(NSLayoutConstraint),很多第三方库就是基于原生的类的封装,比如:Masonry等。在iOS9的时候,加入了NSLayoutAnchor这个类,大大简化了原生的自动化布局。下面就从NSLayoutConstrint这个类说起。

1.NSLayoutConstraint

首先,我们需要了解到一个基本常识,就是多少约束可以最终确定一个物体的位置。我们绘画的时候会从大概一个点开始画,然后以这个点作为参照,逐步绘制整个画面。同理,确定一个物体,也先要确定一个参考点,这里称为初始点,学过数学的坐标系的同学都清楚,在平面坐标系,确定一个点需要横坐标和纵坐标,而作为一个添加在父控件的子控件,初始点的横坐标就是距离父控件的左边间距或者右边间距,纵坐标就是距离父控件的顶部间距或者底部间距。初始点确定了,我们开始绘制图形。由于绘制区域都是矩形,所以,在初始点确定之后,只需要宽和高就能绘制出一个完整的矩形。所以,至少需要四个约束,才能最终确定一个物体,两个约束确定一个初始点,两个约束确定宽和高。

废话不多说,开始进入正题:

(1)NSLayoutAttributes代表一个约束的字符串,比如:上下左右等等。
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,
    
//iOS8.0以后新增
    NSLayoutAttributeFirstBaseline,
    NSLayoutAttributeLeftMargin,
    NSLayoutAttributeRightMargin,
    NSLayoutAttributeTopMargin,
    NSLayoutAttributeBottomMargin,
    NSLayoutAttributeLeadingMargin,
    NSLayoutAttributeTrailingMargin,
    NSLayoutAttributeCenterXWithinMargins,
    NSLayoutAttributeCenterYWithinMargins,
    
    NSLayoutAttributeNotAnAttribute = 0
};

NSLayoutAttributeLeft和NSLayoutAttributeLeftMargin的区别:
NSLayoutAttributeLeft指的是控件的左边,具体来说是控件的最左边;NSLayoutAttributeLeftMargin也是指控件的左边,但是不是最左边,具体距离最左边有多大的Margin和控件的layoutMargins有关。
苹果官方的描述:

NSLayoutAttributeLeftMargin: The object's left margin.For UIView objects, the margins are defined by their layoutMargins property.

注:默认是{8,8,8,8},但是如果是viewController的root view则top和bottom的margins为0,左右margins可能是16或者20,这取决于当前的view尺寸,并且不能修改。

(2)NSLayoutRelation可以理解为一个比较符(<=,=,>=)
typedef NS_ENUM(NSInteger, NSLayoutRelation) {
    NSLayoutRelationLessThanOrEqual = -1,    //<=
    NSLayoutRelationEqual = 0,               //=
    NSLayoutRelationGreaterThanOrEqual = 1,  //>=
}

该选项是用于两个约束的比较,例如:view2的左边距离view1的右边20个点。(view2.left = view1.right * 1 + 20)

(3)UILayoutPriority优先级
static const UILayoutPriority UILayoutPriorityRequired NS_AVAILABLE_IOS(6_0) = 1000; // A required constraint.  Do not exceed this.
static const UILayoutPriority UILayoutPriorityDefaultHigh NS_AVAILABLE_IOS(6_0) = 750; // This is the priority level with which a button resists compressing its content.
static const UILayoutPriority UILayoutPriorityDefaultLow NS_AVAILABLE_IOS(6_0) = 250; // This is the priority level at which a button hugs its contents horizontally.
static const UILayoutPriority UILayoutPriorityFittingSizeLevel NS_AVAILABLE_IOS(6_0) = 50; // When you send -[UIView systemLayoutSizeFittingSize:], the size fitting most closely to the target size (the argument) is computed.  UILayoutPriorityFittingSizeLevel is the priority level with which the view wants to conform to the target size in that computation.  It's quite low.  It is generally not appropriate to make a constraint at exactly this priority.  You want to be higher or lower.

优先级只有在两个约束有冲突的时候才起作用,优先级高的会覆盖优先级低的,最高的优先级为1000。

(4)约束简单入门

🌰 一:简单为两个view添加约束
效果图:


Demo1.PNG

代码:

UIView *leftView = [UIView new];  
leftView.translatesAutoresizingMaskIntoConstraints = NO;
leftView.backgroundColor = [UIColor blueColor];
[self.view addSubview: leftView];
    
UIView *rightView = [UIView new];
rightView.translatesAutoresizingMaskIntoConstraints = NO;
rightView.backgroundColor = [UIColor greenColor];
[self.view addSubview: rightView];
    
//添加约束方式一
//调用父控件添加约束的函数,添加子控件约束
//控件约束对象必须是拥有同一父控件的子控件或者是父控件
NSLayoutConstraint *leftViewLeftConstraint = [NSLayoutConstraint constraintWithItem: leftView attribute: NSLayoutAttributeLeft relatedBy: NSLayoutRelationEqual toItem: self.view attribute: NSLayoutAttributeLeft multiplier: 1.0 constant: 20];
NSLayoutConstraint *leftViewTopConstraint = [NSLayoutConstraint constraintWithItem: leftView attribute: NSLayoutAttributeTop relatedBy: NSLayoutRelationEqual toItem: self.view attribute: NSLayoutAttributeTop multiplier: 1.0 constant: 60];
NSLayoutConstraint *leftViewWidth = [NSLayoutConstraint constraintWithItem: leftView attribute: NSLayoutAttributeWidth relatedBy: NSLayoutRelationEqual toItem: nil attribute: NSLayoutAttributeNotAnAttribute multiplier: 1.0 constant: 80];
NSLayoutConstraint *leftViewHeight = [NSLayoutConstraint constraintWithItem: leftView attribute: NSLayoutAttributeHeight relatedBy: NSLayoutRelationEqual toItem: nil attribute: NSLayoutAttributeNotAnAttribute multiplier: 1.0 constant: 80];
[self.view addConstraints: @[leftViewLeftConstraint, leftViewTopConstraint, leftViewWidth, leftViewHeight]];
    
NSLayoutConstraint *rightViewLeftConstraint = [NSLayoutConstraint constraintWithItem: rightView attribute: NSLayoutAttributeLeft relatedBy: NSLayoutRelationEqual toItem: leftView attribute: NSLayoutAttributeRight multiplier: 1.0 constant: 20];
NSLayoutConstraint *rightViewTopConstraint = [NSLayoutConstraint constraintWithItem: rightView attribute: NSLayoutAttributeTop relatedBy: NSLayoutRelationEqual toItem: self.view attribute: NSLayoutAttributeTop multiplier: 1.0 constant: 60];
NSLayoutConstraint *rightViewWith = [NSLayoutConstraint constraintWithItem: rightView attribute: NSLayoutAttributeWidth relatedBy: NSLayoutRelationEqual toItem: leftView attribute: NSLayoutAttributeWidth multiplier: 1.0 constant: 0];
NSLayoutConstraint *rightViewHeight = [NSLayoutConstraint constraintWithItem: rightView attribute: NSLayoutAttributeHeight relatedBy: NSLayoutRelationEqual toItem: leftView attribute: NSLayoutAttributeHeight multiplier: 1.0 constant: 0];
[self.view addConstraints: @[rightViewLeftConstraint, rightViewTopConstraint, rightViewWith, rightViewHeight]];

//添加约束方式二
//调用NSLayoutConstraint的类方法使约束生效(iOS8以后)
[NSLayoutConstraint activateConstraints: @[leftViewLeftConstraint, leftViewTopConstraint, leftViewWidth, leftViewHeight, rightViewLeftConstraint, rightViewTopConstraint, rightViewWith, rightViewHeight]];

//添加约束方式三
//直接将约束的active设置为YES,使约束生效(iOS8以后)
//并且方式三比方式二更为高效,苹果官方推荐用法
[NSLayoutConstraint constraintWithItem: leftView attribute: NSLayoutAttributeLeft relatedBy: NSLayoutRelationEqual toItem: self.view attribute: NSLayoutAttributeLeft multiplier: 1.0 constant: 20].active = YES;

//Tips:建议controller约束写在updateViewConstraints里面,view约束写在updateConstraints里面。

🌰 二:神奇的视图二等分
效果图:


二等分.PNG
UIView *leftView = [[UIView alloc] init];
leftView.backgroundColor = [UIColor greenColor];
leftView.translatesAutoresizingMaskIntoConstraints = NO;
    
UIView *rightView = [[UIView alloc] init];
rightView.backgroundColor = [UIColor blueColor];
rightView.translatesAutoresizingMaskIntoConstraints = NO;
    
[self.view addSubview: leftView];
[self.view addSubview: rightView];

//首先,设置leftView的约束
[NSLayoutConstraint constraintWithItem: leftView attribute: NSLayoutAttributeTop relatedBy: NSLayoutRelationEqual toItem: self.view attribute: NSLayoutAttributeTop multiplier: 1.0 constant: 84].active = YES;    //leftView顶部距离父控件84个点
[NSLayoutConstraint constraintWithItem: leftView attribute: NSLayoutAttributeLeft relatedBy: NSLayoutRelationEqual toItem: self.view attribute: NSLayoutAttributeLeft multiplier: 1.0 constant: 20].active = YES;    //leftView左边距离父控件20个点
[NSLayoutConstraint constraintWithItem: leftView attribute: NSLayoutAttributeHeight relatedBy: NSLayoutRelationEqual toItem: nil attribute: NSLayoutAttributeNotAnAttribute multiplier: 1.0 constant: 200].active = YES;    //leftView的height=200,这里宽在后面设置

//其次,设置rightView的约束
[NSLayoutConstraint constraintWithItem: rightView attribute: NSLayoutAttributeTop relatedBy: NSLayoutRelationEqual toItem: leftView attribute: NSLayoutAttributeTop multiplier: 1.0 constant: 0].active = YES;    //rightView顶部和leftView对齐
[NSLayoutConstraint constraintWithItem: rightView attribute: NSLayoutAttributeHeight relatedBy: NSLayoutRelationEqual toItem: leftView attribute: NSLayoutAttributeHeight multiplier: 1.0 constant: 0].active = YES;    //rightView的高和leftView的高对等
[NSLayoutConstraint constraintWithItem: rightView attribute: NSLayoutAttributeRight relatedBy: NSLayoutRelationEqual toItem: self.view attribute: NSLayoutAttributeRight multiplier: 1.0 constant: -20].active = YES;    //rightView的右边距离父控件20个点

//最后,二等分的魔法就要开始了
//leftView和rightView都添加了三个约束,从刚开始,我就提到,至少需要四个约束,leftView和rightView都缺少了一个宽的约束,所以,下面就是为这两个view添加上宽的约束
[NSLayoutConstraint constraintWithItem: rightView attribute: NSLayoutAttributeWidth relatedBy: NSLayoutRelationEqual toItem: leftView attribute: NSLayoutAttributeWidth multiplier: 1.0 constant: 0].active = YES;    //使leftView的width和right的width想等
[NSLayoutConstraint constraintWithItem: leftView attribute: NSLayoutAttributeRight relatedBy: NSLayoutRelationEqual toItem: rightView attribute: NSLayoutAttributeLeft multiplier: 1.0 constant: -20].active = YES;    //leftView的右边距离rightView的左边20个点

//可以把leftView和rightView看作一个整体,那么这个整体就是left=super.left+20,right=super.right-20,top=super.top+20,height=200,四个约束可以确定一个view的位置
//这时候来看内部的约束,首先,leftView.width=rightView.width,leftView.right=rightView.left-20,所以,leftView和rightView等分

🌰 三:约束动画(这里涉及到约束优先级)
效果动图:


autoLayoutAnimate.gif
//添加三个view
_redView = [UIView new];
_redView.backgroundColor = [UIColor redColor];
_redView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview: _redView];
    
_purpleView = [UIView new];
_purpleView.backgroundColor = [UIColor purpleColor];
_purpleView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview: _purpleView];
    
_grayView = [UIView new];
_grayView.backgroundColor = [UIColor grayColor];
_grayView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview: _grayView];
    
//设置三个view的约束
NSLayoutConstraint *redLeft = [NSLayoutConstraint constraintWithItem: _redView attribute: NSLayoutAttributeLeft relatedBy: NSLayoutRelationEqual toItem: self.view attribute: NSLayoutAttributeLeft multiplier: 1.0 constant: 20];
NSLayoutConstraint *redTop = [NSLayoutConstraint constraintWithItem: _redView attribute: NSLayoutAttributeTop relatedBy: NSLayoutRelationEqual toItem: self.view attribute: NSLayoutAttributeTop multiplier: 1.0 constant: 84];
NSLayoutConstraint *redHeight = [NSLayoutConstraint constraintWithItem: _redView attribute: NSLayoutAttributeHeight relatedBy: NSLayoutRelationEqual toItem: nil attribute: NSLayoutAttributeNotAnAttribute multiplier: 1.0 constant: 60];
NSLayoutConstraint *redWidth = [NSLayoutConstraint constraintWithItem: _redView attribute: NSLayoutAttributeWidth relatedBy: NSLayoutRelationEqual toItem: nil attribute: NSLayoutAttributeNotAnAttribute multiplier: 1.0 constant: 60];
[self.view addConstraints: @[redLeft, redTop, redHeight, redWidth]];
    
NSLayoutConstraint *purpleLeft = [NSLayoutConstraint constraintWithItem: _purpleView attribute: NSLayoutAttributeLeft relatedBy: NSLayoutRelationEqual toItem: _redView attribute: NSLayoutAttributeRight multiplier: 1.0 constant: 20];
NSLayoutConstraint *purpleTop = [NSLayoutConstraint constraintWithItem: _purpleView attribute: NSLayoutAttributeTop relatedBy: NSLayoutRelationEqual toItem: _redView attribute: NSLayoutAttributeTop multiplier: 1.0 constant: 0];
NSLayoutConstraint *purpleHeight = [NSLayoutConstraint constraintWithItem: _purpleView attribute: NSLayoutAttributeHeight relatedBy: NSLayoutRelationEqual toItem: _redView attribute: NSLayoutAttributeHeight multiplier: 1.0 constant: 0];
NSLayoutConstraint *purpleWidth = [NSLayoutConstraint constraintWithItem: _purpleView attribute: NSLayoutAttributeWidth relatedBy: NSLayoutRelationEqual toItem: _redView attribute: NSLayoutAttributeWidth multiplier: 1.0 constant: 0];
_purpleConstraints = @[purpleLeft, purpleTop, purpleHeight, purpleWidth];[self.view addConstraints: _purpleConstraints];
    
NSLayoutConstraint *grayLeft = [NSLayoutConstraint constraintWithItem: _grayView attribute: NSLayoutAttributeLeft relatedBy: NSLayoutRelationEqual toItem: _purpleView attribute: NSLayoutAttributeRight multiplier: 1.0 constant: 20];
NSLayoutConstraint *grayTop = [NSLayoutConstraint constraintWithItem: _grayView attribute: NSLayoutAttributeTop relatedBy: NSLayoutRelationEqual toItem: _redView attribute: NSLayoutAttributeTop multiplier: 1.0 constant: 0];
NSLayoutConstraint *grayHeight = [NSLayoutConstraint constraintWithItem: _grayView attribute: NSLayoutAttributeHeight relatedBy: NSLayoutRelationEqual toItem: _redView attribute: NSLayoutAttributeHeight multiplier: 1.0 constant: 0];
NSLayoutConstraint *grayWidth = [NSLayoutConstraint constraintWithItem: _grayView attribute: NSLayoutAttributeWidth relatedBy: NSLayoutRelationEqual toItem: _redView attribute: NSLayoutAttributeWidth multiplier: 1.0 constant: 0];
_grayConstraints = @[grayLeft, grayTop, grayHeight, grayWidth];
[self.view addConstraints: _grayConstraints];
    
//最重要的一个约束,将第三个灰色的view另外添加多一条约束grayView.left=redView.right+20(@250),优先级设置比默认的低,这样在和grayView.left=purpleView.right+20(@1000)约束有冲突的时候,系统会忽略优先级低的约束,所以,刚开始显示是红紫灰
_animateConstraint = [NSLayoutConstraint constraintWithItem: _grayView attribute: NSLayoutAttributeLeft relatedBy: NSLayoutRelationEqual toItem: _redView attribute: NSLayoutAttributeRight multiplier: 1.0 constant: 20];
_animateConstraint.priority = UILayoutPriorityDefaultLow;
[self.view addConstraint: _animateConstraint];

//触发动画
//将中间紫色的view移除
//标记页面刷新,并立刻刷新
//这时候由于第二个view被移除了,所以优先级低的约束就起作用了
[_purpleView removeFromSuperview];
[UIView animateWithDuration: 0.2 animations:^{
    [self.view setNeedsLayout];
    [self.view layoutIfNeeded];
}];

2.VFL

VFL其实也是对NSLayoutConstraint的封装,更为具像化,目的就是为了简化自动化布局。在我看来,入门有点复杂,入门后就比较简单。呵呵哒,具体见仁见智。

(1)VFL语法:
表达式 功能
H: 水平方向
V: 垂直方向
[view] 视图
l 父控件边界
>=,==,<= 约束不等式
- 间隙,连接视图和约束,视图和视图
@value 优先级
/**
 *  VFL创建约束
 *
 *
 *  @param format   传入VFL格式的约束字符串,例如:@“H:|-20-[leftView(50)]”,水平方向上,leftView左边与父控件左边相距20,redView宽度为50
 *
 *  @param opts     对齐方式
 *
 *  @param metrics  匹配format里面相同的key的字典,比如:@{@"margin": 20}, @"H:|-margin-[leftView(50)]"等同于@“H:|-20-[leftView(50)]”;
 *
 *  @param views    一个字典,包含format里面的view,比如:format: @“H:|-20-[leftView(50)]”,metrics: @{@"leftView": leftView}
 *
 *  @return 返回包含一系列约束的数组
 *
 */

+ (NSArray<__kindof NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(nullable NSDictionary<NSString *,id> *)metrics views:(NSDictionary<NSString *, id> *)views;

//options
typedef NS_OPTIONS(NSUInteger, NSLayoutFormatOptions) {
    NSLayoutFormatAlignAllLeft = (1 << NSLayoutAttributeLeft),    //所有控件左边缘对齐
    NSLayoutFormatAlignAllRight = (1 << NSLayoutAttributeRight),    
    NSLayoutFormatAlignAllTop = (1 << NSLayoutAttributeTop),    //所有控件顶部边缘对齐
    NSLayoutFormatAlignAllBottom = (1 << NSLayoutAttributeBottom),    
    NSLayoutFormatAlignAllLeading = (1 << NSLayoutAttributeLeading),    //相当于NSLayoutFormatAlignAllLeft
    NSLayoutFormatAlignAllTrailing = (1 << NSLayoutAttributeTrailing),    //相当于NSLayoutFormatAlignAllRight
    NSLayoutFormatAlignAllCenterX = (1 << NSLayoutAttributeCenterX),
    NSLayoutFormatAlignAllCenterY = (1 << NSLayoutAttributeCenterY),
    NSLayoutFormatAlignAllLastBaseline = (1 << NSLayoutAttributeLastBaseline),
    NSLayoutFormatAlignAllBaseline NS_SWIFT_UNAVAILABLE("Use 'alignAllLastBaseline' instead") = NSLayoutFormatAlignAllLastBaseline,
    NSLayoutFormatAlignAllFirstBaseline NS_ENUM_AVAILABLE_IOS(8_0) = (1 << NSLayoutAttributeFirstBaseline),
    
    NSLayoutFormatAlignmentMask = 0xFFFF,
    
    /* choose only one of these three
     */
    NSLayoutFormatDirectionLeadingToTrailing = 0 << 16, // default
    NSLayoutFormatDirectionLeftToRight = 1 << 16,
    NSLayoutFormatDirectionRightToLeft = 2 << 16,  
    
    NSLayoutFormatDirectionMask = 0x3 << 16,  
};
(2)重写NSLayoutConstraint简单入门的例子

🌰 一:简单为两个view添加约束

UIView *leftView = [UIView new];
leftView.translatesAutoresizingMaskIntoConstraints = NO;
leftView.backgroundColor = [UIColor blueColor];
[self.view addSubview: leftView];
    
UIView *rightView = [UIView new];
rightView.translatesAutoresizingMaskIntoConstraints = NO;
rightView.backgroundColor = [UIColor greenColor];
[self.view addSubview: rightView];
    
NSString *hConstraint = @"H:|-20-[leftView(80)]-20-[rightView(==leftView)]";
NSString *vConstraint = @"V:|-84-[leftView(80)]";
NSArray *hConstraints = [NSLayoutConstraint constraintsWithVisualFormat: hConstraint options: NSLayoutFormatAlignAllTop|NSLayoutFormatAlignAllBottom metrics: nil views: NSDictionaryOfVariableBindings(leftView, rightView)];
NSArray *vConstraints = [NSLayoutConstraint constraintsWithVisualFormat: vConstraint options: NSLayoutFormatAlignAllTop metrics: nil views: NSDictionaryOfVariableBindings(leftView, rightView)];
[self.view addConstraints: hConstraints];
[self.view addConstraints: vConstraints];

🌰 二:神奇的视图二等分

UIView *leftView = [[UIView alloc] init];
leftView.backgroundColor = [UIColor greenColor];
leftView.translatesAutoresizingMaskIntoConstraints = NO;
    
UIView *rightView = [[UIView alloc] init];
rightView.backgroundColor = [UIColor blueColor];
rightView.translatesAutoresizingMaskIntoConstraints = NO;
    
[self.view addSubview: leftView];
[self.view addSubview: rightView];
    
NSString *hConstraint = @"H:|-margin-[leftView]-margin-[rightView(==leftView)]-20-|";
NSString *vConstraint = @"V:|-margin-[leftView(200)]";
    
NSArray *hConstraints = [NSLayoutConstraint constraintsWithVisualFormat: hConstraint options: NSLayoutFormatAlignAllTop | NSLayoutFormatAlignAllBottom metrics: @{@"margin": @"20"} views: NSDictionaryOfVariableBindings(leftView, rightView)];
NSArray *vConstraints = [NSLayoutConstraint constraintsWithVisualFormat: vConstraint options: 0 metrics: @{@"margin": @"84"} views: NSDictionaryOfVariableBindings(leftView, rightView)];
[self.view addConstraints: hConstraints];
[self.view addConstraints: vConstraints];

🌰 三:约束动画(这里涉及到约束优先级)

_redView = [UIView new];
_redView.backgroundColor = [UIColor redColor];
_redView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview: _redView];
    
_purpleView = [UIView new];
_purpleView.backgroundColor = [UIColor purpleColor];
_purpleView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview: _purpleView];
    
_grayView = [UIView new];
_grayView.backgroundColor = [UIColor grayColor];
_grayView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview: _grayView];
    
NSString *hConstraint = @"H:|-margin-[_redView(60)]-margin-[_purpleView(==_redView)]-margin-[_grayView(==_redView)]";
NSString *vConstraint = @"V:|-margin-[_redView(60)]";
NSString *hConstraint2 = @"H:[_redView(60)]-margin@250-[_grayView(==_redView)]";
    
NSArray *hConstraints = [NSLayoutConstraint constraintsWithVisualFormat: hConstraint options: NSLayoutFormatAlignAllTop|NSLayoutFormatAlignAllBottom metrics: @{@"margin": @20} views: NSDictionaryOfVariableBindings(_redView, _purpleView, _grayView)];
NSArray *vConstraints = [NSLayoutConstraint constraintsWithVisualFormat: vConstraint options: 0 metrics: @{@"margin": @84} views: NSDictionaryOfVariableBindings(_redView, _purpleView, _grayView)];
NSArray *hConstraints2 = [NSLayoutConstraint constraintsWithVisualFormat: hConstraint2 options: NSLayoutFormatAlignAllTop|NSLayoutFormatAlignAllBottom metrics: @{@"margin": @20} views: NSDictionaryOfVariableBindings(_redView, _grayView)];
[self.view addConstraints: hConstraints];
[self.view addConstraints: vConstraints];
[self.view addConstraints: hConstraints2];

//触发动画
[_purpleView removeFromSuperview];
[UIView animateWithDuration: 0.2 animations:^{
    [self.view setNeedsLayout];
    [self.view layoutIfNeeded];
}];

另外添加一个🌰 ,就是两个view不等高的情况:
效果图:


例子四.PNG
//这里利用优先级不同来解决问题
UIView *leftView = [UIView new];
leftView.translatesAutoresizingMaskIntoConstraints = NO;
leftView.backgroundColor = [UIColor blueColor];
[self.view addSubview: leftView];
    
UIView *rightView = [UIView new];
rightView.translatesAutoresizingMaskIntoConstraints = NO;
rightView.backgroundColor = [UIColor greenColor];
[self.view addSubview: rightView];
    
NSString *hConstraint = @"H:|-20-[leftView(80)]-20-[rightView(==leftView)]";
NSString *vConstraint = @"V:|-84-[leftView(80@250)]";    //先设置优先级低于默认优先级
NSString *vConstraint2 = @"V:[rightView(120)]";    //默认优先级1000,会覆盖前面优先级低的约束
NSArray *hConstraints = [NSLayoutConstraint constraintsWithVisualFormat: hConstraint options: NSLayoutFormatAlignAllTop metrics: nil views: NSDictionaryOfVariableBindings(leftView, rightView)];
NSArray *vConstraints = [NSLayoutConstraint constraintsWithVisualFormat: vConstraint options: 0 metrics: nil views: NSDictionaryOfVariableBindings(leftView, rightView)];
NSArray *vConstraints2 = [NSLayoutConstraint constraintsWithVisualFormat: vConstraint2 options: 0 metrics: nil views: NSDictionaryOfVariableBindings(rightView)];
[self.view addConstraints: hConstraints];
[self.view addConstraints: vConstraints];
[self.view addConstraints: vConstraints2];

3.NSLayoutAnchor

NSLayourAnchor是iOS9之后出来的,总共包含NSLayoutXAxisAnchor,NSLayoutYAxisAnchor以及NSLayoutDimension,分别代表x坐标,y坐标和宽高。

NSLayoutAnchor属性
/* Constraint creation conveniences. See NSLayoutAnchor.h for details.
 */
@property(readonly, strong) NSLayoutXAxisAnchor *leadingAnchor;    //x坐标
@property(readonly, strong) NSLayoutXAxisAnchor *trailingAnchor;
@property(readonly, strong) NSLayoutXAxisAnchor *leftAnchor;
@property(readonly, strong) NSLayoutXAxisAnchor *rightAnchor;
@property(readonly, strong) NSLayoutYAxisAnchor *topAnchor;    //y坐标
@property(readonly, strong) NSLayoutYAxisAnchor *bottomAnchor;
@property(readonly, strong) NSLayoutDimension *widthAnchor;    //宽
@property(readonly, strong) NSLayoutDimension *heightAnchor;    //高
@property(readonly, strong) NSLayoutXAxisAnchor *centerXAnchor;
@property(readonly, strong) NSLayoutYAxisAnchor *centerYAnchor;
@property(readonly, strong) NSLayoutYAxisAnchor *firstBaselineAnchor;
@property(readonly, strong) NSLayoutYAxisAnchor *lastBaselineAnchor;

举个简单的例子:
效果图:


NSLayoutAnchor.PNG
_testLayoutAnchor = [[UIView alloc] init];
_testLayoutAnchor.backgroundColor = [UIColor magentaColor];
_testLayoutAnchor.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview: _testLayoutAnchor];
    
[_testLayoutAnchor.topAnchor constraintEqualToAnchor: self.view.topAnchor constant: 84].active = YES;    //距离顶部anchor84
[_testLayoutAnchor.leadingAnchor constraintEqualToAnchor: self.view.leadingAnchor constant: 20].active = YES;    //距离左边anchor20
[_testLayoutAnchor.widthAnchor constraintEqualToConstant: 100].active = YES;    //宽100
[_testLayoutAnchor.heightAnchor constraintEqualToConstant: 100].active = YES;    //高100

4.小结

iOS约束的实现,或者第三方库约束的实现都是基于NSLayoutConstraint来封装的,万变不离其中,主要熟悉了NSLayoutConstraint,一般的自动化布局也就变的简单。下一章,解析一下,Masonry的源代码。

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

推荐阅读更多精彩内容