iOS视图APP & program

iOS学习(三十七) 一种masonry布局代码的最佳实践

2022-03-14  本文已影响0人  圆脸黑猫警长
Best Practice.jpg

使用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));
        }];
    }
  1. 子组件写法同理,一个子组件可以命名随意一点不必十分有意义,比如leftLabel、rightLabel、label1、label2等。
    因为页面结构复杂以后,层级会很多,不少组件其实本身只是用于定位、占位或者分隔等,这样可以极大的简化命名也不影响对代码的理解(用于需要保留到全局的变量仍然需要使用有意义的命名)

  2. 对动态赋值的元素放到最下面进行 赋值 或 逻辑处理。不变的内容放在布局代码中赋值。

四、可能的疑问
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的所带来的效率提升是显而易见的。

上一篇下一篇

猜你喜欢

热点阅读