iOS 开发中的布局方式,整体而言通过了三个时代。混沌初开之时,世间只有3.5英寸(iPhone 四、iPhone 4S),那个时候屏幕适配对于大多数 iOS 开发者来讲并非什么难题,用 frame 就能精确高效的定位。这以后,苹果发布了4英寸机型(iPhone 五、iPhone 5C、iPhone 5S),与此同时苹果也推出了 AutoresizingMask,用来协调子视图与父视图之间的关系。再以后,各类各样的 iPhone 和 iPad 纷纷面世,不只仅是屏幕尺寸方面的差别,更有异形屏(iPhone X)。在此期间,苹果提出了 AutoLayout 技术,供开发者进行屏幕适配。git
使用 AutoLayout
的方法也有两种——经过 Interface Builder
或者纯代码。前者一直是苹果官方文档里所鼓励的,缘由是苹果从最初到如今,对于 iOS 应用的想法都是小而美的,在他们的认知里,一个 APP 应该提供尽量小的功能集,这也是为为什么苹果迄今为止官方推荐的架构仍然是 MVC,官方推荐的开发方式还是以 StoryBoard(Size Classes)
。可是在一些项目较大的公司,StoryBoard
的某些特性(致使应用包过大,减缓启动速度,合并代码困难)又是不能为人所容忍的,便有了纯代码来实现 View
层的一群开发者(好比我)。github
若是你曾经用代码来实现 AutoLayout
,你会发现苹果提供的 API
的繁琐程度使人发指,这也是 Masonry
这类框架被发明的缘由。Masonry
是一个轻量级的布局框架,它使用更好的语法来封装 AutoLayout
。Masonry
有本身的布局 DSL
,它提供了一种链式的方式来描述你的 NSLayoutConstraints
,从而获得更简洁和可读的布局代码。swift
接下来,咱们就从 Masonry
中 README
提供的代码着手,看一看 Masonry
是如何帮助咱们简化繁琐的 AutoLayout
代码的。api
举一个简单的例子,你想要一个视图填充它的父视图,可是在每一边间隔10个点。数组
UIView *superview = self.view;
UIView *view1 = [[UIView alloc] init];
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler
make.left.equalTo(superview.mas_left).with.offset(padding.left);
make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];
复制代码
咱们想要实现约束的效果,是经过 mas_makeConstraints:
这个方法来实现的,这个方法能够在任意 UIView
类及其子类上调用,说明其是一个分类方法,这也是这个方法加了 mas_
前缀的缘由。该方法声明在 UIView+MASAdditions.h
文件中,先来看一下这个方法的完整声明:bash
- (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
复制代码
这个方法传递的参数是一个参数为 MASConstraintMaker
类型的无返回值的 block
,而该方法的返回值则是一个数组。方法声明中咱们看到了一个叫作 NS_NOESCAPE
的宏,NS_NOESCAPE
用于修饰方法中的 block
类型参数,做用是告诉编译器,该 block
在方法返回以前就会执行完毕,而不是被保存起来在以后的某个时候再执行。编译器被告知后,就会相应的进行一些优化。更详细的内容请参考 Add @noescape to public library API架构
接下来是方法的实现:app
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
block(constraintMaker);
return [constraintMaker install];
}
复制代码
默认状况下,view
上的 autoresizing mask
会产生约束条件,以彻底肯定视图的位置。这容许 AutoLayout
系统跟踪其布局被手动控制的 view
的 frame
(例如经过 setFrame:
)。当你选择经过添加本身的约束来使用 AutoLayout
来定位视图时,必须 self.translatesAutoresizingMaskIntoConstraints = NO;
。IB 会自动为你作这件事。框架
在这以后,创造了一个 MASConstraintMaker
类型的对象 constraintMaker
,MASConstraintMaker
的初始化方法为:ide
- (id)initWithView:(MAS_VIEW *)view;
复制代码
能够看到,传入的 view
参数是由一个叫作 MAS_VIEW
的宏来做为参数声明的,MAS_VIEW
的定义是:
#if TARGET_OS_IPHONE || TARGET_OS_TV
#import <UIKit/UIKit.h>
#define MAS_VIEW UIView
...
#elif TARGET_OS_MAC
#import <AppKit/AppKit.h>
#define MAS_VIEW NSView
...
#endif
复制代码
由于 Masonry
是一个跨平台的框架,因而经过预编译宏来让在不一样的平台上,MAS_VIEW
表明的意义不一样。接下来看初始化方法的实现:
- (id)initWithView:(MAS_VIEW *)view {
self = [super init];
if (!self) return nil;
self.view = view;
self.constraints = NSMutableArray.new;
return self;
}
复制代码
在初始化方法中,将传入的 view
经过一个弱指针(@property (nonatomic, weak) MAS_VIEW *view;
)保留在了 constraintMaker
中,同时初始化了一个名为 constraints
的 NSMutableArray
,用来保存约束。
接着,constraintMaker
经过 block(constraintMaker);
传递给了咱们,而咱们对它作了什么呢?
make.top.equalTo(superview.mas_top).with.offset(padding.top);
make.left.equalTo(superview.mas_left).with.offset(padding.left);
make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
make.right.equalTo(superview.mas_right).with.offset(-padding.right);
复制代码
咱们将对视图约束的描述,以一种迥异于建立 NSLayoutConstraints
对象的方式描述了咱们对视图的约束,咱们以其中的一句做为例子来看看 Masonry
是如何实现链式 DSL
的。
make.top.equalTo(superview.mas_top).with.offset(padding.top);
复制代码
make
是 MASConstraintMaker
类型的对象,这个类型封装了一系列只读 MASConstraint
属性,top
就是其中之一,声明和实现以下:
@property (nonatomic, strong, readonly) MASConstraint *top;
复制代码
- (MASConstraint *)top {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
}
复制代码
addConstraintWithLayoutAttribute:
方法的实现为:
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}
间接调用了 `constraint:addConstraintWithLayoutAttribute:` 方法:
- (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;
}
复制代码
让咱们一句一句来看:
MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
复制代码
首先,会初始化一个 MASViewAttribute
类型的对象(viewAttribute
),该类型的初始化方法是:
- (id)initWithView:(MAS_VIEW *)view layoutAttribute:(NSLayoutAttribute)layoutAttribute;
复制代码
实现为:
- (id)initWithView:(MAS_VIEW *)view layoutAttribute:(NSLayoutAttribute)layoutAttribute {
self = [self initWithView:view item:view layoutAttribute:layoutAttribute];
return self;
}
- (id)initWithView:(MAS_VIEW *)view item:(id)item layoutAttribute:(NSLayoutAttribute)layoutAttribute {
self = [super init];
if (!self) return nil;
_view = view;
_item = item;
_layoutAttribute = layoutAttribute;
return self;
}
复制代码
MASViewAttribute
是一个模型类,用于存储视图和视图对应的 NSLayoutAttribute
。
接下来会初始化一个 MASViewConstraint
类型的对象(newConstraint
):
MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
复制代码
MASViewConstraint
的初始化方法是:
- (id)initWithFirstViewAttribute:(MASViewAttribute *)firstViewAttribute;
复制代码
- (id)initWithFirstViewAttribute:(MASViewAttribute *)firstViewAttribute {
self = [super init];
if (!self) return nil;
_firstViewAttribute = firstViewAttribute;
self.layoutPriority = MASLayoutPriorityRequired;
self.layoutMultiplier = 1;
return self;
}
复制代码
MASViewConstraint
也是一个模型类,会经过刚才初始化的 viewAttribute
做为初始化参数,并存储在 _firstViewAttribute
的实例变量中。
接下来,因为 constraint
参数为 nil, 因此直接走到这里:
if (!constraint) {
newConstraint.delegate = self;
[self.constraints addObject:newConstraint];
}
复制代码
将 newConstraint
对象的代理设为 self
(make
),同时将其放置到 constraints
数组中。
简而言之就是,make
中的 top
方法会初始化一个 MASViewAttribute
类型的对象 viewAttribute
,并经过该对象初始化一个 MASViewConstraint
类型的对象 newConstraint
,让 make
做为 newConstraint
对象的 delegate
,并存储在 make
的 constraints
属性中。接下来,return newConstraint;
return newConstraint;
看似简简单单的一句代码,倒是 Masonry
这套链式 DSL
可以生效的核心。
newConstraint
是 MASViewConstraint
类型的对象,而 MASViewConstraint
又是 MASConstraint
的子类。在 MASConstraint
中,声明了一系列的方法,例如:
/**
* Sets the constraint relation to NSLayoutRelationEqual
* returns a block which accepts one of the following:
* MASViewAttribute, UIView, NSValue, NSArray
* see readme for more details.
*/
- (MASConstraint * (^)(id attr))equalTo;
复制代码
这个方法的返回值是一个接受 id
类型的 attr
参数,返回 MASConstraint
类型的 block
,这样写的意义是,既能够作到传递参数,返回 self
,同时又确保了能够实现链式调用的 DSL
。
该方法的实现为:
- (MASConstraint * (^)(id))equalTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
};
}
复制代码
在方法的实现中,调用了一个名为 equalToWithRelation
的内部方法,方法的实现为:
- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation { MASMethodNotImplemented(); }
复制代码
MASConstraint
实际上是一个基类,其中 equalToWithRelation
方法自己的实现里只有一个名为 MASMethodNotImplemented();
的宏,这个宏的实现仅仅是抛出一个异常:
#define MASMethodNotImplemented() \
@throw [NSException exceptionWithName:NSInternalInconsistencyException \
reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)] \
userInfo:nil]
复制代码
而咱们在 makeConstraints
的时候,实际调用的是 MASViewConstraint
这个 MASConstraint
子类中的实现:
- (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;
}
};
}
复制代码
该方法接收两个参数,一个表示了对应的属性(mas_top
),一个表示了相等关系(NSLayoutRelationEqual
),进入方法后会先对咱们传入的属性作一个类型判断,咱们传入的是一个单个的属性,因此会落入 else
分支,一样是依赖断言作了一系列保护性的判断,并将相等关系和视图属性分别赋值给 layoutRelation
和 secondViewAttribute
属性,并返回 self
。
返回 self
,看似简简单单的一个操做,倒是 Masonry
可以实现链式 DSL 最重要的基石。(重要的事情说 n 遍)
再来看看咱们传入的 mas_top
,这是一个声明在 View+MASAdditions.h
当中的只读属性:
@property (nonatomic, strong, readonly) MASViewAttribute *mas_top;
复制代码
简单生成并返回了一个 MASViewAttribute
属性的对象:
- (MASViewAttribute *)mas_top {
return [[MASViewAttribute alloc] initWithView:self layoutAttribute:NSLayoutAttributeTop];
}
复制代码
实现为:
- (id)initWithView:(MAS_VIEW *)view layoutAttribute:(NSLayoutAttribute)layoutAttribute;
复制代码
- (id)initWithView:(MAS_VIEW *)view layoutAttribute:(NSLayoutAttribute)layoutAttribute {
self = [self initWithView:view item:view layoutAttribute:layoutAttribute];
return self;
}
- (id)initWithView:(MAS_VIEW *)view item:(id)item layoutAttribute:(NSLayoutAttribute)layoutAttribute {
self = [super init];
if (!self) return nil;
_view = view;
_item = item;
_layoutAttribute = layoutAttribute;
return self;
}
复制代码
咱们继续往下看的时候,.with
一样是实如今 MASViewConstraint
中的一个方法:
- (MASConstraint *)with {
return self;
}
复制代码
一样是简简单单的返回了 self
,并且是仅仅作了这一件事情。因此这个方法仅仅是一个语法……好吧都不能叫作语法糖,就叫语气助词吧,是为了让咱们写出的 DSL
可读性更高而存在的。固然了,你要是以为多余,也是能够不写的。
接下来是 offset
:
- (MASConstraint * (^)(CGFloat offset))offset;
复制代码
就是简简单单的一个赋值操做罢了,写成这么复杂的缘由就是实现能够传递参数的链式调用。
- (MASConstraint * (^)(CGFloat))offset {
return ^id(CGFloat offset){
self.offset = offset;
return self;
};
}
复制代码
以上就是 make.top.equalTo(superview.mas_top).with.offset(padding.top);
在 Masonry
内部到底作了些什么,其他几句也是相似的,总而言之就是:
对
MASConstraintMaker
类型的对象属性(MASConstraint
的子类)top
(或其余任何你想要去布局的属性),进行了初始化,并经过返回MASViewConstraint
类型的对象(newConstraint
),不断地调用newConstraint
的对象方法,对newConstraint
中的属性作了赋值,以确保其能够完整的表达一个NSLayoutConstraints
。
在配置好咱们想要的约束后,咱们还须要对视图施加约束:
return [constraintMaker install];
复制代码
先来看一下 install
方法:
- (NSArray *)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;
}
复制代码
首先,会对 constraints
属性作一份 copy
,以后遍历 constraints
中的全部 MASConstraint
及其子类型的属性,并调用其 install
方法:
- (void)install;
复制代码
实现为:
- (void)install {
if (self.hasBeenInstalled) {
return;
}
if ([self supportsActiveProperty] && self.layoutConstraint) {
self.layoutConstraint.active = YES;
[self.firstViewAttribute.view.mas_installedConstraints addObject:self];
return;
}
MAS_VIEW *firstLayoutItem = self.firstViewAttribute.item;
NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute;
MAS_VIEW *secondLayoutItem = self.secondViewAttribute.item;
NSLayoutAttribute secondLayoutAttribute = self.secondViewAttribute.layoutAttribute;
// alignment attributes must have a secondViewAttribute
// therefore we assume that is refering to superview
// eg make.left.equalTo(@10)
if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) {
secondLayoutItem = self.firstViewAttribute.view.superview;
secondLayoutAttribute = firstLayoutAttribute;
}
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;
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) {
self.installedView = self.firstViewAttribute.view;
} else {
self.installedView = self.firstViewAttribute.view.superview;
}
MASLayoutConstraint *existingConstraint = nil;
if (self.updateExisting) {
existingConstraint = [self layoutConstraintSimilarTo:layoutConstraint];
}
if (existingConstraint) {
// just update the constant
existingConstraint.constant = layoutConstraint.constant;
self.layoutConstraint = existingConstraint;
} else {
[self.installedView addConstraint:layoutConstraint];
self.layoutConstraint = layoutConstraint;
[firstLayoutItem.mas_installedConstraints addObject:self];
}
}
复制代码
接下来,咱们来一段一段分析:
首先,在 install
以前,会作一次判断,看是否已经被 install
过:
if (self.hasBeenInstalled) {
return;
}
复制代码
判断依据则是:
- (BOOL)hasBeenInstalled {
return (self.layoutConstraint != nil) && [self isActive];
}
复制代码
layoutConstraint
是一个 MASLayoutConstraint
类型的 weak
属性,MASLayoutConstraint
是 NSLayoutConstraint
的子类,只是为了增长一个属性(mas_key
)。
@property (nonatomic, weak) MASLayoutConstraint *layoutConstraint;
复制代码
isActive
则是经过判断 layoutConstraint
是否响应 isActive
以及 isActive
方法返回的结果,来综合决定。
- (BOOL)isActive {
BOOL active = YES;
if ([self supportsActiveProperty]) {
active = [self.layoutConstraint isActive];
}
return active;
}
复制代码
- (BOOL)supportsActiveProperty {
return [self.layoutConstraint respondsToSelector:@selector(isActive)];
}
复制代码
if ([self supportsActiveProperty] && self.layoutConstraint) {
self.layoutConstraint.active = YES;
[self.firstViewAttribute.view.mas_installedConstraints addObject:self];
return;
}
复制代码
若是 supportsActiveProperty
且 layoutConstraint
不为空,则将 layoutConstraint.active
设为 YES
,并将其添加到 firstViewAttribute.view
的 mas_installedConstraints
只读属性中去。
@property (nonatomic, readonly) NSMutableSet *mas_installedConstraints;
复制代码
mas_installedConstraints
是一个可变集合,是经过分类给 MAS_View 类添加的关联对象,用来保存已经 active
的对象。
static char kInstalledConstraintsKey;
- (NSMutableSet *)mas_installedConstraints {
NSMutableSet *constraints = objc_getAssociatedObject(self, &kInstalledConstraintsKey);
if (!constraints) {
constraints = [NSMutableSet set];
objc_setAssociatedObject(self, &kInstalledConstraintsKey, constraints, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return constraints;
}
复制代码
MAS_VIEW *firstLayoutItem = self.firstViewAttribute.item;
NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute;
MAS_VIEW *secondLayoutItem = self.secondViewAttribute.item;
NSLayoutAttribute secondLayoutAttribute = self.secondViewAttribute.layoutAttribute;
// alignment attributes must have a secondViewAttribute
// therefore we assume that is refering to superview
// eg make.left.equalTo(@10)
if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) {
secondLayoutItem = self.firstViewAttribute.view.superview;
secondLayoutAttribute = firstLayoutAttribute;
}
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;
复制代码
其实就是把咱们再 block
中经过诸如 make.top.equalTo(superview.mas_top).with.offset(padding.top);
这样的语句配置的属性做为 MASLayoutConstraint
初始化方法的参数,生成一个约束。惟一须要注意的是,若是你设置的是一个 isSizeAttribute
,而且 secondViewAttribute
为 nil
,会作一些额外的参数调整。
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) {
self.installedView = self.firstViewAttribute.view;
} else {
self.installedView = self.firstViewAttribute.view.superview;
}
复制代码
若是 secondViewAttribute.view
中的存在,就经过 mas_closestCommonSuperview
方法寻找最近的公共子视图:
/**
* Finds the closest common superview between this view and another view
*
* @param view other view
*
* @return returns nil if common superview could not be found
*/
- (instancetype)mas_closestCommonSuperview:(MAS_VIEW *)view;
复制代码
- (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;
}
复制代码
递归求解。
若是设置的是一个尺寸约束(firstViewAttribute.isSizeAttribute
),则施加在 firstViewAttribute.view
上。不然施加在 firstViewAttribute.view.superView
上。
MASLayoutConstraint *existingConstraint = nil;
if (self.updateExisting) {
existingConstraint = [self layoutConstraintSimilarTo:layoutConstraint];
}
if (existingConstraint) {
// just update the constant
existingConstraint.constant = layoutConstraint.constant;
self.layoutConstraint = existingConstraint;
} else {
[self.installedView addConstraint:layoutConstraint];
self.layoutConstraint = layoutConstraint;
[firstLayoutItem.mas_installedConstraints addObject:self];
}
复制代码
对视图施加约束,并将 layoutConstraint
存储在 self.layoutConstraint
属性中,同时把 self
存储到以前提到过的叫作 mas_installedConstraints
的关联对象中。至此,文章开始提到的例子业已完成。
原文地址:Masonry 源码解读(上)
若是以为我写的还不错,请关注个人微博@小橘爷,最新文章即时推送~