版权声明:本文为博主原创文章,未经博主允许不得转载
关于视图的布局的权威参考文档,首当其中的肯定是apple的官方文档了。这里推荐apple的Auto Layout Guide,当然也可以参考我的一篇译文Auto Layout Guideps:译的很烂,凑合看吧!在该文的最后有几篇apple推荐阅读的文档,坚持看完必定会有很大的收获的。
在iphone4s及其之前的iphone,屏幕尺寸一直是固定的3.5英寸,硬件分辨率为320*480。那时候不存在屏幕适配的问题(当然排除同时兼容ipad和iphone),直接用比较粗暴的方式把一个view的位置写死,比如下面的
- (void)viewDidLoad {
[super viewDidLoad];
UIView *view = [[UIView alloc] init];
view.backgroundColor = [UIColor orangeColor];
view.frame = CGRectMake(50, 50, 100, 100);
[self.view addSubview:view];
}
产生的效果如下:
从2012年9月iphone5的开始,把屏幕的尺寸从之前4s的3.5英寸升级为4英寸。iphone5上市初期,由于各大app公司还没有进行适配,iphone5的屏幕比4s的长(iphone5为320*568,4s为320*480。当时大家都开始调侃到:若干年后的iphone会变成一把上方宝剑,哈哈,足够长!),导致当时未适配的app的上面和下面出现了黑条,当然很快各大app都适配了iphone5。由于屏幕只是变长了,因此适配iphone5工作量不是很大(相对以后iphone6的适配来说)。2014年9月,apple发布了iphone6和6plus,这次屏幕继续变大,分辨率为375*667、414*736,因此如果继续采用frame的写法将会是一个很大的挑战......要是以后再出现其他分辨率的手机呢?苹果设备也出现了碎片化......这个时候是该用一种全新的方法来适配手机屏幕了。之前frame的写法可以认为是一种“绝对布局”的方法,而我们现在需要用一种称为“相对布局”的方法来解决这个问题,Auto Layout正是解决这个问题的一剂良药,而这一解决方案采用了一种称之为“约束”的布局思想。
创建布局约束
创建布局约束主要有3种方法:(这里暂时只讨论apple的方法,第三方的布局库我们以后再讨论,比如Masonry)
-
在IB中使用Auto Layout。
-
用可视化格式语言描述的约束。(+ constraintsWithVisualFormat:options:metrics:views:)
-
为每一个组件提供一个基本关系,从而构建NSLayoutConstraint类的实例。(+ constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:)
尽管很多iOS的入门书籍中绝大多数都是采用IB来介绍iOS开发的,但是绝大多数企业都是采用纯代码的方法来构建app,因此我们这里主要来讨论采用上面的后两种方式,即采用纯代码的方式进行UI创建和适配。关于使用IB还是纯代码方式进行iOS开发,可以参考一篇博文“iOS开发中的争议二”
第二种方法和第三种方法其实都是使用纯代码创建NSLayoutConstraint的实例(第二种方法一次创建多个实例组成的数组),可以参考apple的官方文档NSLayoutConstraint类官方文档
,从中可以看到,创建NSLayoutConstraint一共有两个类方法:
+ constraintsWithVisualFormat:options:metrics:views:
+ constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:
在本博文中,我们把上面3种方法中的第二种方法称为“创建可视化语言描述的约束”,第三种方法称为“创建一般的约束”。下面我们分别讨论这两种方法。
创建一般的约束
从NSLayoutConstraint.h中我们可以看到创建一般约束对象的类方法声明:
+(instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullable id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;
该类方法中一共有7个参数,参数这么多........还能不能愉快的编程了???不过仔细看一下,其实没有那么复杂了。
参数 | 参数的作用 | 参数类型 |
---|---|---|
参数1:view1 | 需要添加约束的视图 | 一个UIView及其子类 |
参数2:attr1 | 指定参数1需要做什么样的约束 | 枚举量NSLayoutAttribute |
参数3:relation | 与参照视图属性之间的关系,比如等于、小于、大于等 | 枚举量NSLayoutRelation |
参数4:view2 | 参照的视图 | NSLayoutRelation |
参数5:attr2 | 指定参数2需要做什么样的约束 | 枚举量NSLayoutAttribute |
参数6:multiplier | 倍数 | CGFloat类型 |
参数7:c | 加数 | CGFloat |
上述关系中view1.attr1 = view2.attr2 *multiplier + c (假如这里relation为相等关系)
其中参数2和参数3的枚举量定义如下:
typedef NS_ENUM(NSInteger, NSLayoutRelation) {
NSLayoutRelationLessThanOrEqual = -1, // 小于等于关系
NSLayoutRelationEqual = 0, // 等于关系
NSLayoutRelationGreaterThanOrEqual = 1, // 大于等于关系
};
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
};
说了那么多啰嗦的理论,现在我们进入实战。假如我们有这样的需要:需要产生一个宽度和高度都为100点的正方形方块,且它位于屏幕中心。入下图所示。
- (void)createOneView {
// 创建视图
UIView *view = [[UIView alloc] init];
view.backgroundColor = [UIColor orangeColor];
// 关闭视图的autoresizing属性!!!!
view.translatesAutoresizingMaskIntoConstraints = NO;
// 把视图添加到self.view视图上
[self.view addSubview:view];
// 创建一个宽度的约束,宽度指定为100
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1
constant:100];
// 创建一个高度约束,高度指定为100
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1
constant:100];
// 创建一个view的水平居中方向的约束,使view的水平中心等于self.view的水平中心
NSLayoutConstraint *centerXConstraint = [NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0];
// 创建一个view的竖直居中方向的约束,使view的竖直中心等于self.view的水平中心
NSLayoutConstraint *centerYConstraint = [NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1
constant:0];
// 添加约束。创建后一定要添加约束才有效,并且约束一定要添加到该视图的最近父视图上。当然也能单独添加。
[self.view addConstraints:@[widthConstraint, heightConstraint, centerXConstraint, centerYConstraint]];
}
下面我们再来实现一个这样的需求:创建一个包含3个矩形块的界面,其中矩形块与屏幕周边的距离都为20点,矩形块的边与边也相距20点,三个矩形块一样高,上面两个宽度一样。具体的样子入下图所示:
实现代码如下:
- (void)createThreeViews {
// 创建3个view对象
UIView *leftView = [[UIView alloc] init];
UIView *rightView = [[UIView alloc] init];
UIView *bottomView = [[UIView alloc] init];
// 设置背景颜色
leftView.backgroundColor = [UIColor greenColor];
rightView.backgroundColor = [UIColor purpleColor];
bottomView.backgroundColor = [UIColor orangeColor];
// 添加到视图上面显示
[self.view addSubview:leftView];
[self.view addSubview:rightView];
[self.view addSubview:bottomView];
// 关闭系统的自定义布局
leftView.translatesAutoresizingMaskIntoConstraints = NO;
rightView.translatesAutoresizingMaskIntoConstraints = NO;
bottomView.translatesAutoresizingMaskIntoConstraints = NO;
// leftView.top = self.view.top + 20
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:leftView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTop
multiplier:1
constant:20]];
// leftView.Left = self.view.left + 20
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:leftView
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeft
multiplier:1
constant:20]];
// rightView.top = leftView.top
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:rightView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:leftView
attribute:NSLayoutAttributeTop
multiplier:1
constant:0]];
// rightView.Left = leftView.right + 20
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:rightView
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:leftView
attribute:NSLayoutAttributeRight
multiplier:1
constant:20]];
// rightView.Right = self.view.Right - 20
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:rightView
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeRight
multiplier:1
constant:-20]];
// rightView.Height = leftView.Height
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:rightView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:leftView
attribute:NSLayoutAttributeHeight
multiplier:1
constant:0]];
// rightView.Width = leftView.Width
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:rightView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:leftView
attribute:NSLayoutAttributeWidth
multiplier:1
constant:0]];
// bottomView.Left = self.view.Left + 20
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:bottomView
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeft
multiplier:1
constant:20]];
// bottomView.Right = self.view.Right - 20
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:bottomView
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeRight
multiplier:1
constant:-20]];
// bottomView.Top = leftView.Bottom + 20
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:bottomView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:leftView
attribute:NSLayoutAttributeBottom
multiplier:1
constant:20]];
// bottomView.Height = leftView.Height
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:bottomView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:leftView
attribute:NSLayoutAttributeHeight
multiplier:1
constant:0]];
// bottomView.Bottom = self.view.bottom - 20
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:bottomView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1
constant:-20]];
}
创建可视化语言描述的约束
待续...