前言
开发中对UI进行布局,有很多种,常用的包括frame,Autolayout,storyboard,Masonry等。代码布局添加约束依赖使用masonry框架是一个很不错的选择。此篇文章就是对masonry框架内部实现进行一个探究,从而学习他的编程思想。
Masonry的核心思想
Masonry框架其实是对NSLayoutConstraint的一个封装,使用了函数式编程和链式编程的思想,使描述语法更加简洁明了,并具备了很高的可读性。
Masonry的核心使用方法
一个简单的masonry的使用
_redView = [[UIView alloc]init];
_redView.backgroundColor = [UIColor redColor];
[self.view addSubview:_redView];
[_redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.top.equalTo(self.view).offset(10);
make.right.bottom.equalTo(self.view).offset(-10);
}];
使用masonry进行布局的时候,都会调用这个方法。
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
block(constraintMaker);
return [constraintMaker install];
}
这个方法的返回值为一个数组,参数是一个block,void(^)(MASConstraintMaker *)。它的作用是给控件设置布局。
- 创建一个约束制造者MASConstraintMaker
- 调用block(maker),调用外部block中描述控件约束代码,将约束全部保存到约束制造者。
- constraintMaker调用install,返回值为一个数组。[constraint install]内部实现的就是遍历约束制造者中的约束,然后给控件添加约束。
点击block代码块中make.left,进入到内部,一层层查找,会跳转到下面的方法。
- (MASConstraint *)left {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
//self.view->redView
//1.相对于哪个view,确定是给哪个view添加约束。
MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
//控件约束类,把当前的这个redview传入到这个类中。
MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
//判断传过来的是哪个类型,从left点击进来可以查看此方法调用的时候参数(MASConstraint *)constraint传入的为nil,因此这里不会进入。
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;
}
// 传入的为nil,会进入下面的判断。签署代理,将新增的约束保存到数组当中。
if (!constraint) {
newConstraint.delegate = self;
[self.constraints addObject:newConstraint];
}
//返回值为约束类MASViewConstraint
return newConstraint;
}
_redView = [[UIView alloc]init];
_redView.backgroundColor = [UIColor redColor];
[self.view addSubview:_redView];
[_redView mas_makeConstraints:^(MASConstraintMaker *make) {
//make.left -> 返回为MASViewConstraint类,此时点击进入top,调用top的是MASViewConstraint类,一层一层点击进去查看
make.left.top.equalTo(self.view).offset(10);
make.right.bottom.equalTo(self.view).offset(-10);
}];
//此时top会进入到这个方法 签署的代理对象
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation");
return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
}
//又进入了这个方法
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
//self.view->redView
//1.相对于哪个view,确定是给哪个view添加约束。
MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
//控件约束类,把当前的这个redview传入到这个类中。
MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
//判断传过来的是哪个类型,从top进入的时候,传入的(MASConstraint *)constraint为self,当时的self就是left返回的类型,就是MASViewConstraint类型,因此会进入到下面的代码,将top的约束保存到maker的数组中,
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];
//返回值为MASCompositeConstraint类型
return compositeConstraint;
}
// 传入的不为nil,不会进入下面的判断。
if (!constraint) {
newConstraint.delegate = self;
[self.constraints addObject:newConstraint];
}
//返回值为约束类MASViewConstraint
return newConstraint;
}
//再次进行约束的时候 此时返回的self为MASCompositeConstraint类,点击进入方法。
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
//此时的self为MASCompositeConstraint类 再次点击进入
[self constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
return self;
}
- (MASConstraint *)constraint:(MASConstraint __unused *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
id<MASConstraintDelegate> strongDelegate = self.delegate;
//strongDelegate调用这个方法,strongDelegate = self.delegate,此时的strongDelegate为最初签署的代理MASConstraintMaker类
MASConstraint *newConstraint = [strongDelegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
newConstraint.delegate = self;
[self.childConstraints addObject:newConstraint];
//返回值为MASConstraint类
return newConstraint;
}
层层设置则同理,第一次调用left时,当时的self为MASConstraintMaker类型,会[self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute]这样的调用方式,和MASConstraintMaker类签署代理,并且将约束保存到数组当中,返回类型为MASViewConstraint,再次调用left,right,top等等操作时,这时候的self为MASViewConstraint类型,进入到其他的方法,会[self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];这样的调用方式,此时constraint传入了之前返回的MASViewConstraint类型的对象,内部会走不同的判断方法,将新的约束加入到之前保存约束的数组当中,同时返回类型为MASCompositeConstraint的返回值,再次添加约束,此时self为MASCompositeConstraint类,会走[self constraint:self addConstraintWithLayoutAttribute:layoutAttribute];,在跳转到- (MASConstraint *)constraint:(MASConstraint __unused *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute,内部调用MASConstraint *newConstraint = [strongDelegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];方法,这时候的strongDelegate是MASConstraintMaker类,则会再次进入添加约束的方法中,返回值的类型仍然为MASCompositeConstraint类。则实现了依次添加约束的效果。
_redView = [[UIView alloc]init];
_redView.backgroundColor = [UIColor redColor];
[self.view addSubview:_redView];
[_redView mas_makeConstraints:^(MASConstraintMaker *make) {
//equalTo方法返回值为一个block equalTo()就是调用了block
//这里体现了链式编程的思想 返回值为block
make.left.top.equalTo(self.view).offset(10);
make.right.bottom.equalTo(self.view).offset(-10);
}];
- (MASConstraint * (^)(id))equalTo {
return ^id(id attribute) {
//这里的返回值又是一个block
//一层层点进去最终会进入下面的代码
return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
};
}
//这里的返回值仍然是一个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;
}
};
}
通过下面这行代码就将要添加的约束,以及约束的数值都添加了进去。
make.left.top.equalTo(self.view).offset(10);
此时
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
//此时block代码块就执行完毕了
block(constraintMaker);
//然后执行这一步代码,把约束添加到视图上。
return [constraintMaker install];
}
外部的调用,参数是一个block,利用了函数式编程的思想,block代码块中的点语法连续的书写,利用了返回值为block,block的返回值又是对象,对象继续使用点语法调用方法的思想,就是链式编程的核心思想。实现链式编程的关键就是声明一个block的属性,而这个block返回值必须还是一个对象(根据业务需求不同,可以返回的是这个对象实例本身,也可以是这个类的另一个实例,更可以是另一个类的实例对象。)
网友评论