Masonry中的mas_makeConstraints方法

一、简单介绍

我们一般来说会这样进行使用

 [view mas_makeConstraints:^(MASConstraintMaker *make) {
       //这个使用的就是MASCompositeConstraint类型的
        make.left.top.width.height.mas_equalTo(100).multipliedBy(1);
            //这个使用的就是单个单个的MASViewConstraint
//            make.left.mas_equalTo(100);
//            make.top.mas_equalTo(100);
//            make.width.mas_equalTo(100);
//            make.height.mas_equalTo(100);
    }];
然后我们先看看其内部的方法
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
    
    self.translatesAutoresizingMaskIntoConstraints = NO;
    
    //创建MASConstraintMaker对象,就是我们block中的make,
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    
    //执行block
    block(constraintMaker);
    
    //安装install 约束
    return [constraintMaker install];
}

我们再去看看install的方法,其内部就是去先去获取view的所有的约束,然后进行移除,之后再去安装新的约束

- (NSArray *)install {
 //判断是否要移除已经存在的约束,这里其实就是在使用re_make的时候这个会为YES
 if (self.removeExisting) {
        
        //获取view的所有的约束
        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;
}

这个时候大家肯定很疑惑,What? 我约束都还没有添加,怎么直接开始遍历了? 其实我们在block中执行make.left.width这个时候其实就已经在添加约束了,先来看下在调用.left的时候调用的MASConstraintMaker的方法


返回值是MASConstriant,所以我们在make.left之后再.width其实是调用的是MASConstraint的width方法了其中这个方法是个抽象方法定义在MASConstraint类中。


然后之后调用的就是MASViewConstraint中的addConstraintWithLayoutAttribute方法


然后我们点进上面的self.delegate 调用的方法进去看看,调用的是下面的方法,这里其实就可以理解为把left和Width的约束进行合并成为一个约束集合类

- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
   
    //根据layoutAttribute属性也就是NSLayoutAttributeTop这些初始化MASViewAttribute类
    MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
    
    //根据viewAttribute创建MASViewConstraint对象
    MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
    //如果有constraint的话,就进行合并成一个composite Constraint,这个是一个集合类
    if ([constraint isKindOfClass:MASViewConstraint.class]) {
        //replace with composite constraint
        NSArray *children = @[constraint, newConstraint];
        
        /*
         MASCompositeConstraint:约束的集合类。内部有一个数组,可以保存多个MASViewConstraint。对MASCompositeConstraint调用方法实际等于
         对其内部的所有MASViewConstraint调用方法
         */
        MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
        
        compositeConstraint.delegate = self;
        
        //在self.constraint进行替换
        [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
        
        return compositeConstraint;
    }
    //如果不存在constraint那就去设置newConstraint,然后添加进入约束数组
    if (!constraint) {
        newConstraint.delegate = self;
        //add
        [self.constraints addObject:newConstraint];
    }
    return newConstraint;
}

再去看看constraintMaker中的install方法中的NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];里面的installedConstraintsForView方法内部调用了mas_installedConstraints的get方法,然后去获取所有的对象,也就是这里面的方法


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

再去看看这个get方法其实就是运用了运行时的知识

- (NSMutableSet *)mas_installedConstraints {
    
    NSMutableSet *constraints = objc_getAssociatedObject(self, &kInstalledConstraintsKey);
    
    if (!constraints) {
        
        constraints = [NSMutableSet set];
        
        //相当于 setValue:forKey 进行关联value对象
        objc_setAssociatedObject(self, &kInstalledConstraintsKey, constraints, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return constraints;
}

然后再去看看关于uninstall方法的实现,这个方法的实现就是先判断能不能相应active方法,因为这个属性是ios8才出现的

- (void)uninstall {
    
    if ([self supportsActiveProperty]) {
        //设置约束不可用
        self.layoutConstraint.active = NO;
        //移除约束
        [self.firstViewAttribute.view.mas_installedConstraints removeObject:self];
        
        return;
    }
    //来到下面表示不能响应active方法,做了适配
    [self.installedView removeConstraint:self.layoutConstraint];
    
    self.layoutConstraint = nil;
    
    self.installedView = nil;
    
    [self.firstViewAttribute.view.mas_installedConstraints removeObject:self];
}


我们再去看看安装新约束当中的install方法

- (void)install {
    
    //判断约束是否已经存在,以及是否已经处理激活状态其实就是使用状态
    if (self.hasBeenInstalled) {
        return;
    }
    
    //判断布局是否可以响应active这个方法的设置,判断layoutConstriant存不存在
    if ([self supportsActiveProperty] && self.layoutConstraint) {
        
        //iOS 6.0或者7.0调用addConstraints
        //[self.view addConstraints:@[leftConstraint, rightConstraint, topConstraint, heightConstraint]];
        //iOS 8.0以后设置active属性值 就可以去使用约束了
        self.layoutConstraint.active = YES;
        
        //这里会进行添加约束
        [self.firstViewAttribute.view.mas_installedConstraints addObject:self];
        
        return;
    }
    //获取item,获取第一个viewattribute的item也就是constraintWithItem中的item
    MAS_VIEW *firstLayoutItem = self.firstViewAttribute.item;
    
    //获取属性比如说NSLayoutAttributeTop
    NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute;
    
    //获取约束的第二个view
    MAS_VIEW *secondLayoutItem = self.secondViewAttribute.item;
    
    //获取layout的属性
    NSLayoutAttribute secondLayoutAttribute = self.secondViewAttribute.layoutAttribute;

    // alignment attributes must have a secondViewAttribute
    // therefore we assume that is refering to superview
    // eg make.left.equalTo(@10)
    
    //判断是不是要设置的是size的约束,以及判断第二个约束的属性是不是为空如果为空,就去设置下面的属性
    if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) {
        secondLayoutItem = self.firstViewAttribute.view.superview;
        secondLayoutAttribute = firstLayoutAttribute;
    }
    //创建约束布局 self.layoutRelation就是约束关系
    MASLayoutConstraint *layoutConstraint
        = [MASLayoutConstraint constraintWithItem:firstLayoutItem
                                        attribute:firstLayoutAttribute
                                        relatedBy:self.layoutRelation
                                           toItem:secondLayoutItem
                                        attribute:secondLayoutAttribute
                                       multiplier:self.layoutMultiplier
                                         constant:self.layoutConstant];
    
    //设置约束的优先级
    layoutConstraint.priority = self.layoutPriority;
    
    /*
     当约束冲突发生的时候,我们可以设置view的key来定位是哪个view
     redView.mas_key = @"redView";
     greenView.mas_key = @"greenView";
     blueView.mas_key = @"blueView";
     若是觉得这样一个个设置比较繁琐,怎么办呢,Masonry则提供了批量设置的宏MASAttachKeys
     MASAttachKeys(redView,greenView,blueView); //一句代码即可全部设置
     */
    layoutConstraint.mas_key = self.mas_key;
    
    //判断第二个view是否存在
    if (self.secondViewAttribute.view) {
        
        //寻找两个视图的公共父视图,这个方法其实就是循环遍历寻找
        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;
    }
    //如果是设置view的宽度和高度的,设置安装越苏的视图为第一个view
    else if (self.firstViewAttribute.isSizeAttribute) {
        self.installedView = self.firstViewAttribute.view;
    }
    //否则就给superview进行设置
    else {
        self.installedView = self.firstViewAttribute.view.superview;
    }


    MASLayoutConstraint *existingConstraint = nil;
    
    //判断是否是更新约束,这里判断的条件就是是否只存在constant不一样的视图
    if (self.updateExisting) {
        existingConstraint = [self layoutConstraintSimilarTo:layoutConstraint];
    }
    //如果已经存在只有constant不一样的约束,就去更新constant
    if (existingConstraint) {
        // just update the constant
        existingConstraint.constant = layoutConstraint.constant;
        
        self.layoutConstraint = existingConstraint;
    }
    //如果没有存在的只有constant不一样的约束,就去添加约束
    else {
        [self.installedView addConstraint:layoutConstraint];
        
        self.layoutConstraint = layoutConstraint;
        
        [firstLayoutItem.mas_installedConstraints addObject:self];
    }
}

关于上面判断是否只存在constant不一样的视图的方法

- (MASLayoutConstraint *)layoutConstraintSimilarTo:(MASLayoutConstraint *)layoutConstraint {
    // check if any constraints are the same apart from the only mutable property constant

    // go through constraints in reverse as we do not want to match auto-resizing or interface builder constraints
    // and they are likely to be added first.
    
    //从后面往前面遍历这个数组
    for (NSLayoutConstraint *existingConstraint in self.installedView.constraints.reverseObjectEnumerator) {
        if (![existingConstraint isKindOfClass:MASLayoutConstraint.class]) continue;
        if (existingConstraint.firstItem != layoutConstraint.firstItem) continue;
        if (existingConstraint.secondItem != layoutConstraint.secondItem) continue;
        if (existingConstraint.firstAttribute != layoutConstraint.firstAttribute) continue;
        if (existingConstraint.secondAttribute != layoutConstraint.secondAttribute) continue;
        if (existingConstraint.relation != layoutConstraint.relation) continue;
        if (existingConstraint.multiplier != layoutConstraint.multiplier) continue;
        if (existingConstraint.priority != layoutConstraint.priority) continue;

        return (id)existingConstraint;
    }
    return nil;
}

关于self.hasBeenInstalled其实就是调用了下面的方法

- (BOOL)hasBeenInstalled {
    return (self.layoutConstraint != nil) && [self isActive];
}

其实Masonry就是对系统本身的自动布局的layout进行了封装


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值