一.前言
Masonry是非常有名的布局框架,今天我们就分析它的具体实现。通读了一边源码,写的非常的好,有很多值得我们学习的地方。
二.前期准备
Masonry
之所以非常让人着迷,得益于简单的api和链式编程,提高约束代码的可读性。
例如:
[greenView mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.mas_greaterThanOrEqualTo(200);
make.width.mas_lessThanOrEqualTo(340);
make.height.mas_equalTo(40);
make.left.mas_greaterThanOrEqualTo(view1);
make.top.mas_equalTo(120);
}];
要阅读Masonry
源码,首先要了解链式编程和组合模式,其中组合模式格外重要,因为Masonry
中使用了该设计模式。
三.整体结构图 & 各类提供的作用
-
1.
View+MASAdditions.h
为UIView
(如果是macOS,则为NSView)提供创建make
的方法,方便实现布局。并且提供当前view
的mas_xxx
属性// 获取两个view最近superview - (instancetype)mas_closestCommonSuperview:(MAS_VIEW *)view; - (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;
-
2.
MASConstraintMaker
提供单一的约束属性和组合约束属性,提供install
方法。// 单一约束属性,get方法,都是创建一个Constraint @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; // 组合约束属性,用于一次性创建多个Constraint @property (nonatomic, strong, readonly) MASConstraint *(^attributes)(MASAttribute attrs); @property (nonatomic, strong, readonly) MASConstraint *edges; @property (nonatomic, strong, readonly) MASConstraint *size; @property (nonatomic, strong, readonly) MASConstraint *center; // 将约束设置到view上 - (NSArray *)install;
-
3.
MASConstraint
特别注意:是一个抽象类,声明方法和属性,不实现具体的方法,都要子类实现!!!
并且声明了一下方法,使链式编程变为可能。// 声明get方法,用于创建MASConstraint,然后添加到make的数组中。通过Constraint的addConstraintWithLayoutAttribute来添加,具体逻辑由子类实现。 - (MASConstraint *)left; - (MASConstraint *)top; - (MASConstraint *)right; - (MASConstraint *)bottom; - (MASConstraint *)leading; - (MASConstraint *)trailing; - (MASConstraint *)width; - (MASConstraint *)height; - (MASConstraint *)centerX; - (MASConstraint *)centerY; - (MASConstraint *)baseline; // 为constraint添加属性或equal关系,然后返回self(MASConstraint) - (MASConstraint * (^)(MASLayoutPriority priority))priority; - (MASConstraint * (^)(void))priorityLow; - (MASConstraint * (^)(void))priorityMedium; - (MASConstraint * (^)(void))priorityHigh; - (MASConstraint * (^)(id attr))equalTo; // 安装约束到targetView上 - (void)install; - (void)uninstall;
4.
MASViewConstraint
继承于MASConstraint
,实现抽象类的方法。是对约束所需要所有属性的封装。
在组合模式中,属于叶子节点!!!不可以添加子节点。
Masonry
中如果想让叶子节点变为树枝节点,会将两个叶子节点放到一个数组中,然后创建一个树枝节点,然后用树枝节点替换叶子节点的位置。
// 布局公式:view1.attr1 = view2.attr2 * multiplier + constant
// view1的各个属性参数
@property (nonatomic, strong, readonly) MASViewAttribute *firstViewAttribute;
// view2的各个属性参数,secondViewAttribute可以是NSValue,UIView,MASViewAttribute,提供了较多的方式,后续会分析
@property (nonatomic, strong, readonly) MASViewAttribute *secondViewAttribute;
// 某个view已安装的约束
+ (NSArray *)installedConstraintsForView:(MAS_VIEW *)view;
// .m文件实现方法
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute;
// 非常重要的方法
- (void)install;
-
5.
MASCompositeConstraint
继承于MASConstraint
,实现抽象类的方法。内部维护一个数组,全部都是MASViewConstraint
对象。在组合模式中,是树枝节点,内部维护一个数组,可以添加叶子节点。
// .h 文件 - (id)initWithChildren:(NSArray *)children; // .m 文件中实现了抽象类的方法 基本上就是for循环变量MASViewConstraint,然后执行对应名称的方法而已。唯一特别的就是它可以add叶子节点!!! #pragma mark - MASConstraintDelegate 稍后分析 - (void)constraint:(MASConstraint *)constraint shouldBeReplacedWithConstraint:(MASConstraint *)replacementConstraint; - (MASConstraint *)constraint:(MASConstraint __unused *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute;
-
6.
MASConstraint+Private.h
声明了MASConstraintDelegate
代理方法。
由MASCompositeConstraint
和MASConstraintMaker
实现的。@protocol MASConstraintDelegate
// 当组合模式中,要将叶子节点替换为树枝节点,使用此方法 - (void)constraint:(MASConstraint *)constraint shouldBeReplacedWithConstraint:(MASConstraint *)replacementConstraint; // 将叶子节点添加到组合模式中 - (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute; @end
四.Masonsy 如何工作的
下面通过代码来看看Masonry
是如何运作的,代码如下:
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.bottom.mas_equalTo(20);
make.width.mas_equalTo(40)�;
}];
4.1创建make对象,执行block
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
// a.关闭转换AutoresizingMask至Constraints的属性
self.translatesAutoresizingMaskIntoConstraints = NO;
// b.创建make,用weak保存self(设置约束的view)
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
// c.执行block,make.top.left.bottom.mas_equalTo(20); && make.width.mas_equalTo(40)�;
block(constraintMaker);
// d.执行install操作
return [constraintMaker install];
}
4.2 block(constraintMaker);
这里执行make.top
语句,即:
// make.x 直接返回MASConstraint对象
- (MASConstraint *)top {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
}
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}
#pragma mark - MASConstraintDelegate 实现的代理方法
- (void)constraint:(MASConstraint *)constraint shouldBeReplacedWithConstraint:(MASConstraint *)replacementConstraint {
// 如果将viewConstraint替换为compositionConstaint,调用此方法
NSUInteger index = [self.constraints indexOfObject:constraint];
NSAssert(index != NSNotFound, @"Could not find constraint %@", constraint);
[self.constraints replaceObjectAtIndex:index withObject:replacementConstraint];
}
// 该方法是将树枝节点或者是叶子节点直接存放到make.constraints数组中
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
// 如果是make.x 的时候,constraint = nil,就是创建一个MASViewConstraint
MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
if ([constraint isKindOfClass:MASViewConstraint.class]) {
// 将MASViewConstraint 变成MASCompositeConstraint使用调用。
// make.top.left的时候会执行到这里 ,make.top.left.bottom也会执行到这里。实际上就是MASCompositeConstraint存放了三个MASViewConstraint。
// MASCompositeConstraint放置在make.constraints数组中。典型的组合模式
NSArray *children = @[constraint, newConstraint];
MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
compositeConstraint.delegate = self;
[self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
return compositeConstraint;
}
// 当前make.top会执行这里newConstraint = topConstraint,将topConstraint.delegate = self(make)
if (!constraint) {
newConstraint.delegate = self;
[self.constraints addObject:newConstraint];
}
return newConstraint;
}
这里执行make.top.left
语句,刚才通过make.top
返回了MASViewConstraint
对象,现在看看MASViewConstraint.left
的函数实现
// 重写父类方法
#pragma mark - attribute chaining
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation");
// 此时self = newConstraint,layoutAttribute = left,带着constraint:self参数调用了代理方法,此时self.delegate = make
return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
}
此时会调用make
的方法,返回CompositionConstraint
对象
// make.m 文件
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
// constraint = topConstraint
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;
}
执行make.top.left.bottom
代码
会执行MASCompositionConstraint.bottom
方法。
该代码的含义,就是创建了bottomViewConstraint
,然后被添加到了MASCompositionConstraint
中。
现在MASCompositionConstraint
有三个viewConstraint
了。分别是top,left.bottom
。
// MASCompositionConstraint.m 文件
#pragma mark - attribute chaining
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
[self constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
return self;
}
#pragma mark - MASConstraintDelegate
- (void)constraint:(MASConstraint *)constraint shouldBeReplacedWithConstraint:(MASConstraint *)replacementConstraint {
NSUInteger index = [self.childConstraints indexOfObject:constraint];
NSAssert(index != NSNotFound, @"Could not find constraint %@", constraint);
[self.childConstraints replaceObjectAtIndex:index withObject:replacementConstraint];
}
- (MASConstraint *)constraint:(MASConstraint __unused *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
id strongDelegate = self.delegate;
MASConstraint *newConstraint = [strongDelegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
newConstraint.delegate = self;
[self.childConstraints addObject:newConstraint];
return newConstraint;
}
// make.m 文件
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
// 此时constraint = compositionConstraint,所以不会执行到这里
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;
}
// 此时constraint = compositionConstraint,所以不会执行到这里
if (!constraint) {
newConstraint.delegate = self;
[self.constraints addObject:newConstraint];
}
// 执行这里,就是创建一个viewConstraint,然后返回viewConstraint
return newConstraint;
}
make.top.right.bottom.mas_equalTo(20)
的逻辑
make.top.right.bottom
前面返回了viewConstraint
对象,然后执行mas_equal(20)
// 抽象类MASConstraint.m中
#pragma mark - NSLayoutRelation proxies
- (MASConstraint * (^)(id))equalTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
};
}
// 具体实现类中 MASViewConstraint.m文件
// 执行mas_equalTo,或者mas_greaterThanTo等方法,可以传递多种类型对象,例如某个view.mas_x,也可以是一个数组。
// 如果是数组,就会进入`[attribute isKindOfClass:NSArray.class]`内部,
// 然后将多个sectionAttribution创建成compositionConstraint存储起来。equalToWithRelation只是存储或替换数据,
// 不会立刻执行,要通过install来执行。
- (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.width.mas_equalTo(40)�;
,逻辑和上边的一致。
执行完毕后,要知道make.constraints
中有1个compositionContraint
,一个viewConstraint
.compositionContraint
放置了三个viewConstraint
。
4.4 [make install]
此时block函数全部执行完毕,现在接着执行[make install]
函数
// make.m 文件
- (NSArray *)install {
// 如果是re_make方法,就会执行这个
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;
// 具体是约束执行install方法
[constraint install];
}
[self.constraints removeAllObjects];
return constraints;
}
进入constraint
文件查看具体install
方法
- (void)install {
if (self.hasBeenInstalled) {
return;
}
if ([self supportsActiveProperty] && self.layoutConstraint) {
self.layoutConstraint.active = YES;
[self.firstViewAttribute.view.mas_installedConstraints addObject:self];
return;
}
// view1.attr1 = view2.attr2 * multiplier + constant
MAS_VIEW *firstLayoutItem = self.firstViewAttribute.item;
NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute;
MAS_VIEW *secondLayoutItem = self.secondViewAttribute.item;
NSLayoutAttribute secondLayoutAttribute = self.secondViewAttribute.layoutAttribute;
// 对齐两个view必须有 secondViewAttribute
// 如果没有secondViewAttribute,我们假设是superview
// eg make.left.equalTo(@10)
if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) {
secondLayoutItem = self.firstViewAttribute.view.superview;
secondLayoutAttribute = firstLayoutAttribute;
}
// 仅仅是创建layoutConstraint,还未应用
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;
// 确定该约束应该作用到那个view上
// 如果有secondViewAttribute.view 有可能调用的时候是make.left.equalTo(�otherView)
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;
} else if (self.firstViewAttribute.isSizeAttribute) {
// 如果是make.width.mas_equalTo(4);那么直接将约束安装到firstViewAttribute.view上
self.installedView = self.firstViewAttribute.view;
} else {
self.installedView = self.firstViewAttribute.view.superview;
}
MASLayoutConstraint *existingConstraint = nil;
// 仅仅是mas_update某个属性
if (self.updateExisting) {
existingConstraint = [self layoutConstraintSimilarTo:layoutConstraint];
}
if (existingConstraint) {
// just update the constant
existingConstraint.constant = layoutConstraint.constant;
self.layoutConstraint = existingConstraint;
} else {
// 过去view上没有该约束,直接安装即可,并且将该约束添加到firstLayoutItem的已安装约束数组中,将来如果使用了mas_remake好去移除,mas_update好去寻找约束然后更新
[self.installedView addConstraint:layoutConstraint];
self.layoutConstraint = layoutConstraint;
[firstLayoutItem.mas_installedConstraints addObject:self];
}
}
至此设置view
约束的代码就全部执行完毕。
五.masonry提供的其他功能
1.make提供了某个属性的设置,例如,left,right,top,bottom.也可以使用组合的方式来设置约束.
@property (nonatomic, strong, readonly) MASConstraint *(^attributes)(MASAttribute attrs);
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.attributes(MASAttributeTop | MASAttributeLeft).mas_equalTo(133);
make.attributes(MASAttributeBottom | MASAttributeRight).mas_equalTo(-133);
}];
2.查看当前view有哪些约束
+ (NSArray *)installedConstraintsForView:(MAS_VIEW *)view;
六.设计亮点
6.1 自定义添加了约束的优先级
在已有的优先级基础上,在添加新的优先级。
为了防止出错,直接使用过去的赋值。
typedef UILayoutPriority MASLayoutPriority;
static const MASLayoutPriority MASLayoutPriorityRequired = UILayoutPriorityRequired;
static const MASLayoutPriority MASLayoutPriorityDefaultHigh = UILayoutPriorityDefaultHigh;
static const MASLayoutPriority MASLayoutPriorityDefaultMedium = 500;
static const MASLayoutPriority MASLayoutPriorityDefaultLow = UILayoutPriorityDefaultLow;
static const MASLayoutPriority MASLayoutPriorityFittingSizeLevel = UILayoutPriorityFittingSizeLevel;
6.2.firstViewAttribute && secondViewAttribute 相对灵活
原生的布局的计算公式为,所以需要两个view。但是masonry假设只给定一个view(即firstViewAttribute)也行,另一个view根据某些给定的参数来确定。如果有了第二个view(secondViewAttribute)便可以直接布局了。
但是这里的secondViewAttribute不一定是view,可以是其他的,masonry会根据实际情况来确定,然后布局的。
secondViewAttribute 可以是NSValue
,MAS_VIEW
,MASViewAttribute
,非常的灵活。
6.3.用于判断view所安装的约束,采用了option来修饰MASAttribute属性
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,
#if TARGET_OS_IPHONE || TARGET_OS_TV
MASAttributeLeftMargin = 1 << NSLayoutAttributeLeftMargin,
MASAttributeRightMargin = 1 << NSLayoutAttributeRightMargin,
MASAttributeTopMargin = 1 << NSLayoutAttributeTopMargin,
MASAttributeBottomMargin = 1 << NSLayoutAttributeBottomMargin,
MASAttributeLeadingMargin = 1 << NSLayoutAttributeLeadingMargin,
MASAttributeTrailingMargin = 1 << NSLayoutAttributeTrailingMargin,
MASAttributeCenterXWithinMargins = 1 << NSLayoutAttributeCenterXWithinMargins,
MASAttributeCenterYWithinMargins = 1 << NSLayoutAttributeCenterYWithinMargins,
#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
6.4.通过设置NSValue,自动判断类型&为属性设置数据
#pragma mark - NSLayoutConstraint constant setter
- (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);
}
}