「OC」自动布局Masonry的运用
前言
笔者在前面写过关于OC自带的系统自动布局,自动布局的好处就是控件的位置都是相对的,这样即使在不同屏幕尺寸的手机之中的布局都是大致相同的,比frame布局来的更加智能。方便是方便但是我在使用的过程之中发现,使用系统自带的自动布局代码量多且过程繁琐,我们还需要了解在OC之中各个边的名字,这样子好像也不够方便。最近才知道有第三方库Masonry这个工具(难怪当时学习自动布局的时候,网上相关的资料很少)
自动布局
在这里顺带提一嘴frame布局和自动布局的差别
Frame布局方式在简单情况下,较为简单,而且性能极高,但是不利于扩展,当手机的尺寸不同或者横竖屏的时候布局都会有一定的问题,如果想要解决一般来说就是使用几套frame布局。反观自动布局布局,使用起来较为方便,可以轻松应对各种布局方式,但是会消耗一定的性能,使用不当容易造成性能问题。
首先我们来看使用官方自带的自动布局是如何实现的
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIView *view = [[UIView alloc] init];
view.backgroundColor = [UIColor orangeColor];
view.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:view];
UILabel *label = [[UILabel alloc] init];
label.backgroundColor = [UIColor redColor];
label.text = @"AutoLayout";
label.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:label];
[NSLayoutConstraint activateConstraints:@[
[view.topAnchor constraintEqualToAnchor: self.view.safeAreaLayoutGuide.topAnchor constant:200],
[view.centerYAnchor constraintEqualToAnchor: self.view.centerYAnchor ],
[view.centerXAnchor constraintEqualToAnchor: self.view.centerXAnchor ],
[view.heightAnchor constraintEqualToConstant:300],
[view.widthAnchor constraintEqualToConstant:180],
[label.topAnchor constraintEqualToAnchor:view.topAnchor constant:30],
[label.centerXAnchor constraintEqualToAnchor: view.centerXAnchor ],
[label.heightAnchor constraintEqualToConstant:40],
[label.widthAnchor constraintEqualToConstant:90]
]];
}
@end
代码实现的形状如下
NSLayoutConstraint
之中就是我们自动布局所需要的代码了,我们还要对控件的translatesAutoresizingMaskIntoConstraints
该写为NO,可以看到NSLayoutConstraint
之中的代码量还是很多的,所以这也是我们学习Masonry
的原因。
Masonry的布局使用
Masonry 是一个流行的 iOS 和 macOS 开发库,用于简化系统自带的 Auto Layout 的使用。它提供了链式语法和便捷的方法,使得创建和管理视图之间的约束变得更加简单和直观。
和系统自带的自动布局比较相似的一个点,那就是Masonry
也是通过约束来对控件进行相关的布局,我们使用Masonry
进行布局需要以下几个方法
- (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
- (NSArray *)mas_updateConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
- (NSArray *)mas_remakeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
Masonry的使用方式
我们可以将我们前面给出的官方自动布局的代码稍作转化
#import "ViewController.h"
#import "Masonry.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIView *view = [[UIView alloc] init];
view.backgroundColor = [UIColor orangeColor];
view.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:view];
UILabel *label = [[UILabel alloc] init];
label.backgroundColor = [UIColor redColor];
label.text = @"AutoLayout";
label.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:label];
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view).offset(200);
make.centerY.equalTo(self.view);
make.centerX.equalTo(self.view);
make.height.equalTo(@300);
make.width.equalTo(@180);
}];
[label mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(view).offset(30);
make.centerX.equalTo(view);
make.height.equalTo(@40);
make.width.equalTo(@90);
}];
}
@end
注:self.view还可以再被简化,直接用self代替,你就只管偷懒剩下就交给编译器!!!
我们可以看到,我们在创建约束的时候,都没有将对应边写出来,但是系统却帮我们给自动处理了,在这个代码·make.top.equalTo(self.view).offset(200);
我们并没有指定这个view
去对齐self.view
的top,但是系统就进行自动对应了,十分的方便,但如果我们不要进行对应边进行对应,我们也可以使用指定边进行布局操作。
例如:我们将label对top去对应view的bottom,那么我们作出以下修改,可以得到
[label mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(view.mas_bottom).offset(30);
make.centerX.equalTo(view);
make.height.equalTo(@40);
make.width.equalTo(@90);
}];
另外,我们在使用数字直接限制控件的高度和宽度的时候,一共有两种写法
make.height.mas_equalTo(40);
make.height.equalTo(@40);
这两种方式似乎都各有繁琐的地方,那我看到网上有一个小技巧,利用宏定义,可以让代码变得更加的简洁
#define MAS_SHORTHAND_GLOBALS
[label mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(40);
}];
说完了关于常数以及边界的内容,如果我们想要改变约束方程之中的系数,就需要用上multipliedBy 和 dividedBy这两个方法了,看到方法我们不难知道,一个是乘法一个除法,如果我们想要控件的宽度为控制器宽度的一半的话,可以用以下方法
[label mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(self).multipliedBy(0.5);
}];
[label mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(self).dividedBy(2);
}];
有了系数,有了常数接下来我们就可以使用我们熟悉的约束的写法了
我们设置label的宽度为控制器尺寸的0.5倍在多20个像素点,代码如下
[label mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(self).offset(20).multipliedBy(0.5);
}];
offset和multipliedBy的位置可以互换,不影响实现
一般来说我们的约束都是对控件的四条边进行操作的,那这样就要写四行代码,还是有一点麻烦的,那我们可以使用以下方法,再进行简化
[label mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self).with.insets(UIEdgeInsetsMake(10, 10, 10, 10));
}];
以上代码我们就是将label限制在了,上左下右边距各为10,我们会发现这个set的设置与之前之前的接触的不太一样,按之前的习惯如果要设置内边距的话,其实应该是设置为(10,10,-10,-10),但是这个编译器帮我们进行了自动处理,所以就变成以上代码的内容,需要注意
Masonry的优先级
Masonry为我们提供了三个优先级的方法,priorityLow()、priorityMedium()、priorityHigh(),这三个方法内部对应着不同的默认优先级,当然我们也可以使用priority() 括号之中添加数字设置具体的数值。
优先级的方法 | 相等于的数值大小 |
---|---|
priorityLow() | 250 |
priorityMedium() | 500 |
priorityHigh() | 750 |
[label mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(view).priorityLow();
make.width.equalTo(@[view,@100]).priorityHigh();
make.width.equalTo(@300).priority(888);
}];
优先级的用处是在于,如果我们在约束发生冲突的时候,而且我们也不想将约束删除的话,会优先实现优先级更高的。系统通过比较两个”相互冲突的约束”的优先级,从而忽略低优先级的某个约束,达到正确布局的目的约束优先级默认都是1000。