文章目录
简介
在苹果推出iPhone5之前,苹果的屏幕分辨率只有一种,因此对于iOS开发者来说,是不需要像Android工程师一样去考虑屏幕适配问题的。但随着iPhone5的发布,iPhone的屏幕出现了3.5寸和4.0寸两种尺寸,这种情况下,仍然可以分别为两种尺寸编写各自的界面布局样式,但随着iPhone6以及iPhone6 Plus的发布,iPhone的屏幕尺寸变为4种,考虑到还存在横屏以及竖屏两种展示方式,此时就不能够再为每一种屏幕尺寸来编写单独的布局样式了。而苹果为多屏幕适配提供的解决方案就是自动布局(Auto Layout)。
工作原理
在Auto Layout中的基本工具就是约束(constraint),一个约束描述了两个视图之间的关系。Auto Layout会根据视图的约束自动计算视图的位置和大小。比如,设置一个按钮的顶部距离一个图片视图(UIImageView)的底部距离为8px。那么一旦图片视图(UIImageView)的位置发生了改变,按钮的位置也会随之发生改变。这种基于约束的设计方法允许你根据内部或者外部约束来动态的构建用户界面。
Auto Layout考虑所有约束,然后通过数学计算来最终确定视图的位置和大小。这些计算都是由Auto Layout来帮我们做的,我们要做的仅仅是设置约束。当我们为一个控件设置的约束不足以确定其大小和位置时,Auto Layout会给我们相关的提示。
另外,当我们为一个控件设置其约束后,不论是横屏还是竖屏状态下,我们都能够确定该控件的位置和大小。
Masonry
虽然通过Storyboard可以非常直观的设置控件之间的约束关系,但是当界面比较复杂的情况下,通过Storyboard来设置约束会非常的难以维护,特别是涉及到团队开发时,Storyboard绝对不是优选方法。虽然苹果官方也为我们提供了通过代码编写自动布局的类–NSLayoutConstraints类,但是NSLayoutConstraints使用起来也非常的不便。鉴于此,在实际的开发中,我们经常使用第三方SDK–Masonry来替代NSLayoutConstraints。
Masonry用漂亮的语法对AutoLayout进行了包装,是一个轻量级的布局框架。Masonry提供了一种链式描述约束的方法,这使得布局的代码更简洁和易读。并且Masonry支持iOS和Mac OS X。
安装
Masonry推荐使用CocoaPods进行安装。在安装时,需要在工程的Podfile中添加下方的代码,然后更新下载即可。
pod 'Masonry'
在需要使用Masonry的文件中引入头文件:
#import "Masonry.h"
Masonry常用方法
Masonry是对自动布局Auto Layout的封装,因此在其中提供了与自动布局相对应的一些针对约束的操作方法,主要包括如下几个。
添加约束。mas_makeConstraints:方法可以用于为所有UIView类添加约束。
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block;
// 创建一个MASConstraint属性
@property (nonatomic, strong) MASConstraint *topConstraint;
...
// 创建约束
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
self.topConstraint = make.top.equalTo(superview.mas_top).with.offset(padding.top);
make.left.equalTo(superview.mas_left).with.offset(padding.left);
}];
...
// 不需要的时候可以卸载掉约束
[self.topConstraint uninstall];
更新约束。mas_updateConstraints:方法是用来更新约束的,如果约束存在则更新,如果不存在则添加新的约束。需要额外注意的是,当需要更新视图的约束时,苹果官方推荐在updateViewConstraints方法中进行更新。
- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block;
- (void)updateViewConstraints {
[self.growingButton mas_updateConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self);
make.width.equalTo(@(self.buttonSize.width)).priorityLow();
make.height.equalTo(@(self.buttonSize.height)).priorityLow();
make.width.lessThanOrEqualTo(self);
make.height.lessThanOrEqualTo(self);
}];
//在结束时记得调用父类的方法!
[super updateViewConstraints];
}
重新添加约束。mas_remakeConstraints:方法是用来重新添加约束的,在添加新约束前会先删除所有约束。
- (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block;
- (void)changeButtonPosition {
[self.button mas_remakeConstraints:^(MASConstraintMaker *make) {
make.size.equalTo(self.buttonSize);
if (topLeft) {
make.top.and.left.offset(10);
} else {
make.bottom.and.right.offset(-10);
}
}];
}
删除约束。uninstall用于清除之前添加的所有约束。
- (void)uninstall;
Masonry中定义的关系
Masonry中有三种关系。
.equalTo等价于NSLayoutRelationEqual。
.lessThanOrEqualTo等价于NSLayoutRelationLessThanOrEqual。
.greaterThanOrEqualTo等价于NSLayoutRelationGreaterThanOrEqual。
这三个方法都接受下面任何一个参数。
MASViewAttribute。MASViewAttribute与NSLayoutAttribute的对应关系如下。
MASViewAttribute | NSLayoutAttribute |
---|---|
view.mas_left | NSLayoutAttributeLeft |
view.mas_right | NSLayoutAttributeRight |
view.mas_top | NSLayoutAttributeTop |
view.mas_bottom | NSLayoutAttributeBottom |
view.mas_leading | NSLayoutAttributeLeading |
view.mas_trailing | NSLayoutAttributeTrailing |
view.mas_width | NSLayoutAttributeWidth |
view.mas_height | NSLayoutAttributeHeight |
view.mas_centerX | NSLayoutAttributeCenterX |
view.mas_centerY | NSLayoutAttributeCenterY |
view.mas_baseline | NSLayoutAttributeBaseline |
@property (nonatomic, strong, readonly) MASConstraint *left; /< 左侧 */
@property (nonatomic, strong, readonly) MASConstraint *top; /< 顶部 */
@property (nonatomic, strong, readonly) MASConstraint *right; /< 右侧 */
@property (nonatomic, strong, readonly) MASConstraint *bottom; /< 底部 */
@property (nonatomic, strong, readonly) MASConstraint *leading; /< 首部 */
@property (nonatomic, strong, readonly) MASConstraint *trailing; /< 尾部 */
@property (nonatomic, strong, readonly) MASConstraint *width; /< 宽度 */
@property (nonatomic, strong, readonly) MASConstraint *height; /< 高度 */
@property (nonatomic, strong, readonly) MASConstraint *centerX; /< 中心点X */
@property (nonatomic, strong, readonly) MASConstraint *centerY; /< 中心点Y */
@property (nonatomic, strong, readonly) MASConstraint *baseline; /**< 文本基线 */
分别可以设置leading、trailing、top、buttom、CenterX、CenterY、Baselines七种。
- UIView。视图的左边大于等于label的左边,下面两种写法是等价的。
make.left.greaterThanOrEqualTo(label);
make.left.greaterThanOrEqualTo(label.mas_left);
- NSNumber。也可以针对宽度和高度设置具体的约束大小。
make.width.greaterThanOrEqualTo(@200);
make.width.lessThanOrEqualTo(@400)
如果设置left、right、centerY等为一个具体值时意味着它相对于父视图left、right、centerY的位移。比如下面的代码是视图相对于父视图左边位移10个点。
//creates view.left = view.superview.left + 10
make.left.lessThanOrEqualTo(@10)
如果你不想用NSNumer作为参数你可以使用这个以mas_equal…开头的方法来进行设置。
make.top.mas_equalTo(42);
make.height.mas_equalTo(20);
make.size.mas_equalTo(CGSizeMake(50, 100));
make.edges.mas_equalTo(UIEdgeInsetsMake(10, 0, 10, 0));
make.left.mas_equalTo(view).mas_offset(UIEdgeInsetsMake(10, 0, 10, 0));
- NSArray。equalTo方法也接受一组上面类型的数组。
make.height.equalTo(@[view1.mas_height, view2.mas_height]);
make.height.equalTo(@[view1, view2]);
make.left.equalTo(@[view1, @100, view3.right]);
举例
手动倒入Masonry注意如果Masonry的文件夹里有plist文件,需要删除。
//
// ViewController.m
// Masonry
//
// Created by 谢鑫 on 2019/8/4.
// Copyright © 2019 Shae. All rights reserved.
//
#import "ViewController.h"
#import "Masonry.h"
@interface ViewController ()
@property (nonatomic, strong) UIView *yellowView;
@property (nonatomic, strong) UIView *greenView;
@property (nonatomic, strong) UIView *redView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.yellowView];
[self.view addSubview:self.greenView];
[self.view addSubview:self.redView];
[self updateViewConstraints];
}
- (UIView *)yellowView{
if (_yellowView==nil) {
_yellowView=[[UIView alloc]init];
_yellowView.backgroundColor=[UIColor yellowColor];
}
return _yellowView;
}
- (UIView *)greenView{
if (_greenView==nil) {
_greenView=[[UIView alloc]init];
_greenView.backgroundColor=[UIColor greenColor];
}
return _greenView;
}
- (UIView *)redView{
if (_redView==nil) {
_redView=[[UIView alloc]init];
_redView.backgroundColor=[UIColor redColor];
}
return _redView;
}
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
- (void)updateViewConstraints {
[self.yellowView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.and.leading.equalTo(@20);
make.width.equalTo(self.greenView);
make.trailing.equalTo(self.greenView.mas_leading).offset(-20);
}];
[self.greenView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(@20);
make.trailing.equalTo(@(-20));
}];
[self.redView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.bottom.and.trailing.equalTo(@(-20));
make.leading.equalTo(@(20));
make.top.equalTo(@[self.yellowView.mas_bottom,self.greenView.mas_bottom]).offset(20);
make.height.equalTo(@[self.yellowView.mas_height,self.greenView.mas_height]);
}];
[super updateViewConstraints];
}
@end
更详细的分析
//
// ViewController.m
// masonryDemo
//
// Created by 谢鑫 on 2020/4/1.
// Copyright © 2020 Shae. All rights reserved.
//
#import "ViewController.h"
#include <Masonry.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
//1.居中显示一个view
UIView *view1=[[UIView alloc]init];
view1.backgroundColor=[UIColor redColor];
//在使用autolayout之前,一定要先将视图添加到superview上,否则会报错
[self.view addSubview:view1];
//使用masonry函数来对添加的view进行约束
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
//设置居中
make.center.equalTo(self.view);
//设置view1的宽高
make.size.mas_equalTo(CGSizeMake(100, 100));
}];
//2.让一个view略小于其superView(边界15)
UIView *view2=[[UIView alloc]init];
view2.backgroundColor=[UIColor cyanColor];
[self.view addSubview:view2];
[view2 mas_makeConstraints:^(MASConstraintMaker *make) {
//make.top.equalTo(self.view).with.offset(15);//上,左是正
// make.left.equalTo(self.view).with.offset(15);
//make.right.equalTo(self.view).with.offset(-15);//右,下是负
//make.bottom.equalTo(self.view).with.offset(-15);
make.top.left.bottom.and.right.equalTo(self.view).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));//等价于
//make.edges.equalTo(self.view).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));//还等价于
}];
//3.让两个高度为150的view垂直居中并且等宽,等高排列 间隔20,宽度自动计算
UIView *view3=[[UIView alloc]init];
view3.backgroundColor=[UIColor yellowColor];
[self.view addSubview:view3];
UIView *view4=[[UIView alloc]init];
view4.backgroundColor=[UIColor blueColor];
[self.view addSubview:view4];
[view3 mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.mas_equalTo(self.view.mas_centerY);
make.left.equalTo(self.view.mas_left).with.offset(20);
make.right.equalTo(view4.mas_left).with.offset(-20);
make.height.equalTo(@150);//左右约定了,高得手写,或约定
make.width.equalTo(view4);
}];
[view4 mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.mas_equalTo(self.view.mas_centerY);
make.left.equalTo(view3.mas_right).with.offset(20);//这句和上面好像重复了
//inset和offset不要写错了,效果相反
make.right.equalTo(self.view.mas_right).with.offset(-20);
make.height.equalTo(view3);
make.width.equalTo(view3);//这句和上面好像重复了
}];
}
/**
*1.mas_makeConstraints只负责添加新增约束 Autolayout中不能同时存在两条针对于同一对象的约束,否则会报错
*2.mas_updateConstraints 针对上面的情况 会更新在block中出现的约束,确保不会出现两个相同的约束
*3.mas_remakeConstraints会清除之前的所有约束,仅保留最新的约束
*
*mas_equal除了支持NSNumber的数值类型外,就只支持CGPoint,CGSize,UIEdgeInsets
*
*/
@end
Masonry 等间隔或等宽高排列多个控件
一、先解释相关API
/**
* distribute with fixed spacing
*
* @param axisType 横排还是竖排
* @param fixedSpacing 两个控件间隔
* @param leadSpacing 第一个控件与边缘的间隔
* @param tailSpacing 最后一个控件与边缘的间隔
*/
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;
/**
* distribute with fixed item size
*
* @param axisType 横排还是竖排
* @param fixedItemLength 控件的宽或高
* @param leadSpacing 第一个控件与边缘的间隔
* @param tailSpacing 最后一个控件与边缘的间隔
*/
- (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;
两个API,分为固定间隔不固定宽高,固定宽高不固定间隔,根据具体需求使用相应的即可。
需要注意的是: 横排的时候要相应设置控件数组的垂直约束,竖排的时候要相应设置控件数组的水平约束。
二、具体实践测试
首先做准备工作,先生成四个View(需要被排列的),代码如下:
- (NSMutableArray *)masonryViewArray {
if (!_masonryViewArray) {
_masonryViewArray = [NSMutableArray array];
for (int i = 0; i < 4; i ++) {
UIView *view = [[UIView alloc] init];
view.backgroundColor = [UIColor redColor];
[self.view addSubview:view];
[_masonryViewArray addObject:view];
}
}
return _masonryViewArray;
}
1.向排列、固定控件间隔、控件长度不定
测试代码如下
- (void)test_masonry_horizontal_fixSpace {
// 实现masonry水平固定间隔方法
[self.masonryViewArray mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedSpacing:30 leadSpacing:10 tailSpacing:10];
// 设置array的垂直方向的约束
[self.masonryViewArray mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(150);
make.height.equalTo(80);
}];
}
测试结果如下:
2、水平方向排列、固定控件长度、控件间隔不定
代码如下:
- (void)test_masonry_horizontal_fixItemWidth {
// 实现masonry水平固定控件宽度方法
[self.masonryViewArray mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedItemLength:80 leadSpacing:10 tailSpacing:10];
// 设置array的垂直方向的约束
[self.masonryViewArray mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(150);
make.height.equalTo(80);
}];
}
测试结果如下:
3、垂直方向排列、固定控件间隔、控件高度不定
- (void)test_masonry_vertical_fixSpace {
// 实现masonry垂直固定控件高度方法
[self.masonryViewArray mas_distributeViewsAlongAxis:MASAxisTypeVertical withFixedSpacing:30 leadSpacing:10 tailSpacing:10];
// 设置array的水平方向的约束
[self.masonryViewArray mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(150);
make.width.equalTo(80);
}];
}
4、垂直方向排列、固定控件高度、控件间隔不定
- (void)test_masonry_vertical_fixItemWidth {
// 实现masonry垂直方向固定控件高度方法
[self.masonryViewArray mas_distributeViewsAlongAxis:MASAxisTypeVertical withFixedItemLength:80 leadSpacing:10 tailSpacing:10];
// 设置array的水平方向的约束
[self.masonryViewArray mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(150);
make.width.equalTo(80);
}];
}