Masonry的学习
媒介
在日常的开发中,我们如果面临一些很复杂的UI布局,我们如果统一使用frame的方式来设置的话,会相称复杂,这时候就孕育而生了自动布局,但是在我们原生的环境下,苹果原生的自动布局语言比力复杂,而我们现在要学习的一个Masonry这个第三方库会给我们提供了更加简便的语法。
使用Masonry
Masonry支持的属性
Masonry支持下类别的属性
- @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;
- @property (nonatomic, strong, readonly) MASConstraint *centerY;
- @property (nonatomic, strong, readonly) MASConstraint *baseline;
复制代码 属性可以大致分成以下几个部门的内容:
- 尺寸:width、height、size。
- 界限:left、leading、right、trailing、top、bottom、edges。
- 中心点:center、centerX、centerY。
- 偏移量:offset、insets、sizeOffset、centerOffset。
- priority()约束优先级(0~1000),multipler乘因数,dividedBy除因数。
这里给出一张图片便于明白这些相关属性的内容。
修饰语
Masonry为了让代码使用和阅读更轻易明白,Masonry还添加了and和with两个方法,这两个方法内部只是将self返回,没有任何实际作用,仅仅只是为了方便阅读。
- make.top.and.bottom.equalTo(self.containerView).with.offset(padding);
复制代码 这些修饰符的返回。
- - (MASConstraint *)with {
- return self;
- }
复制代码 底子API
下面列出了这里常用的一些底子的API
- mas_makeConstraints() //添加约束
- mas_remakeConstraints() //移除之前的约束,重新添加新的约束
- mas_updateConstraints() //更新约束
- equalTo() //参数是对象类型,一般是视图对象或者 mas_width 这样的坐标系对象
- mas_equalTo() //和上面功能相同,参数可以传递基础数据类型对象,可以理解为比上面的API更强大
- width() //用来表示宽度,例如代表view的宽度
- mas_width() //用来获取宽度的值。和上面的区别在于,一个代表某个坐标系对象,一个用来获取坐标系对象的值
复制代码 Auto Boxing
在上面可以看到我们上面有一些API的功能其实类似,只不外有前缀mas_之分,在实际开发中我们必要注意一下这两个API的差别,但是我们也可以通过两个宏界说来让我们不用区分这两种API的区别。
- // 定义这个常量,就可以不用在开发过程中使用mas_前缀。
- #define MAS_SHORTHAND
- // 定义这个常量,就可以让Masonry帮我们自动把基础数据类型的数据,自动装箱为对象类型。
- #define MAS_SHORTHAND_GLOBALS
复制代码 我们来看一下下面这段代码来表明一下这个Auto Boxing的一个简朴过程
- UIView* view = [[UIView alloc] init];// 创建一个view
- view.backgroundColor = UIColor.redColor;
- [self.view addSubview:view];// 添加到父亲视图
- UIEdgeInsets pddings = UIEdgeInsetsMake(20, 20, 20, 20);
- [view makeConstraints:^(MASConstraintMaker *make) {// 添加Masonry约束 注意一定要在我们添加到父亲视图之后进行一个添加
- // 设置宽度和高度为100
- // euqalTo(@100),这里不用mas前缀的话,参数要加@.
- // 如果我们想要直接使用数字的话,Masonry提供了mas_前缀。
- // 这个前缀会自动将基础数据类型转换为NSNumber类型。
- // 这个过程叫做封箱(Auto Boxing)。
- // "mas_xx"开头的宏定义,内部都是通过MASBoxValue()函数实现的。
- // 这样的宏定义主要有四个,分别是mas_equalTo()、mas_offset()和大于等于、小于等于四个。
- // 不过因为引用了开头两个宏定义。所以这里应该不加也没事。
- make.height.width.equalTo(100);
- }];
复制代码 这部门代码与注释泉源于iOS开发——Masonry的使用。
中心点
- [view makeConstraints:^(MASConstraintMaker *make) {// 添加Masonry约束 注意一定要在我们添加到父亲视图之后进行一个添加
- make.center.equalTo(self.view);//我们可以通过这种方式将这个视图添加到我们的视图中心点的位置。
- }];
复制代码 设置边距
- UIEdgeInsets pddings = UIEdgeInsetsMake(20, 20, 20, 20);
- [view makeConstraints:^(MASConstraintMaker *make) {// 添加Masonry约束 注意一定要在我们添加到父亲视图之后进行一个添加
- //这里需要注意如果设置过宽度和高度,那么就意味着下面可以生效的仅仅之后左侧边距和上方的边距可以生效。
- make.top.equalTo(self.view.mas_top).with.offset(pddings.top);
- make.left.equalTo(self.view.mas_left).with.offset(pddings.left);
- //注意如果是数值的方式来设置的话,我们记得将右下的数值改成负值。
- make.right.equalTo(self.view.mas_right).with.offset(-pddings.right);
- make.bottom.equalTo(self.view.mas_bottom).with.offset(-pddings.bottom);
-
- //上面代码等同于下面这一行的代码
- make.edges.equalTo(self.view).insets(pddings);
- }];
复制代码 优先级
- priority可以指定一个确切的优先级值,范围在0 ~ 1000,值越大优先级越高。
- priorityHigh相称于UILayoutPriorityDefaultHigh,优先级值为750。
- priorityMedium介于高优先级和低优先级之间,优先级值在250 ~ 750之间。
- priorityLow相称于UILayoutPriorityDefaultLow,优先级值为250。
优先级写在约束链的末了方
- make.top.equalTo(self.view).with.priority(800);
- make.bottom.equalTo(self.view).priorityMedium();
- make.left.equalTo(self.view).priorityLow();
- make.right.equalTo(self.view).priorityHigh();
复制代码 创建约束
自动布局允许将宽度和长度设置为一个固定值,例如:
- [view makeConstraints:^(MASConstraintMaker *make) {
- make.height.width.equalTo(100);
- }];
复制代码 但是,自动布局不允许将对齐属性(如left、right、centerY等)设置为常量值。如果为这些属性传递了一个NSNumber范例的值,Masonry会将这些变为相对于视图的父视图的约束,即:
- [self.greenView makeConstraints:^(MASConstraintMaker *make) {
- make.left.equalTo(@10);
- }];
复制代码 =>上面的代码会被转化成下方的代码
- [self.greenView makeConstraints:^(MASConstraintMaker *make) {
- make.left.equalTo(self.view.mas_left).offset(10);
- }];
复制代码 更新约束
- [控件 mas_remakeConstraints:^(MASConstraintMaker *make) {
- //这个方法会将以前的约束全部删除,添加新的约束
- }];
- [控件 mas_updateConstraints:^(MASConstraintMaker *make) {
- //这个方法将会覆盖以前的某些特定的约束
- }];
复制代码 我们这里采用第二个方式来进行一个更新,这里笔者给出一个比力简朴的案例
- - (void)viewDidLoad {
- [super viewDidLoad];
- self.view1 = [[UIView alloc] init];
- UIView* vc = [[UIView alloc] init];
- vc.backgroundColor = UIColor.greenColor;
- _view1.backgroundColor = UIColor.redColor;
- [self.view addSubview:_view1];
- UIEdgeInsets pddings = UIEdgeInsetsMake(50, 50, 50, 50);
- [_view1 makeConstraints:^(MASConstraintMaker *make) {
- //make.height.width.equalTo(100);
- //make.center.equalTo(self.view);
- make.edges.equalTo(self.view).insets(pddings);
- }];
- UITapGestureRecognizer* tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(press:)];
- tap.numberOfTapsRequired = 1;
- tap.numberOfTouchesRequired = 1;
- [_view1 addGestureRecognizer:tap];
-
-
- [_view1 addSubview:vc];
-
- [vc makeConstraints:^(MASConstraintMaker *make) {
- make.height.width.equalTo(40);
-
- }];
-
- // Do any additional setup after loading the view.
- }
- -(void)press:(UIGestureRecognizer*)tap {
- if (flag) {
- UIEdgeInsets pddings = UIEdgeInsetsMake(10, 10, 10, 10);
- [_view1 updateConstraints:^(MASConstraintMaker *make) {
- make.edges.equalTo(self.view).insets(pddings);
- }];
- flag = NO;
- } else {
- UIEdgeInsets pddings = UIEdgeInsetsMake(50, 50, 50, 50);
- [_view1 updateConstraints:^(MASConstraintMaker *make) {
- make.edges.equalTo(self.view).insets(pddings);
- }];
- flag = YES;
- }
-
- }
复制代码 下面是一个实现的效果。

这里简朴总结一下有关约束的问题:
- mas_makeConstraints只负责新增约束,每次调用会创建新的约束,如果对已存在的约束再次创建,会提示冲突(此时会存在对同一属性的两条约束)。
- mas_updateConstraints用于更新之前创建的约束,如果对已存在的约束进行更新,没有任何问题(此时只存在对同一属性的最新一条约束)。
- mas_remakeConstraints会扫除之前的全部约束,仅保留最新的约束。
使用Masonry来布局UIScrollview
对UIScrollView使用自动布局的时候,经常会遇到许多问题,比如布局无效,不能滑动等。紧张是由于UIScrollView除了有自己的frame之外,尚有一个contentSize。如果对子视图进行布局时参考了scrollView,例如设置子视图的leading/trailing/top/bottom属性即是scrollView的leading/trailing/top/bottom,这实际上是子视图相对于scrollview的contentsize来确定的,而非bounds。而且由于scrollview的contentSize又是根据子视图的位置决定的,如许就形成了一个依靠循环。iOS自动布局内容选自这里
以是我们该怎么使用Masonry来布局UIScrollview,这里我们最紧张的内容就是把我们内部的视图布局与UIScrollView的关系剥脱离,这里我们就可以使用一个过渡的视图,将过渡视图和原先的UIScrollView重叠,然后对于UIScrollVIew内部的视图全部都布局在这个过渡视图中间。
- 子视图不能依靠任何scrollView有关的布局,即不能参考scrollView的位置。
- 子视图除了要确定自己的巨细以外,还必要确定自己与contentSize附近的间隔,以此来确定contentSize。
这里我们开始通过仿写一下照片墙居中的一个小demo来实验使用MAsonry来布局一下UIscrollView,下面先给出代码。
我们先确定我们UIScrollView的布局
- [scrollView makeConstraints:^(MASConstraintMaker *make) {
- make.edges.equalTo(UIEdgeInsetsMake(0, 0, 0, 0));//布局UIScrollView
- }];
复制代码 然后设置一个过渡视图,来确定UIScrollView的ContentSize的巨细
- [_contentView makeConstraints:^(MASConstraintMaker *make) {
- make.edges.equalTo(scrollView);//确定ContentSize的大小
-
- make.width.equalTo(scrollView);//竖向滑动固定宽度,横向滑动固定高度
- }];
复制代码 然后我们就可以把全部的原本必要添加到UIScrollView的视图全部添加到现在的过渡视图中
- viewInsert.backgroundColor = UIColor.redColor;
- viewInsert.tag = 100 + i;
- //UIEdgeInsets pddings = UIEdgeInsetsMake(50, 50, 50, 50);
- [viewInsert makeConstraints:^(MASConstraintMaker *make) {
- make.top.equalTo(i / 3 * 140 + 70);
- make.left.equalTo(i % 3 * 120 + 20);
- make.width.equalTo(100);
- make.height.equalTo(100);
- }];
复制代码 末了我们还必要通过末了一个视图来确定我们过渡视图的底部的位置
- [_contentView makeConstraints:^(MASConstraintMaker *make) {
- make.bottom.equalTo(previousView);
- }];
复制代码 末了这是完整的代码
- - (void)viewDidLoad { [super viewDidLoad]; //self.view1 = [[UIView alloc] init]; UIScrollView* scrollView = [[UIScrollView alloc] init]; scrollView.backgroundColor = UIColor.orangeColor; [self.view addSubview: scrollView]; [scrollView makeConstraints:^(MASConstraintMaker *make) {
- make.edges.equalTo(UIEdgeInsetsMake(0, 0, 0, 0));//布局UIScrollView
- }];
- self.contentView = [[UIView alloc] init]; _contentView.backgroundColor = UIColor.clearColor; [scrollView addSubview:_contentView]; [_contentView makeConstraints:^(MASConstraintMaker *make) {
- make.edges.equalTo(scrollView);//确定ContentSize的大小
-
- make.width.equalTo(scrollView);//竖向滑动固定宽度,横向滑动固定高度
- }];
- UIButton* previousView = [UIButton buttonWithType:UIButtonTypeCustom]; for (int i = 0; i < 9; i++) { UIButton* viewInsert = [UIButton buttonWithType:UIButtonTypeCustom]; NSString* str = [NSString stringWithFormat:@"%d.jpg", i + 1]; UIImage* image = [UIImage imageNamed:str]; [viewInsert setImage:image forState:UIControlStateNormal]; [viewInsert setImage:image forState:UIControlStateSelected]; if (flag == YES) { NSLog(@"11"); [viewInsert addTarget:self action:@selector(press:) forControlEvents:UIControlEventTouchUpInside]; } //UIView* viewInsert = [[UIView alloc] init]; [self.contentView addSubview:viewInsert]; viewInsert.backgroundColor = UIColor.redColor;
- viewInsert.tag = 100 + i;
- //UIEdgeInsets pddings = UIEdgeInsetsMake(50, 50, 50, 50);
- [viewInsert makeConstraints:^(MASConstraintMaker *make) {
- make.top.equalTo(i / 3 * 140 + 70);
- make.left.equalTo(i % 3 * 120 + 20);
- make.width.equalTo(100);
- make.height.equalTo(100);
- }];
- previousView = viewInsert; } [_contentView makeConstraints:^(MASConstraintMaker *make) {
- make.bottom.equalTo(previousView);
- }];
- -(void)press:(UIButton*)btn { if (flag && !btn.selected) { btn.selected = YES; [self.contentView bringSubviewToFront:btn]; [btn remakeConstraints:^(MASConstraintMaker *make) { make.center.equalTo(self.view); make.height.and.width.equalTo(300); }]; flag = NO; } else if (!flag && btn.selected) { NSLog(@"22"); btn.selected = NO; NSInteger i = (btn.tag - 100) / 3; NSLog(@"%ld", i); NSInteger j = (btn.tag - 100) % 3; [btn remakeConstraints:^(MASConstraintMaker *make) { make.height.and.width.equalTo(100); make.top.equalTo(i * 140 + 70); make.left.equalTo(j * 120 + 20); }]; flag = YES; } else { ; } }@end
复制代码 末了实现效果:

小结
- 对视图使用自动布局之前,必要先将视图添加到父视图上。
- 使用Masonry时注意区分
mas_equalTo和equalTo的区别:
mas_equalTo
- 是对equalTo的封装,支持NSNumber、CGSize、CGPoint、UIEdgeInsets;
- mas_equalTo是一个Macro,比力的是值;
- 一样寻常对于数值元素使用mas_equalTo,例如make.width.mas_equalTo(100);。
- equalTo
- equalTo比力的是view;
- 对于对象或多个属性的处理使用equalTo,例如make.left.and.right.equalTo(self.view);。
- 对视图使用自动布局后,如果实验获取它的frame很大概获取到的是0。对于这种情况,我们可以在获取视图frame之前手动调用layoutIfNeeded方法来更新布局。
- Masonry中的block不存在循环引用的问题,不必使用weakSelf。固然block内引用了view,但block并没有被view所持有,因此不会发生循环引用。
笔者认为这段总结很好,摘自iOS开发—Masonry
这是笔者第一次接触MAsonry这个第三方库以及相关的一些使用,之后会继承学习相关内容。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |