iOS:原生代码添加约束

Width:对视图宽度的约束
Height:对视图高度的约束
Horizontal Spacing:对视图间水平距离的约束
Vertical Spacing:对视图间垂直距离的约束
Leading Space to Superview:与父视图左边界的约束
Trailing Space to Superview:与父视图右边界的约束
Top Space to Superview:与父视图上边界的约束
Bottom Space to Superview:与父视图下边界的约束
Widths Equally:视图等宽约束
Heights Equally:视图等高约束

Auto Layout 中约束对应的类为 NSLayoutConstraint,一个 NSLayoutConstraint 实例代表一条约束。 (在iOS6之后,引入了autolayout这个概念,相应的也增加了NSLayoutConstraint这个对象,这个对象就是专门用来进行约束布局的设置对象。通过这个对象,我们可以设置类似视图对象之间的间距,约束的宽高,比例等属性。创建NSLayoutConstraint对象的方法有两种:)

NSLayoutConstraint有两个方法,我们主要介绍 constraintWithItem:也是最常用的。

创建NSLayoutConstraint对象
+ (instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullable id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c API_AVAILABLE(macos(10.7), ios(6.0), tvos(9.0));

意思是:(描述的是一个view与另外一个view的位置和大小约束关系。)
view1:要添加约束的视图对象。
view2:与之对应添加约束的视图对象,例如,如过我要设置view1的上边距离父视图的上边一定间距,这个view2就是view1的父视图。
attribute(属性):有上、下、左、右、宽、高等。
relation(关系):有(<=,==,>=)小于等于、等于、大于等于。(注意:小于等于或大于等于优先会使用等于关系,如果等于不能满足,才会使用小于或大于。例如设置一个大于等于100 的关系,默认会是 100,当视图被拉伸时,100 无法被满足,尺寸才会变得更大。)
multiplier(比例):约束的比例,比如view1的宽是view2的宽的两倍,这个multiplie就是2.

AutoLayout 的核心计算公式:

view1.attribute1 = view2.attribute2 * multiplier + constant value
item1 =(>=,<=) multiplier * item2 + constant。

举个简单的例子来说我们想设置第一个视图的宽度是第二个视图宽度的2倍,我们可以这样写:

[self.view addConstraint:[NSLayoutConstraint constraintWithItem:view2 attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:view1 attribute:NSLayoutAttributeWidth multiplier:2 constant:0]];

在这个方程式中,attribute1 和 attribute2 是自动布局可以调整时解决这些制约因素的变量。当你创建约束定义其他值。例如,如果您定义两个按钮的相对位置,第二个按钮的起始位置距离第一个按钮结束位置后8像素点。这种关系为线性方程如下所示:

 button2.leading = 1.0 × button1.trailing + 8.0

例如一: 假如我们设计一个简单的页面。一个子view在父view中,其中子view的上下左右边缘都离父view的边缘40个像素。如下:

//设置背景颜色
self.view.backgroundColor = [UIColor redColor];
//创建子view
UIView *subView = [[UIView alloc]init];
//设置子view背景颜色
subView.backgroundColor = [UIColor blackColor];
//将子view添加到父视图上
[self.view addSubview:subView];
//使用Auto Layout约束,禁止将Autoresizing Mask转换为约束
[subView setTranslatesAutoresizingMaskIntoConstraints:NO];
//layout 子view
//子view的上边缘离父view的上边缘40个像素
NSLayoutConstraint *contraint1 = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:40.0];
//子view的左边缘离父view的左边缘40个像素
NSLayoutConstraint *contraint2 = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:40.0];
//子view的下边缘离父view的下边缘40个像素
NSLayoutConstraint *contraint3 = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-40.0];
//子view的右边缘离父view的右边缘40个像素
NSLayoutConstraint *contraint4 = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0 constant:-40.0];
//把约束添加到父视图上
NSArray *array = [NSArray arrayWithObjects:contraint1,contraint2,contraint3,contraint4, nil];
[self.view addConstraints:array];

注意事项:

  1. 添加约束前确定已经把需要布局的子view添加到父view上了
    (添加约束之前,一定要保证相关控件都已经在各自的父控件上。用上面的例子就是 [self.view
    addSubview:purpleView]; 一定要放在添加 left 约束之前,否则程序会 crash-崩溃,因为要确保
    purpleView 要已经在 self.view 上了。建议先写 [self.view addSubview:purpleView];
    之后,再专心写约束。)

  2. 一定要禁止将Autoresizing Mask转换为约束
    view.translatesAutoresizingMaskIntoConstraints = NO; (要先禁止
    autoresizing 功能,防止 AutoresizingMask 转换成 Constraints,避免造成冲突,需要设置 view
    的下面属性为 NO:)

  3. 要把子view的约束加在父view上

  4. 因为iOS中原点在左上角所以使用offset时注意right和bottom用负数

  5. 不用再给 view 设置 frame

例如二: 子view在父view的中间,且子view长300,高200。

//设置背景颜色
[self.view setBackgroundColor:[UIColor redColor]];
//创建子view
UIView *subView = [[UIView alloc] init];
//设置子view背景颜色
[subView setBackgroundColor:[UIColor blackColor]];
//将子view添加到父视图上
[self.view addSubview:subView];
//使用Auto Layout约束,禁止将Autoresizing Mask转换为约束
[subView setTranslatesAutoresizingMaskIntoConstraints:NO];
//layout 子view
//子view的中心横坐标等于父view的中心横坐标 (水平居中)  
NSLayoutConstraint *constrant1 = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0];
 
//子view的中心纵坐标等于父view的中心纵坐标(垂直居中)
NSLayoutConstraint *constrant2 = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0];
 
//子view的宽度为300 这里直接设置自身宽为300
NSLayoutConstraint *constrant3 = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:300.0];
 
//设置自身宽,宽可以参照其他控件设置,比如是self.view宽的一半,则应该这样写
 
//参照其他控件设置位置 
NSLayoutConstraint *contraint = [NSLayoutConstraint constraintWithItem:libraryView attribute:NSLayoutAttributeToprelatedBy:NSLayoutRelationEqual toItem:labelLibrary attribute:NSLayoutAttributeBottom multiplier:1.0 constant:20.0];

//距离父视图底部40
NSLayoutConstraint *contraint = [NSLayoutConstraint constraintWithItem:btn attribute:NSLayoutAttributeBottomrelatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-40.0];
 
//居中显示
NSLayoutConstraint *contraint = [NSLayoutConstraint constraintWithItem:clickLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:bgView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0];
 
NSLayoutConstraint *contraint = [NSLayoutConstraint constraintWithItem:clickLabel attribute:NSLayoutAttributeCenterXrelatedBy:NSLayoutRelationEqual toItem:bgView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0];
 
//子view的高度为200
NSLayoutConstraint *constrant4 = [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:200.0];
 
//把约束添加到父视图上
NSArray *array = [NSArray arrayWithObjects:contraint1,contraint2,contraint3,contraint4, nil];
 
[self.view addConstraints:array];
 
//可以直接用addConstraints
[self.view addConstraints:@[[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0], [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0], [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:200.0], [NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:300.0]]];

注意事项:

  1. 如果是设置view自身的属性,不涉及到与其他view的位置约束关系。 比如view自身的宽、高等约束时,方法constraintWithItem:的第四个参数view2(secondItem)应设为nil;且第五个参数attribute2(secondAttribute)应设为 NSLayoutAttributeNotAnAttribute 。
  2. 在设置宽和高这两个约束时,relatedBy参数使用的是 NSLayoutRelationGreaterThanOrEqual,而不是 NSLayoutRelationEqual。
    因为 Auto Layout 是相对布局,所以通常你不应该直接设置宽度和高度这种固定不变的值,除非你很确定视图的宽度或高度需要保持不变。
添加约束(addConstraint)的规则

在创建约束了之后,需要将其添加到作用的控件上才能生效,注意在添加约束的时候目标控件需要遵循以下规则(这里控件就用 view 表示):

  1. 对于两个同层级 view 之间的约束关系,添加到它们的父 view 上
  2. 对于两个不同层级 view 之间的约束关系,添加到他们最近的共同父 view 上
  3. 对于有层次关系的两个 view 之间的约束关系,添加到层次较高的父 view 上
  4. 对于比如长宽之类的,只作用在该 view 自己身上的话,添加到该 view 自己上。

可以看出,widthConstraint 和 Constraint 属于第(4)种,leftConstraint 和 rightConstraint 属于第(3)种。

例如四:
topView在父视图的左边间距10,下边间距-10,宽度为100,高度为20的实现代码

UIView *topView = [[UIView alloc] init];
topView.backgroundColor = [UIColor redColor];
[self.view addSubview:topView];
topView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addConstraints:@[[NSLayoutConstraint constraintWithItem:topView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:10], [NSLayoutConstraint constraintWithItem:topView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-10], [NSLayoutConstraint constraintWithItem:topView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:100],[NSLayoutConstraint constraintWithItem:topView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:20]]];
Auto Layout 关于更新约束的几个方法
  • setNeedsLayout:告知页面需要更新,但是不会立刻开始更新。执行后会立刻调用layoutSubviews。
  • layoutIfNeeded:告知页面布局立刻更新。所以一般都会和setNeedsLayout一起使用。如果希望立刻生成新的frame需要调用此方法,利用这点一般布局动画可以在更新布局后直接使用这个方法让动画生效。
  • layoutSubviews:系统重写布局。
  • setNeedsUpdateConstraints:告知需要更新约束,但是不会立刻开始。
  • updateConstraintsIfNeeded:告知立刻更新约束。
  • updateConstraints:系统更新约束。

这么多方法中,目前使用比较多的是 layoutIfNeeded 。因为在Auto Layout 实现动画的时候,layoutIfNeeded 方法可以立刻生成新的frame特性是一大利器。

你可能感兴趣的:(iOS)