读Masonry源码

读读让写自动布局时可以帮忙解放双手的 Masonry源码

如果不使用第三方库,使用自动布局的话,苹果提供两种方式:

  1. NSLayoutConstraint,缺点是常见约束的代码冗长,描述性不强;
  2. VFL(Visual Format Language),苹果为了简化Autolayout的编码而推出的可视化格式语言;缺点为基于字符串,不支持存在倍数关系的约束,是NSLayoutConstraint子功能集;NSLayoutConstraint constraintsWithVisualFormat:方法返回数组类型,难以进行单个约束的动画处理;

针对这些缺点,Masonry有哪些改进呢,官网文档上有特征介绍:

  1. NSLayoutConstraint 能做到的,Masonry也能做;
  2. 出色的调试支持,为视图和约束提供了有意义的名称;
  3. 没有疯狂的宏魔法,不会用宏污染全局命名空间;
  4. 支持编译检查,因为没有基于字符串或字典;

原生写法:

UIView *superview = self.view;

UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
[superview addSubview:view1];

UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);

[superview addConstraints:@[
    //view1 constraints
    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeTop
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeTop
                                multiplier:1.0
                                  constant:padding.top],
 ]];

Masonry写法:

UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(superview.mas_top).with.offset(padding.top); 
}];

的确是精简了很多,那Masonry是怎么做到的呢? 不可能封装的VFL,所以只会是对NSLayoutConstraint的封装;

对比直接使用NSLayoutConstraint,猜测mas_makeConstraints: 完成了如下步骤的封装:

  1. 猜测1 调用的view 设置属性 .translatesAutoresizingMaskIntoConstraints = NO;
  2. 猜测2 封装了 NSLayoutConstraint 创建过程,可能是 block中的 make.top.equalTo…
  3. 猜测3 mas_makeConstraints: 执行 相当于 addConstraints: 过程;

但是 查看方法声明

- (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;

为啥有数组型返回值?可能是用来做动画的吧,没用过…聊胜于无吧,看SnapKit同样的方法返回的void;

MAS_VIEW (MASAdditions)

- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
    self.translatesAutoresizingMaskIntoConstraints = NO;
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    block(constraintMaker);
    return [constraintMaker install];
}

以UIView的分类形式,猜测1没问题; MASConstraintMaker调用了 initWithView、install两个方法;

initWithView方法,只是弱引用了当前view,初始化一个用于保存约束的数组constraints而已;

install会调用MASConstraintMaker->MASViewConstraint中的install方法,最终调用[self.installedView addConstraint:layoutConstraint];

疑问?上一步中layoutConstraint需要的参数都是怎么来的 ?

block(constraintMaker);传入了maker参数,里做了什么?

make.top.equalTo(superview.mas_top).with.offset(padding.top);

这句代码到底做了什么?将block中写的约束依次创建为MASViewConstraint然后加入maker的属性数组constraints中,单句代码返回值为刚才创建的MASViewConstraint(官方给的例子中有把当前返回值保存到数组中,然后做动画的例子,这样就避免了VFL一次性数组返回而过程不透明的弊端);

DSL (domain specific language)是什么 ?

领域特定语言,指专注与某个应用程序领域的计算机语言;

某位大佬的结论是:

绝大部分DSL的存在是设计者没有理解问题的本质;绝大部分的问题可以通过“库代码”来解决,而不是DSL;

补全了哪些知识点

  1. 有个ViewController+MASAdditions分类,添加了self.view的部分快捷属性,但NS_DEPRECATED_IOS(8.0, 11.0);
  2. 有个NSArray+MASAdditions数组的分类,mas_distributeViewsAlongAxis:,看一下官方的例子,挺好用的,线性布局方式看起来很像UIStackView的Api,或者Flex布局(Axis,轴)阮一峰的 Flex 布局教程:语法篇

引用源

Written on September 8, 2019