源码阅读:Masonry(七)—— MASConstraintMaker

508 阅读7分钟

该文章阅读的 Masonry 的版本为 1.1.0。

这个类可以说是 Masonry 的核心类了,根据该类的名字可以知道这个类是用于创建约束的工厂类。

1.公共枚举

typedef NS_OPTIONS(NSInteger, MASAttribute) {
    MASAttributeLeft = 1 << NSLayoutAttributeLeft,
    MASAttributeRight = 1 << NSLayoutAttributeRight,
    MASAttributeTop = 1 << NSLayoutAttributeTop,
    MASAttributeBottom = 1 << NSLayoutAttributeBottom,
    MASAttributeLeading = 1 << NSLayoutAttributeLeading,
    MASAttributeTrailing = 1 << NSLayoutAttributeTrailing,
    MASAttributeWidth = 1 << NSLayoutAttributeWidth,
    MASAttributeHeight = 1 << NSLayoutAttributeHeight,
    MASAttributeCenterX = 1 << NSLayoutAttributeCenterX,
    MASAttributeCenterY = 1 << NSLayoutAttributeCenterY,
    MASAttributeBaseline = 1 << NSLayoutAttributeBaseline,
    
    MASAttributeFirstBaseline = 1 << NSLayoutAttributeFirstBaseline,
    MASAttributeLastBaseline = 1 << NSLayoutAttributeLastBaseline,
    
    MASAttributeLeftMargin = 1 << NSLayoutAttributeLeftMargin,
    MASAttributeRightMargin = 1 << NSLayoutAttributeRightMargin,
    MASAttributeTopMargin = 1 << NSLayoutAttributeTopMargin,
    MASAttributeBottomMargin = 1 << NSLayoutAttributeBottomMargin,
    MASAttributeLeadingMargin = 1 << NSLayoutAttributeLeadingMargin,
    MASAttributeTrailingMargin = 1 << NSLayoutAttributeTrailingMargin,
    MASAttributeCenterXWithinMargins = 1 << NSLayoutAttributeCenterXWithinMargins,
    MASAttributeCenterYWithinMargins = 1 << NSLayoutAttributeCenterYWithinMargins,
};

这个枚举对原有枚举进行了二次封装。

2.公共属性

@property (nonatomic, strong, readonly) MASConstraint *left;
@property (nonatomic, strong, readonly) MASConstraint *top;
@property (nonatomic, strong, readonly) MASConstraint *right;
@property (nonatomic, strong, readonly) MASConstraint *bottom;
@property (nonatomic, strong, readonly) MASConstraint *leading;
@property (nonatomic, strong, readonly) MASConstraint *trailing;
@property (nonatomic, strong, readonly) MASConstraint *width;
@property (nonatomic, strong, readonly) MASConstraint *height;
@property (nonatomic, strong, readonly) MASConstraint *centerX;
@property (nonatomic, strong, readonly) MASConstraint *centerY;
@property (nonatomic, strong, readonly) MASConstraint *baseline;
@property (nonatomic, strong, readonly) MASConstraint *firstBaseline;
@property (nonatomic, strong, readonly) MASConstraint *lastBaseline;
@property (nonatomic, strong, readonly) MASConstraint *leftMargin;
@property (nonatomic, strong, readonly) MASConstraint *rightMargin;
@property (nonatomic, strong, readonly) MASConstraint *topMargin;
@property (nonatomic, strong, readonly) MASConstraint *bottomMargin;
@property (nonatomic, strong, readonly) MASConstraint *leadingMargin;
@property (nonatomic, strong, readonly) MASConstraint *trailingMargin;
@property (nonatomic, strong, readonly) MASConstraint *centerXWithinMargins;
@property (nonatomic, strong, readonly) MASConstraint *centerYWithinMargins;

上面这一些属性分别对应着当前视图的约束属性,也就是 view1attr1


/**
 这个属性可以同时设置 NSLayoutAttributeTop、NSLayoutAttributeLeft、NSLayoutAttributeBottom 和 NSLayoutAttributeRight 这四个约束属性
 */
@property (nonatomic, strong, readonly) MASConstraint *edges;

/**
 这个属性可以同时设置 NSLayoutAttributeWidth 和 NSLayoutAttributeHeight 这两个约束属性
 */
@property (nonatomic, strong, readonly) MASConstraint *size;

/**
 这个属性可以同时设置 NSLayoutAttributeCenterX 和 NSLayoutAttributeCenterY 这两个约束属性
 */
@property (nonatomic, strong, readonly) MASConstraint *center;

这三个属性也是用来设置当前视图的约束属性的,只不过做了封装,可以更方便简洁的进行设置,具体


@property (nonatomic, strong, readonly) MASConstraint *(^attributes)(MASAttribute attrs);

这个属性是一个 block 类型的,可以直接设置属性。

具体的用法如下:

如果使用 MASConstraintMaker 提供属性设置是这样的:

make.top.left.bottom.right.equalTo(self.view);

如果直接使用属性 attributes 进行设置就是这样的:

make.attributes(MASAttributeTop | MASAttributeLeft | MASAttributeBottom | MASAttributeRight).equalTo(self.view);

上面两种设置方式的效果是相同的。


/**
 在添加约束时是否检查该约束已经被添加,用于方法 mas_updateConstraints: 中
 */
@property (nonatomic, assign) BOOL updateExisting;
/**
 在添加约束时是否移除已添加的约束,由于方法 mas_remakeConstraints 中
 */
@property (nonatomic, assign) BOOL removeExisting;

3.公共方法

/**
 以指定视图初始化本类对象的方法
 */
- (id)initWithView:(MAS_VIEW *)view;
/**
 安装通过该类对象添加的所有约束
 */
- (NSArray *)install;
/**
 这个方法用于传入一个 block 
 具体这个方法是做什么的不太清楚,没有注释,也没有例子
 从该方法的实现来看,好像是对该对象中的约束对象进行添加
 */
- (MASConstraint * (^)(dispatch_block_t))group;

4.类扩展

/**
 保存 view1
 */
@property (nonatomic, weak) MAS_VIEW *view;
/**
 保存设置的约束对象
 */
@property (nonatomic, strong) NSMutableArray *constraints;

5. 实现

5.1 私有方法

- (MASConstraint *)addConstraintWithAttributes:(MASAttribute)attrs {
    // 定义变量获取所有可设置的枚举值
    // __unused 的作用是:如果定义的变量未使用的话编译器就会报警,添加 __unused 前缀以后就不会有报警
    __unused MASAttribute anyAttribute = (MASAttributeLeft | MASAttributeRight | MASAttributeTop | MASAttributeBottom | MASAttributeLeading
                                          | MASAttributeTrailing | MASAttributeWidth | MASAttributeHeight | MASAttributeCenterX
                                          | MASAttributeCenterY | MASAttributeBaseline
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
                                          | MASAttributeFirstBaseline | MASAttributeLastBaseline
#endif
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000)
                                          | MASAttributeLeftMargin | MASAttributeRightMargin | MASAttributeTopMargin | MASAttributeBottomMargin
                                          | MASAttributeLeadingMargin | MASAttributeTrailingMargin | MASAttributeCenterXWithinMargins
                                          | MASAttributeCenterYWithinMargins
#endif
                                          );
    // 传入的参数必须是枚举中的枚举值
    NSAssert((attrs & anyAttribute) != 0, @"You didn't pass any attribute to make.attributes(...)");
    
    // 创建变量保存视图属性封装对象
    NSMutableArray *attributes = [NSMutableArray array];
    
    // 根据传入的枚举值添加相应的视图属性封装对象
    if (attrs & MASAttributeLeft) [attributes addObject:self.view.mas_left];
    if (attrs & MASAttributeRight) [attributes addObject:self.view.mas_right];
    if (attrs & MASAttributeTop) [attributes addObject:self.view.mas_top];
    if (attrs & MASAttributeBottom) [attributes addObject:self.view.mas_bottom];
    if (attrs & MASAttributeLeading) [attributes addObject:self.view.mas_leading];
    if (attrs & MASAttributeTrailing) [attributes addObject:self.view.mas_trailing];
    if (attrs & MASAttributeWidth) [attributes addObject:self.view.mas_width];
    if (attrs & MASAttributeHeight) [attributes addObject:self.view.mas_height];
    if (attrs & MASAttributeCenterX) [attributes addObject:self.view.mas_centerX];
    if (attrs & MASAttributeCenterY) [attributes addObject:self.view.mas_centerY];
    if (attrs & MASAttributeBaseline) [attributes addObject:self.view.mas_baseline];
    
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
    
    if (attrs & MASAttributeFirstBaseline) [attributes addObject:self.view.mas_firstBaseline];
    if (attrs & MASAttributeLastBaseline) [attributes addObject:self.view.mas_lastBaseline];
    
#endif
    
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000)
    
    if (attrs & MASAttributeLeftMargin) [attributes addObject:self.view.mas_leftMargin];
    if (attrs & MASAttributeRightMargin) [attributes addObject:self.view.mas_rightMargin];
    if (attrs & MASAttributeTopMargin) [attributes addObject:self.view.mas_topMargin];
    if (attrs & MASAttributeBottomMargin) [attributes addObject:self.view.mas_bottomMargin];
    if (attrs & MASAttributeLeadingMargin) [attributes addObject:self.view.mas_leadingMargin];
    if (attrs & MASAttributeTrailingMargin) [attributes addObject:self.view.mas_trailingMargin];
    if (attrs & MASAttributeCenterXWithinMargins) [attributes addObject:self.view.mas_centerXWithinMargins];
    if (attrs & MASAttributeCenterYWithinMargins) [attributes addObject:self.view.mas_centerYWithinMargins];
    
#endif
    
    // 创建变量保存视图约束封装对象
    NSMutableArray *children = [NSMutableArray arrayWithCapacity:attributes.count];
    
    // 遍历视图属性封装对象
    for (MASViewAttribute *a in attributes) {
        // 创建视图约束封装对象,并添加到数组中
        [children addObject:[[MASViewConstraint alloc] initWithFirstViewAttribute:a]];
    }
    
    // 创建多约束封装对象
    MASCompositeConstraint *constraint = [[MASCompositeConstraint alloc] initWithChildren:children];
    // 设置代理对象
    constraint.delegate = self;
    // 添加到数组中保存
    [self.constraints addObject:constraint];
    // 多约束封装对象
    return constraint;
}
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    // 调用 MASConstraintDelegate 中的方法,并且第一个参数传 nil,也就是创建 MASViewConstraint 类型对象
    return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}

5.2 MASConstraintDelegate 方法

- (void)constraint:(MASConstraint *)constraint shouldBeReplacedWithConstraint:(MASConstraint *)replacementConstraint {
    // 获取要替换约束在当前数数中的索引
    NSUInteger index = [self.constraints indexOfObject:constraint];
    NSAssert(index != NSNotFound, @"Could not find constraint %@", constraint);
    // 替换数组中地址索引的约束
    [self.constraints replaceObjectAtIndex:index withObject:replacementConstraint];
}
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    // 根据当前视图和传入的参数创建视图属性封装对象
    MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
    // 根据生成的视图属性封装对象创建视图约束封装对象
    MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
    // 判断参数是否是 MASViewConstraint 类型的
    if ([constraint isKindOfClass:MASViewConstraint.class]) {
        // 如果是,也就是设置多约束:make.top.left.equalTo(self.view); 
        // 创建数组保存约束对象
        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;
}

5.4 公共方法

- (id)initWithView:(MAS_VIEW *)view {
    self = [super init];
    if (!self) return nil;
    
    // 保存参数
    self.view = view;
    // 初始化属性
    self.constraints = NSMutableArray.new;
    
    return self;
}
- (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;
}
- (MASConstraint *)left {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}

- (MASConstraint *)top {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
}

- (MASConstraint *)right {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRight];
}

- (MASConstraint *)bottom {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBottom];
}

- (MASConstraint *)leading {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeading];
}

- (MASConstraint *)trailing {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTrailing];
}

- (MASConstraint *)width {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeWidth];
}

- (MASConstraint *)height {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeHeight];
}

- (MASConstraint *)centerX {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterX];
}

- (MASConstraint *)centerY {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterY];
}

- (MASConstraint *)baseline {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBaseline];
}

- (MASConstraint *)firstBaseline {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeFirstBaseline];
}

- (MASConstraint *)lastBaseline {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLastBaseline];
}
- (MASConstraint *)leftMargin {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeftMargin];
}

- (MASConstraint *)rightMargin {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeRightMargin];
}

- (MASConstraint *)topMargin {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTopMargin];
}

- (MASConstraint *)bottomMargin {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeBottomMargin];
}

- (MASConstraint *)leadingMargin {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeadingMargin];
}

- (MASConstraint *)trailingMargin {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTrailingMargin];
}

- (MASConstraint *)centerXWithinMargins {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterXWithinMargins];
}

- (MASConstraint *)centerYWithinMargins {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeCenterYWithinMargins];
}

上面的这些方法都是直接调用 addConstraintWithLayoutAttribute: 方法,并且传递对应的参数,也就是不同的约束属性

- (MASConstraint *)edges {
    return [self addConstraintWithAttributes:MASAttributeTop | MASAttributeLeft | MASAttributeRight | MASAttributeBottom];
}

- (MASConstraint *)size {
    return [self addConstraintWithAttributes:MASAttributeWidth | MASAttributeHeight];
}

- (MASConstraint *)center {
    return [self addConstraintWithAttributes:MASAttributeCenterX | MASAttributeCenterY];
}

- (MASConstraint *(^)(MASAttribute))attributes {
    return ^(MASAttribute attrs){
        return [self addConstraintWithAttributes:attrs];
    };
}

这四个方法调用的是 addConstraintWithLayoutAttribute: 方法,传递多个约束属性。

- (MASConstraint *(^)(dispatch_block_t group))group {
    return ^id(dispatch_block_t group) {
        // 记录执行 block 之前约束封装对象的数量
        NSInteger previousCount = self.constraints.count;
        // 执行 block
        group();

        // 获取执行 block 之后新增的约束封装对象
        NSArray *children = [self.constraints subarrayWithRange:NSMakeRange(previousCount, self.constraints.count - previousCount)];
        // 将新增的约束封装对象封装成一个多约束封装对象
        MASCompositeConstraint *constraint = [[MASCompositeConstraint alloc] initWithChildren:children];
        // 设置代理
        constraint.delegate = self;
        // 返回多约束封装对象
        return constraint;
    };
}

6. 总结

这个类是用来创建约束封装对象,无论是视图约束封装对象,还是多视图约束封装对象。该类虽然是继承自 NSObject,但是也实现了 MASConstraint 类相同的链式编程的效果。