Masonry源码分析---名字就是这么没特色

Masonry 结构图,本文会按照程序的执行来说解。 本文主要分两部分,第一部分是约束的安装,主要看约束的整个操做过程;第二部分分析Block代码块中的操做 (下图是由做者青玉伏案 所作,做者作的很漂亮这里拿来参考一下)。html

Masonry结构图

第一部分 约束的安装部分

1. View+MASAdditions categary方法入口

1.1 方法入口通常以下,咱们从头开始分析:

//添加约束
- (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
//更新约束
- (NSArray *)mas_updateConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
//从新添加约束
- (NSArray *)mas_remakeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
复制代码

1.2 首先查看添加约束的方法,方法以下:

其实这段代码很简单,就是建立一个约束制造者,将block中的约束添加后更新约束,更新约束和从新添加约束代码相似,比第一种方式添加了updateExisting和removeExisting的设置数组

- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
    //translatesAutoresizingMaskIntoConstraints能够自动将frame转化为约束,设置约束时须要关闭
    self.translatesAutoresizingMaskIntoConstraints = NO;
    //约束制造者 --- 初始化,而且初始化约束的数组
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    //设置block的约束 --- 回到block的操做
    block(constraintMaker);
    //升级
    return [constraintMaker install];
}
复制代码

1.3 这一段代码暂时跳过,下文讲到MASViewConstraint类的install方法时回来查看就能够更快的明白做者的意图

- (instancetype)mas_closestCommonSuperview:(MAS_VIEW *)view {
    //获取两个视图最近的公共父视图 -----> 就是说一层一层的父视图找,直到两个视图的父视图是一个的时候 跳出循环
    //目的是为了给两个view添加约束时找到他的父视图
    MAS_VIEW *closestCommonSuperview = nil;

    MAS_VIEW *secondViewSuperview = view;
    while (!closestCommonSuperview && secondViewSuperview) {
        MAS_VIEW *firstViewSuperview = self;
        while (!closestCommonSuperview && firstViewSuperview) {
            if (secondViewSuperview == firstViewSuperview) {
                closestCommonSuperview = secondViewSuperview;
            }
            firstViewSuperview = firstViewSuperview.superview;
        }
        secondViewSuperview = secondViewSuperview.superview;
    }
    return closestCommonSuperview;
}
复制代码

2. MASConstraintMaker 约束管理者

上面在分类中调用了MASConstraintMaker中的两个方法,初始化操做和install操做,代码以下less

2.1 约束管理者初始化,主要是初始化一个存储约束的数组

- (id)initWithView:(MAS_VIEW *)view {
    self = [super init];
    if (!self) return nil;
    //当前视图
    self.view = view;
    //存储约束的数组
    self.constraints = NSMutableArray.new;
    return self;
}
复制代码

2.2 install操做

- (NSArray *)install {
    //这里判断是不是从新添加约束
    if (self.removeExisting) {
        //获取view中存在的约束
        NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];
        //获取到的数组挨个调用卸载方法,删除约束
        for (MASConstraint *constraint in installedConstraints) {
            [constraint uninstall];
        }
    }
    //self.constraints将block中添加的约束进行遍历
    NSArray *constraints = self.constraints.copy;
    for (MASConstraint *constraint in constraints) {
        //设置约束的updateExisting,若是调用的是更新方法在此处进行更新
        constraint.updateExisting = self.updateExisting;
        //将约束安装
        [constraint install];
    }
    //安装完成移除全部的约束
    [self.constraints removeAllObjects];
    return constraints;
}
复制代码

3. MASViewConstraint 具体的约束

3.1 原生类的操做

为了理清这个类的做用,咱们先看一下苹果为咱们提供的原生操做(以下),能够猜想这个类主要的操做就是下面重点标记的那一步操做ui

UIView *superview = self.view;
UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[superview addConstraints:@[

    //重点看这一步的操做
    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeTop
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeTop
                                multiplier:1.0
                                  constant:padding.top]
 ]];
复制代码

3.2 installedConstraintsForView方法

这里给view动态添加了一个mas_installedConstraints的get方法,里面存储了install中添加的方法,下面的方法会往这个数组中添加MASViewConstraint属性spa

+ (NSArray *)installedConstraintsForView:(MAS_VIEW *)view {
    return [view.mas_installedConstraints allObjects];
}
复制代码

3.3 install中的操做

3.3.1 咱们先看下install方法中的操做

方法比较长,先看其中最重要的方法,感受和苹果原生的使用很像,进入MASLayoutConstraint的头文件发现 @interface MASLayoutConstraint : NSLayoutConstraint,其实就是在这将约束添加到视图上的3d

//生成一个NSLayoutConstraint
 MASLayoutConstraint *layoutConstraint
 = [MASLayoutConstraint constraintWithItem:firstLayoutItem
                                        attribute:firstLayoutAttribute
                                        relatedBy:self.layoutRelation
                                           toItem:secondLayoutItem
                                        attribute:secondLayoutAttribute
                                       multiplier:self.layoutMultiplier
                                         constant:self.layoutConstant];
复制代码
3.3.2 咱们来详细分析这段代码每一句的意思

暂时忽略,代码中讲到mas_closestCommonSuperview方法能够在这进行跳转代理

- (void)install {
    //判断约束是否存在 不存在就直接返回
    if (self.hasBeenInstalled) {
        return;
    }
    //[self supportsActiveProperty]判断是否能响应
    if ([self supportsActiveProperty] && self.layoutConstraint) {
        //让约束起做用
        self.layoutConstraint.active = YES;
        //[self.firstViewAttribute.view.mas_installedConstraints addObject:self];
        return;
    }
    //获取父视图和当前视图及对应的约束 这里的Attribute是视图和约束的一个封装类
    MAS_VIEW *firstLayoutItem = self.firstViewAttribute.item;
    NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute;
    MAS_VIEW *secondLayoutItem = self.secondViewAttribute.item; 
    NSLayoutAttribute secondLayoutAttribute = self.secondViewAttribute.layoutAttribute;

    //若是secondViewAttribute不存在而且firstViewAttribute约束不是宽高,那么就把view的父视图及约束设置给secondViewAttribute
    if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) {
        secondLayoutItem = self.firstViewAttribute.view.superview;
        secondLayoutAttribute = firstLayoutAttribute;
    }
    //生成一个NSLayoutConstraint
    MASLayoutConstraint *layoutConstraint
        = [MASLayoutConstraint constraintWithItem:firstLayoutItem
                                        attribute:firstLayoutAttribute
                                        relatedBy:self.layoutRelation
                                           toItem:secondLayoutItem
                                        attribute:secondLayoutAttribute
                                       multiplier:self.layoutMultiplier
                                         constant:self.layoutConstant];
    
    layoutConstraint.priority = self.layoutPriority;
    layoutConstraint.mas_key = self.mas_key;
    //获得两个视图当前的公共superview
    if (self.secondViewAttribute.view) {
        /******************************************* / mas_closestCommonSuperview 这个方法具体操做能够回到View+MASAdditions类中查看就是上文告诉你们暂时跳过的地方 */
        MAS_VIEW *closestCommonSuperview = [self.firstViewAttribute.view mas_closestCommonSuperview:self.secondViewAttribute.view];
        NSAssert(closestCommonSuperview,
                 @"couldn't find a common superview for %@ and %@",
                 self.firstViewAttribute.view, self.secondViewAttribute.view);
        self.installedView = closestCommonSuperview;
    } else if (self.firstViewAttribute.isSizeAttribute) {
        self.installedView = self.firstViewAttribute.view;
    } else {
        self.installedView = self.firstViewAttribute.view.superview;
    }

    //若是是更新的话,判断具体更新的参数是什么 可到layoutConstraintSimilarTo中查看
    MASLayoutConstraint *existingConstraint = nil;
    if (self.updateExisting) {
        existingConstraint = [self layoutConstraintSimilarTo:layoutConstraint];
    }
    
    if (existingConstraint) {
        // 若是只是更新的话,更新一下这个约束
        existingConstraint.constant = layoutConstraint.constant;
        self.layoutConstraint = existingConstraint;
    } else {
        //添加layout到父视图,而且把约束类添加到mas_installedConstraints中
        [self.installedView addConstraint:layoutConstraint];
        self.layoutConstraint = layoutConstraint;
        [firstLayoutItem.mas_installedConstraints addObject:self];
    }
}
复制代码

3.4 uninstall中的操做

看完了上面的代码就能够松一口气了,下面的uninstall过程就很简单了code

- (void)uninstall {
    //判断可否响应active
    if ([self supportsActiveProperty]) {
        //让约束不起做用
        self.layoutConstraint.active = NO;
        //从数组里删除掉这个约束
[self.firstViewAttribute.view.mas_installedConstraints removeObject:self];
        return;
    }
    //从父视图删除约束
    [self.installedView removeConstraint:self.layoutConstraint];
    self.layoutConstraint = nil;
    self.installedView = nil;
    //删除约束
    [self.firstViewAttribute.view.mas_installedConstraints removeObject:self];
}
复制代码

第二部分 Block代码块操做

这部分操做讲述以前先上一张图,不知道你们有没有和我相同的疑问,那就是left和right调用颜色为何不一样,看完这部分你们就会有本身的答案了cdn

代码截图

感兴趣的朋友能够把下面代码粘贴到IDE试一下htm

[self.bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.right.and.bottom.equalTo(self);
            make.top.equalTo(self.topView.mas_bottom);
            make.height.equalTo(self.topView);
        }];
复制代码

1. make.top.equalTo(self.topView.mas_bottom)

咱们先来分析标题中的代码调用的方式

1.1 maker的属性调用流程及其结果

1.1.1 调用的left/right/top/bottom等方法

left/right/top/bottom.....,调用的方法相同

- (MASConstraint *)top {
    //添加不一样的约束属性 返回值是约束的属性
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
}
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    
    return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}
复制代码
1.1.2 添加属性,并返回一个MASViewAttribute对象

添加属性到constraints数组中,第一次进入constraint为空,那么去掉第一个判断的内容,代码以下

- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    //初始化一个MASViewAttribute,存放view和layoutAttribute
    MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
    //初始化一个MASViewConstraint
    MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
    if (!constraint) {
        //设置代理方法
        newConstraint.delegate = self;
        //有的话就添加到constraints
        [self.constraints addObject:newConstraint];
    }
    return newConstraint;
}
复制代码

1.2 equalTo调用分析

1.2.1 查找equalTo具体调用

经过对equalTo的查找发现调用的是以下的代码,能够看到这部分方法中调用的代码是相同的,因为MASAttribute是一个抽象类,在类中没有找到其实现方法,经过上一节返回类型MASViewAttribute查找其具体实现

- (MASConstraint * (^)(id))equalTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
    };
}

- (MASConstraint * (^)(id))mas_equalTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
    };
}

- (MASConstraint * (^)(id))greaterThanOrEqualTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationGreaterThanOrEqual);
    };
}

- (MASConstraint * (^)(id))mas_greaterThanOrEqualTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationGreaterThanOrEqual);
    };
}

- (MASConstraint * (^)(id))lessThanOrEqualTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationLessThanOrEqual);
    };
}

- (MASConstraint * (^)(id))mas_lessThanOrEqualTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationLessThanOrEqual);
    };
}
复制代码
1.2.2 equalToWithRelation调用
- (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;
            //把maker的constraints数组中的单个约束MASViewConstraint替换成MASCompositeConstraint这种组合约束
            [self.delegate constraint:self shouldBeReplacedWithConstraint:compositeConstraint];
            return compositeConstraint;
        } else {
            NSAssert(!self.hasLayoutRelation || self.layoutRelation == relation && [attribute isKindOfClass:NSValue.class], @"Redefinition of constraint relation");
            //若是是NSValue直接给MASViewConstraint赋值
            self.layoutRelation = relation;
            self.secondViewAttribute = attribute;
            return self;
        }
    };
}
复制代码

2. make.left.right.and.bottom.equalTo(self)


咱们先来分析标题中的代码调用的方式,区别于第一种,这种属于组合形式

2.1 类MASCompositeConstraint介绍

MASCompositeConstraint,一个组合约束类,集成自抽象类MASConstraint,其属性childConstraints数组包含了其组合的MASViewConstraint约束类,也就是说当一个block代码块中一行添加的约束超过一个之后,masonry内部会将约束统一更新为MASCompositeConstraint类,并在最后将MASCompositeConstraint实例添加到maker的constraints中

2.2 当约束超过一个时的调用

约束为一个的时候与上一小节代码执行顺序没有区别,也就是说执行make.left的时候与上文的调用方式相同,可是当调用到right的时候,程序的调用方式发生改变,改变以下(你们能够对照源码查看)

//调用.right首先调用下面代码
#import "MASConstraint.h"
- (MASConstraint *)right {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRight];
}
//接着会走子类方法
#import "MASViewConstraint.h"
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation");
    //能够看到此时的constraint已经赋值了,delegate指向maker类
    return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
}
//调用maker类中的代理方法
#import "MASConstraintMaker.h"
- (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;
        //把maker的constraints数组中的单个约束MASViewConstraint替换成MASCompositeConstraint这种组合约束
        [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
        return compositeConstraint;
    }
    //这部分就不会执行了
    if (!constraint) {
        newConstraint.delegate = self;
        //有的话就添加到constraints
        [self.constraints addObject:newConstraint];
    }
    return newConstraint;
}
复制代码

至此masonry的分析就完成了,剩下的就是一次次的重复调用了

结语

masonry的链式语法很漂亮,有时间会给你们分析一下

相关文章
相关标签/搜索