Masonry源码阅读

作者: HoooChan | 来源:发表于2018-11-24 18:04 被阅读18次

Masonry源码阅读

AutoLayout是Apple在iOS6中新增的UI布局适配的方法,用来替代iOS6之前的AutoResizing。自动布局的约束可以通过拖拽的方式添加,也可以通过代码添加。AutoLayout对应的代码约束就是NSLayoutConstraint。

NSLayoutConstraint的API虽然很简单,但是添加约束的代码量却很大,因此出现了许多对NSLayoutConstraint的封装,Masonry就是其中一个。

为了使子视图填满父视图并且留10像素的间隙,来看看用官方API和Masonry添加约束的差别:
官方API:

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:@[

    //view1 constraints
    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeTop
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeTop
                                multiplier:1.0
                                  constant:padding.top],

    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeLeft
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeLeft
                                multiplier:1.0
                                  constant:padding.left],

    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeBottom
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeBottom
                                multiplier:1.0
                                  constant:-padding.bottom],

    [NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeRight
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeRight
                                multiplier:1
                                  constant:-padding.right],

 ]];

Masonry

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(superview).with.insets(padding);
}];

可以明显看到使用Masonry添加约束的代码精简许多。

基本使用

Msonry的使用方法很简单,主要有以下三个方法:

  • mas_remakeConstraints会清除View已有的约束,再添加新的约束
  • mas_makeConstraints不会清楚之前的约束,直接添加新的约束
  • mas_updateConstraints如果该条件的约束已经存在,则直接更新,否则添加新的约束。

添加约束的时机

查看官方实例即可一目了然

@implementation DIYCustomView

- (id)init {
    self = [super init];
    if (!self) return nil;

    // --- Create your views here ---
    self.button = [[UIButton alloc] init];

    return self;
}

// tell UIKit that you are using AutoLayout
+ (BOOL)requiresConstraintBasedLayout {
    return YES;
}

// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {

    // --- remake/update constraints here
    [self.button remakeConstraints:^(MASConstraintMaker *make) {
        make.width.equalTo(@(self.buttonSize.width));
        make.height.equalTo(@(self.buttonSize.height));
    }];
    
    //according to apple super should be called at end of method
    [super updateConstraints];
}

- (void)didTapButton:(UIButton *)button {
    // --- Do your changes ie change variables that affect your layout etc ---
    self.buttonSize = CGSize(200, 200);

    // tell constraints they need updating
    [self setNeedsUpdateConstraints];
}

@end

有几个需要注意的点:

  • updateConstraints方法中添加约束要用mas_remakeConstraints方法
  • updateConstraints方法中要在最后调用父类的方法
  • 更新约束后要调用[self setNeedsUpdateConstraints]

setNeedsUpdateConstraints:当一个自定义view的某个属性发生改变,并且可能影响到constraint时,需要调用此方法去标记constraints需要在未来的某个点更新,系统然后调用updateConstraints;
updateConstraintsIfNeeded:立即触发约束更新,自动更新布局。

使用技巧

避免压缩和拉伸

一般我们会通过以下代码来添加两个并列的视图:

    UILabel *typeLable = [[UILabel alloc] init];
    typeLable.text = @"类型";
    typeLable.textColor = [UIColor whiteColor];
    typeLable.backgroundColor = [UIColor grayColor];
    [self.view addSubview:typeLable];
    [typeLable mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerY.equalTo(self.view);
        make.left.equalTo(self.view).offset(15);
    }];
    
    UILabel *titleLable = [[UILabel alloc] init];
    titleLable.text = @"标题";
    titleLable.textColor = [UIColor blackColor];
    titleLable.backgroundColor = [UIColor orangeColor];
    titleLable.font = [UIFont systemFontOfSize:25];
    [self.view addSubview:titleLable];
    [titleLable mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(typeLable.mas_right);
        make.centerY.equalTo(typeLable);
        make.right.equalTo(self.view).offset(-15);
    }];

运行之后会发现左侧的View被拉伸了,但实际上我们会希望左边保持原有大小,而拉伸右侧的。这时我们可以再添加另外的约束来实现这个效果:

    [typeLable setContentCompressionResistancePriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal];
    [typeLable setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal];
    [titleLable setContentCompressionResistancePriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal];
    [titleLable setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal];

最后的结果:


使某个约束失效

这种情况一般出现九宫格图片时,可能需要设置contentView的bottom:

make.bottom.equalTo(self.contentView);

如果cell被复用了,就需要使之前的底部约束失效,重新再设置。所有可以用一个全局变量保存这个MASConstraint,在重新设置之前先调用[constraint uninstall]

解决常见的约束冲突警告

UITableViewCell 的约束冲突

2017-12-29 16:24:42.645364+0800 project[3804:770025] [LayoutConstraints] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
[
    <MASLayoutConstraint:0x6040002b7d60 UILabel:0x7fa5e0c5c490.top == UITableViewCellContentView:0x7fa5e0c5c280.top + 10>,
    <MASLayoutConstraint:0x6040002b85a0 UILabel:0x7fa5e0c5c770.top == UILabel:0x7fa5e0c5c490.bottom + 10>,
    <MASLayoutConstraint:0x6040002b8f00 UILabel:0x7fa5e0c5d010.top == UILabel:0x7fa5e0c5c770.bottom + 5>,
    <MASLayoutConstraint:0x6040002b94a0 UILabel:0x7fa5e0c5d5d0.top == UILabel:0x7fa5e0c5d010.bottom + 5>,
    <MASLayoutConstraint:0x6040002b9800 UILabel:0x7fa5e0c5d8b0.top == UILabel:0x7fa5e0c5d5d0.bottom + 5>,
    <MASLayoutConstraint:0x6040002b9aa0 UILabel:0x7fa5e0c5d8b0.bottom == UITableViewCellContentView:0x7fa5e0c5c280.bottom - 10>,
    <NSLayoutConstraint:0x600000290810 UITableViewCellContentView:0x7fa5e0c5c280.height == 43.6667>
]

Will attempt to recover by breaking constraint 
<MASLayoutConstraint:0x6040002b85a0 UILabel:0x7fa5e0c5c770.top == UILabel:0x7fa5e0c5c490.bottom + 10>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

找到被覆盖的约束:<MASLayoutConstraint:0x6040002b85a0 UILabel:0x7fa5e0c5c770.top == UILabel:0x7fa5e0c5c490.bottom + 10>,给它添加优先级即可消除警告:

make.top.equalTo(self.titleLable.mas_bottom).offset(10).priority(500);

自定义View的NSAutoresizingMaskLayoutConstraint冲突

Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<MASLayoutConstraint:0x7fafcb1c8e10 UIButton:0x7fafcb18e160.width == UIView:0x7fafcb0bd380.width - 60>",
    "<NSAutoresizingMaskLayoutConstraint:0x7fafcb0a2570 UIView:0x7fafcb0bd380.width == 0>"
)

Will attempt to recover by breaking constraint 
<MASLayoutConstraint:0x7fafcb1c8e10 UIButton:0x7fafcb18e160.width == UIView:0x7fafcb0bd380.width - 60>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

解决方法:View的translatesAutoresizingMaskIntoConstraints设置为NO

将UIStackView的translatesAutoresizingMaskIntoConstraints设为NO

如果设置了UIStackView的Spacing可能会出现冲突,此时需要将UIStackView的translatesAutoresizingMaskIntoConstraints设为NO。

源码解析

Masonry之所以可以如此简洁地添加约束,一个主要原因是它可以使用链式语法。在后面的源码阅读中可以重点看看它是如何实现的。

添加一个简单的约束:

    UIView *view = [[UIView alloc] init];
    [self.view addSubview:view];
    [view mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.view);
    }];

跟踪代码会发现它进入了分类的一个方法中:

- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
    self.translatesAutoresizingMaskIntoConstraints = NO;
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    block(constraintMaker);
    return [constraintMaker install];
}

这里做了4件事:

  • 1、将View的translatesAutoresizingMaskIntoConstraints属性设置为NO
  • 2、初始化了一个MASConstraintMaker
  • 3、调用添加约束的block;
  • 4、调用[constraintMaker install]

这里View并没有持有block,block执行后就会释放,所有不会形成循环引用。

来到我们写的添加约束的代码中make.left.equalTo(self.view);

首先是make.left,来到MASConstraintMaker的edges方法,返回的是一个MASConstraint

- (MASConstraint *)left {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}

最终会来到:

- (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;
}
  • 1、根据NSLayoutAtrribute生成MASViewAttribute;
  • 2、根据MASViewAtrribute生成MASViewConstraint,这个viewAtrribute是newConstraint的firstViewAttribute;
  • 3、如果传来的constraint不为空且为MASViewConstraints,则生成一个MASCompositeConstraint,并替换原有的constraint;
  • 4、否则将newConstraint的delegate设为此MASConstaintMaker, 并保存;

这里的delegate都是为了实现链式调用而实现的,因为我们可能会这样调用make.left.right,make.left返回的是MASViewConstraint,它也有right方法:

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

- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation");

    return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
}

最后调用的是delegate的方法,从前面可以知道delegate其实就是MASConstraintMaker,所以最后回到MASConstraintMaker来创建。

这里调用的[self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute]传入了self,也就是MASViewConstraint(make.left),最后会被新的MASCompositeConstraint替换。


到目前为止都是为了创建MASConstraint,主要出现了三个类,MASConstraintMakerMASViewConstraintMASCompositeConstraint

MASConstraintMaker就是为了给View创建MASConstraint的。它有个属性constraints用来保存每一个约束。

MASViewConstraint和MASCompositeConstraint都继承自MASConstraint。

MASViewConstraint有两个重要的属性,firstViewAttribute和secondViewAttribute,都是MASViewAttribute,而MASViewAttribute保存的又是NSLayoutAttribute。来看原生的创建约束的方法:

[NSLayoutConstraint constraintWithItem:view1
                                 attribute:NSLayoutAttributeTop
                                 relatedBy:NSLayoutRelationEqual
                                    toItem:superview
                                 attribute:NSLayoutAttributeTop
                                multiplier:1.0
                                  constant:padding.top]

可以看到有两个attribute,MASViewConstraint的这两个MASViewAtrribute也就是要用在这里的。

MASCompositeConstraint可以看做一组MASViewConstraint的集合。它有个childConstraints用来保存它的子约束。在调用make.edges时返回的就是MASCompositeConstraint:

- (MASConstraint *)edges {
    return [self addConstraintWithAttributes:MASAttributeTop | MASAttributeLeft | MASAttributeRight | MASAttributeBottom];
}
- (MASConstraint *)addConstraintWithAttributes:(MASAttribute)attrs {
    __unused MASAttribute anyAttribute = (MASAttributeLeft | MASAttributeRight | MASAttributeTop | MASAttributeBottom | MASAttributeLeading
                                          | MASAttributeTrailing | MASAttributeWidth | MASAttributeHeight | MASAttributeCenterX
                                          | MASAttributeCenterY | MASAttributeBaseline
                                          | MASAttributeFirstBaseline | MASAttributeLastBaseline
#if TARGET_OS_IPHONE || TARGET_OS_TV
                                          | 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 (attrs & MASAttributeFirstBaseline) [attributes addObject:self.view.mas_firstBaseline];
    if (attrs & MASAttributeLastBaseline) [attributes addObject:self.view.mas_lastBaseline];
    
#if TARGET_OS_IPHONE || TARGET_OS_TV
    
    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;
}

这里主要做了以下5件事:

  • 1、将MASAtrribute转为MASViewAtrribute数组,比如把make.edges转为@[self.view.mas_left, self.view.mas_right, self.view.mas_top, self.view.mas_bottom];
  • 2、再把MASViewAtrribute数组转为MASViewConstraint数组
  • 3、创建最终的MASCompositeConstraint,MASViewConstraint数组就是它的childConstraints;
  • 4、保存上面创建的MASCompositeConstraint到MASConstraintMaker的constraints中,以备后面install。

到目前为止拿到的都是firstViewAtrribute,接下来的.equalTo等则是拿到secondViewAtrribute。

.equalTo(self.view)其实是链式语法,调用.equalTo得到一个block,然后调用这个block,相当于:

id(^block)(id attribute) = constraint.mas_equalTo;
if (block) {
    block(self.view);
}

之所以要返回block,是因为它要传递参数。源码:

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

来到MASViewConstraint的equalToWithRelation(attribute, NSLayoutRelationEqual),它同样是链式语法,这里的atrribute就是make.left.equalTo(self.view)中的self.view.mas_left。

- (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.equalTo(self.view),所以atrribute是View,直接走到self.secondViewAttribute = attribute:

- (void)setSecondViewAttribute:(id)secondViewAttribute {
    if ([secondViewAttribute isKindOfClass:NSValue.class]) {
        [self setLayoutConstantWithValue:secondViewAttribute];
    } else if ([secondViewAttribute isKindOfClass:MAS_VIEW.class]) {
        _secondViewAttribute = [[MASViewAttribute alloc] initWithView:secondViewAttribute layoutAttribute:self.firstViewAttribute.layoutAttribute];
    } else if ([secondViewAttribute isKindOfClass:MASViewAttribute.class]) {
        MASViewAttribute *attr = secondViewAttribute;
        if (attr.layoutAttribute == NSLayoutAttributeNotAnAttribute) {
            _secondViewAttribute = [[MASViewAttribute alloc] initWithView:attr.view item:attr.item layoutAttribute:self.firstViewAttribute.layoutAttribute];;
        } else {
            _secondViewAttribute = secondViewAttribute;
        }
    } else {
        NSAssert(NO, @"attempting to add unsupported attribute: %@", secondViewAttribute);
    }
}

因为是View,他会根据firstViewAtrribute来创建secondViewAtrribute

_secondViewAttribute = [[MASViewAttribute alloc] initWithView:secondViewAttribute layoutAttribute:self.firstViewAttribute.layoutAttribute];

到这secondViewAtrribute也拿到了。

设置偏差值:make.left.equalTo(self.view).offset(5):

- (MASConstraint *(^)(NSValue *value))valueOffset {
    return ^id(NSValue *offset) {
        NSAssert([offset isKindOfClass:NSValue.class], @"expected an NSValue offset, got: %@", offset);
        [self setLayoutConstantWithValue:offset];
        return self;
    };
}
- (void)setLayoutConstantWithValue:(NSValue *)value {
    if ([value isKindOfClass:NSNumber.class]) {
        self.offset = [(NSNumber *) value doubleValue];
    } else if (strcmp(value.objCType, @encode(CGPoint)) == 0) {
        CGPoint point;
        [value getValue:&point];
        self.centerOffset = point;
    } else if (strcmp(value.objCType, @encode(CGSize)) == 0) {
        CGSize size;
        [value getValue:&size];
        self.sizeOffset = size;
    } else if (strcmp(value.objCType, @encode(MASEdgeInsets)) == 0) {
        MASEdgeInsets insets;
        [value getValue:&insets];
        self.insets = insets;
    } else {
        NSAssert(NO, @"attempting to set layout constant with unsupported value: %@", value);
    }
}

添加约束完成后,开始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;
}

关键:[constraint install]

这段代码很长,主要完成的工作:

  • 1、通过firstViewAtrribute和secondViewAtrribute分别拿到对应的View和NSLayoutAttribute;
  • 2、如果是类似make.left.equalTo(@10)这样添加的约束,则secondViewAtrribute是空,默认会把firstViewAtrribute的view的superView和NSLayoutAttribute作为第二个约束;
  • 3、生成MASLayoutConstraint,其实就是继承自NSLayoutConstraint,多了一个参数mas_key,用来调试。当约束冲突时,可以设置View的mas_key来确定是哪个View发生了冲突;
  • 4、设置installedView,也就是用来addConstraint的View:如果secondViewAtrribute的View不为空,则installedView为firstView和secondView最相近的superView;否则如果firstViewAtrribute是sizeAtrribute,那installedView就是firstViewAtrribute的View本身;否则installView就是firstViewAtrribute的View的superView;
  • 5、调用原生API addConstraint,添加约束,并在View的mas_installedConstraints保存约束。

至此,Constraint设置完成。

找到最近的公共superView的代码:

- (instancetype)mas_closestCommonSuperview:(MAS_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;
}

mas_key在View的分类中也添加了:

@interface MAS_VIEW (MASAdditions)

@property (nonatomic, strong) id mas_key;

@end

实现的方法:

- (id)mas_key {
    return objc_getAssociatedObject(self, @selector(mas_key));
}

- (void)setMas_key:(id)key {
    objc_setAssociatedObject(self, @selector(mas_key), key, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

Masonry提供了一个宏来批量设置View的mas_key:

#define MASAttachKeys(...)                                                        \
    {                                                                             \
        NSDictionary *keyPairs = NSDictionaryOfVariableBindings(__VA_ARGS__);     \
        for (id key in keyPairs.allKeys) {                                        \
            id obj = keyPairs[key];                                               \
            NSAssert([obj respondsToSelector:@selector(setMas_key:)],             \
                     @"Cannot attach mas_key to %@", obj);                        \
            [obj setMas_key:key];                                                 \
        }                                                                         \
    }

参考

相关文章

  • Masonry 介绍 2018-01-29

    介绍 Masonry 源码:https://github.com/Masonry/Masonry Masonry是...

  • Masonry源码阅读

    Masonry源码阅读 AutoLayout是Apple在iOS6中新增的UI布局适配的方法,用来替代iOS6之前...

  • Masonry 源码阅读

    前言 Masonry 是 Objective-C 中用于 AutoLayout (see Understandin...

  • Masonry源码阅读

    在项目中使用Masonry让我们做屏幕适配变得简单,下面来通过源码了解下它。 一、点链式语法怎么实现的 1> .语...

  • 关于Masonry小记

    Masonry 源码:https://github.com/Masonry/Masonry Masonry是一个轻...

  • Masonry的用法

    Masonry 源码:https://github.com/Masonry/Masonry; 看一下Masonry...

  • Masonry学习报告

    Masonry 源码:https://github.com/Masonry/Masonry 如果是使用cocoa ...

  • Masonry使用归纳总结

    前言 Masonry 源码:https://github.com/Masonry/MasonryMasonry是一...

  • iOS Masonry 源码阅读

    Masonry 源码阅读 阅读源码是一种美妙的体验 这么强大的布局库,就不做解释了。因为系统的自动布局写起来很麻烦...

  • 第三方Masonry-实现纯代码自动布局(1)

    Masonry 源码:https://github.com/Masonry/Masonry 使用第三方先看看Mas...

网友评论

    本文标题:Masonry源码阅读

    本文链接:https://www.haomeiwen.com/subject/smeitqtx.html