AutoLayout 之 读一读Masonry的源码
第一次写博客 怀着一颗认真的心 开始
Masonry 是如何实现链式调用的
先看一段代码
UIView *bgView = [[UIView alloc]initWithFrame:CGRectMake(100, 100, 200, 100)];
bgView.backgroundColor = [UIColor redColor];
[self.view addSubview:bgView];
**subView是bgView的子视图**
UIView *subView = [[UIView alloc]init];
subView.backgroundColor = [UIColor greenColor];
[bgView addSubview:subView];
[subView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(bgView).with.offset(10);
make.left.equalTo(bgView).with.offset(20);
make.bottom.equalTo(bgView).with.offset(-10);
make.right.equalTo(bgView).with.offset(-50);
make.height.lessThanOrEqualTo(bgView);
}];
OC的方法 都是以 [] 这种形式掉用的 ,可是从上边可以看到 make.top.equalTo() 是链式掉用 那么他是怎么实现的呢?其实就是block 接下来通过看源码 来读读他是怎么实现的
约束配置类 MASConstraintMaker![MASConstraintMaker 所拥有属性的一部分](https://i-blog.csdnimg.cn/blog_migrate/f5162632d84a26826454d9bf12856b67.png)
我们以top 为例 来看他的get 方法
- (MASConstraint *)top {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
}
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}
通过他的层层掉用 发现他最终在 下面这个方法
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
if ([constraint isKindOfClass:MASViewConstraint.class]) {
//replace with composite constraint
NSArray *children = @[constraint, newConstraint];
MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
compositeConstraint.delegate = self;
[self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
return compositeConstraint;
}
if (!constraint) {
newConstraint.delegate = self;
[self.constraints addObject:newConstraint];
}
return newConstraint;
}
**我们来分析constraint 方法 **
从 self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute 代码来看 掉用.top的时候 其实直接走到了 if (!constraint) {} 这个代码分支 此时此刻 self.constraints 里有值了 并且是 MASViewConstraint 的对象 并且此方法返回了一个MASViewConstraint对象 下面分析 MASViewConstraint
约束的生成和添加类 MASViewConstraint
MASViewConstraint 继承于MASConstraint 这个很关键
我们来看他的初始化方法- (id)initWithFirstViewAttribute:
入参是一个MASViewAttribute 对象其实就是保存了一个约束的一个属性 这里就是 top 其实就是 NSLayoutAttributeTop
再来看一看 make.top.equalTo
equalTo 是 MASConstraint的一个block 这就是链式掉用的实现方式 我们来看这个equalTo方法的实现
- (MASConstraint * (^)(id))equalTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
};
}
注意 注意 注意 ! 最终 掉用到了 equalToWithRelation block 关键方法来了
- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation {
return ^id(id attribute, NSLayoutRelation relation) {
if ([attribute isKindOfClass:NSArray.class]) {
NSAssert(!self.hasLayoutRelation, @"Redefinition of constraint relation");
NSMutableArray *children = NSMutableArray.new;
for (id attr in attribute) {
MASViewConstraint *viewConstraint = [self copy];
viewConstraint.layoutRelation = relation;
viewConstraint.secondViewAttribute = attr;
[children addObject:viewConstraint];
}
MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
compositeConstraint.delegate = self.delegate;
[self.delegate constraint:self shouldBeReplacedWithConstraint:compositeConstraint];
return compositeConstraint;
} else {
NSAssert(!self.hasLayoutRelation || self.layoutRelation == relation && [attribute isKindOfClass:NSValue.class], @"Redefinition of constraint relation");
self.layoutRelation = relation;
self.secondViewAttribute = attribute;
return self;
}
};
}
我们在前边传入 equalToWithRelation 方法的 参数是 bgView 和 NSLayoutRelationEqual 我们这里传入的是 单个 所以直接走 else分支 relation 和 attribute 都赋值给了 self.layoutRelation self.secondViewAttribute 此时的self 是谁呀 是 MASViewConstraint 对象吧 是 前面声明的 newConstraint 吧 此时此刻我们的约束都装进了 newConstraint 中 统一保管
接下来一个关键 方法 MASConstraintMaker 的 install 方法
所有约束进行装配 install
哈哈 其实 上边说的 MASConstraintMaker 的 install 方法 只是个幌子 来看看他的实现
- (NSArray *)install {
if (self.removeExisting) {
NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];
for (MASConstraint *constraint in installedConstraints) {
[constraint uninstall];
}
}
NSArray *constraints = self.constraints.copy;
for (MASConstraint *constraint in constraints) {
constraint.updateExisting = self.updateExisting;
[constraint install];
}
[self.constraints removeAllObjects];
return constraints;
}
因为我们现在是添加操作 其实 if (self.removeExisting) { } 这个分支是不会走的 直接往下看 真正的装配方法 是走的 [constraint install];
其实 最终走的是 MASViewConstraint 的 install 方法 下面来看 install 方法的其中两行
回到原生AutoLayout 生成一个约束
MASLayoutConstraint *layoutConstraint
= [MASLayoutConstraint constraintWithItem:firstLayoutItem
attribute:firstLayoutAttribute
relatedBy:self.layoutRelation
toItem:secondLayoutItem
attribute:secondLayoutAttribute
multiplier:self.layoutMultiplier
constant:self.layoutConstant];
**将约束加到view上 在这个例子中是 subView **
[self.installedView addConstraint:layoutConstraint];
至此我们的一个top 约束添加完毕
可以参考https://github.com/SnapKit/Masonry 看看原生是怎么掉用的 结合Masonry源码看
后记
感谢有这些大牛的存在 正是有了他们的存在才使我们的开发更简单
怀着一颗感恩的心读他们的代码
这是我第一篇在csdn 上写博客 关于对Masonry 的分析 这只是一部分 读者可以发现 这只是一种情况 文中提到的方法 有一些if 分支是没有分析的 以后再补上。