使用masonry进行布局构建的时候,代码逻辑往往比较长。在布局页面时往往会有嵌套的情况发生,组件的创建代码就和布局代码交织在一起,一段代码看起来更是复杂,让人难以理解。
本文仅仅通过一些UI写法上的调整让整体UI布局易读、易改。如果你有同样的问题,耐心花5分钟读完,稍加实践即可事半功倍。
为了应对这样的情况,通常的做法是将UI构建代码写在上面,布局代码全部写在最后(布局的组件必须先要被添加到父组件上),这样的好处是构建和布局分开了,看起来似乎整齐了一些,但是调试几乎是灾难性的,实践一段时间后发现一些难以解决的问题。
一、遇到的问题:
1.元素分散不内聚
原本页面构建时,会根据设计结构先把一个大的页面分割成几部分,然后分别进行布局。在实践中,由于纯代码代码量多,所以大多数情况下往往能少写组件就少写,这样原本该通过添加适当的父View去分离的UI结构就不会去分离,导致各个组件之间关系过紧密,不利于后期的修改,相似UI复用不易。
2.命名困难
由于布局在一起,则每个组件都应该有自己有意义的名称,往往导致命名困难。但大多数都只是在布局中使用一次,动态变化的少。这样也不利于读代码。
二、一种最佳实践
经过一段时间的实践和总结,找到一种比较清晰的写法:
1.利用{}
构成代码块对每一个组件进行分离
2.在每个代码块中将布局代码写在最后
3.每个子view都尽可能的之依托于自己的父组件进行布局
举例说明:
中间有个view,view中有两个label, 一个居上,一局下,效果如图
例子.png
代码如下:
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *bgView = [[UIView alloc] init];
{
[self.view addSubview:bgView];
bgView.backgroundColor = [UIColor blackColor];
UILabel *label1 = [[UILabel alloc] init];
{
[bgView addSubview:label1];
self.nameLabel = label1;
label1.font = [UIFont systemFontOfSize:18];
label1.textAlignment = NSTextAlignmentCenter;
label1.textColor = [UIColor systemYellowColor];
[label1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.right.mas_equalTo(0);
}];
}
UILabel *label2 = [[UILabel alloc] init];
{
[bgView addSubview:label2];
self.infoLabel = label2;
label2.textColor = [UIColor whiteColor];
label1.textAlignment = NSTextAlignmentCenter;
[label2 mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.left.right.mas_equalTo(0);
}];
}
[bgView mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.mas_equalTo(0);
make.size.mas_equalTo(CGSizeMake(200, 200));
}];
}
self.nameLabel.text = @"Jack";
self.infoLabel.text = @"Jack is a good student";
}
三、写法说明:
1.每一个组件本身都使用{}
进行封闭,在封闭区域的最上面写添加到父组件代码,然后是该组件本身的属性设置,赋值全局变量等。在最下面写布局代码。如果还有子组件,就写在两者之间(截选上方代码部分):
UIView *bgView = [[UIView alloc] init];
{
[self.view addSubview:bgView];
bgView.backgroundColor = [UIColor blackColor];
// 这里添加子组件
[bgView mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.mas_equalTo(0);
make.size.mas_equalTo(CGSizeMake(200, 200));
}];
}
-
子组件写法同理,一个子组件可以命名随意一点不必十分有意义,比如leftLabel、rightLabel、label1、label2等。
因为页面结构复杂以后,层级会很多,不少组件其实本身只是用于定位、占位或者分隔等,这样可以极大的简化命名也不影响对代码的理解(用于需要保留到全局的变量仍然需要使用有意义的命名) -
对动态赋值的元素放到最下面进行 赋值 或 逻辑处理。不变的内容放在布局代码中赋值。
四、可能的疑问
1.为什么不将组件的声明也放到代码块中?
组件之间往往有约束关系,全部放到代码块中再要使用需要将其放到全局变量中进行引用,这样别的代码块中才能访问到,违背了初衷。
2.为什么不把UI的创建拆分成一个个的方法调用,在方法内部去写布局?
通过这样的方式构建UI,表面看起来可以简化一部分代码,但是缺点也是显而易见的:
方法要接收父组件作为参数(需要添加到父组件上才能设置约束,否则会报错),同时,如果需要参考其他元素的位置,那么方法的参数也需要传入这个元素。很明显,这样的方式只是转移了杂乱的代码,并没有简化,后期页面修改时带来的问题也是灾难性的。
当然,我们可以把组件的创建和一些设置
写到一个方法去代替写在{}
前的创建和{}
中的属性设置,比如:
UIView *bgView = [self createBgView];
{
[self.view addSubview:bgView];
// 这里添加子组件
[bgView mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.mas_equalTo(0);
make.size.mas_equalTo(CGSizeMake(200, 200));
}];
}
在createBgView
这个方法中进行创建,设置属性等操作。这样的写法其实对于通用的View创建才有用,如果定制的比较多,那么这样的做法也只是增加了麻烦(因为每一个定制的都需要这样一个方法),反而得不偿失。
3.其他的优化方法
如果可能的话,其实还是更推荐使用storyboard / xib
的布局方式,固定的直接布局,动态显示内容用一个父控件去占位,只把动态的部分去代码构建。毕竟GUI的所带来的效率提升是显而易见的。
网友评论