前言
CSDN地址:http://blog.csdn.net/game3108/article/details/52473932
本文的中文注釋代碼demo更新在我的github上。
AutoLayout是Apple在iOS6中新增的UI布局適配的方法,用來替代iOS6之前的AutoResizeing。AutoLayout對應的代碼約束就是NSLayoutConstraint。NSLayoutConstraint的API雖說時分簡單,但約束的代碼量較大,所以出現了很多對NSLayoutConstraint的封裝,今天講的就是其中最為有名的Masonry框架。
Masonry框架簡化了約束NSLayoutConstraint的寫法,在各種APP中都有很多的使用。Masonry是基于Objective-C語言的框架,Swift項目可以參考Snapkit框架。
本文會基于Masonry v1.0.1版本,同時借鑒了網上很多解析文章,對源代碼進行解析,進行學習。
16.9.14更新:
MASViewConstraint的equalToWithRelation
添加array缺少//viewConstraint.layoutRelation = relation;
的pull request已經被接受,該問題fixed。
約束
NSLayoutConstraint約束是基于以下公式:
item1.attribute1 = multiplier × item2.attribute2 + constant
比如button1的左側距離button2有10的約束會這么寫:
button1.left = button2.right + 10;
事實上添加NSLayoutConstraint約束有三種方式:
- 1.storyboard/xib添加
- 2.VFL語言添加
- 3.NSLayoutConstraint純代碼添加
1.storyboard/xib添加
storyboard/xib添加NSLayoutConstraint的方式主要是右下角的約束設置:
分別是:
- Align
- Pin
- Resolve Auto Layout Issues
相應的圖這里就不再貼了,有興趣的可以自己去試一下
2.VFL語言添加
VFL(Visual Format Language)是蘋果公司為了簡化autolayout的編碼而推出的抽象語言。
VFL調用以下方法:
+ (NSArray<__kindof NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format
options:(NSLayoutFormatOptions)opts
metrics:(nullable NSDictionary<NSString *,id> *)metrics
views:(NSDictionary<NSString *, id> *)views;
其中的format
就是vfl語句。
vfl的語句也較為復雜,這里不詳細介紹了,具體可以參考蘋果文檔Visual Format Language。
3.NSLayoutConstraint純代碼添加
我們舉一個例子:
view的上部距離距離superview有10的距離:
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:view //view
attribute:NSLayoutAttributeTop //view.top
relatedBy:NSLayoutRelationEqual //view.top =
toItem:superView //view.top = superView
attribute:NSLayoutAttributeTop //view.top = superView.top
multiplier:1.0 //view.top = 1.0 * superView.top
constant:10.0]; //view.top = 1.0 * superView.top + 10
[view addConstraint:constraint];
可以看到約束代碼算是列出了一個約束公式,也達到了約束的目的。
但這些代碼,其實只寫了一個top的約束,如果有其它約束代碼,需要同樣寫類似的代碼出來,所以直接用NSLayoutConstraint純代碼添加還是比較麻煩的一件事。
4.約束的限制
(1)對于兩個同層級 view 之間的約束關系,添加到它們的父 view 上
(2)對于兩個不同層級 view 之間的約束關系,添加到他們最近的共同父 view 上
(3)對于有層次關系的兩個 view 之間的約束關系,添加到層次較高的父 view 上
(4)對于比如長寬之類的,只作用在該 view 自己身上的話,添加到該 view 自己上
Masonry的使用
1.Masonry的例子
Masonry的代碼封裝了NSLayoutConstraint純代碼,簡潔了許多,和NSLayoutConstraint純代碼舉同一個例子:
view的上部距離距離superview有10的距離
[view mas_makeConstraints:^(MASConstraintMaker *make){
make.top.equalTo(10); //默認是父view 三者等價 view.top = 1.0 * superView.top + 10
make.top.mas_equalTo(superView.mas_top).with.multipliedBy(1.0).mas_offset(10);
make.top.equalTo(superView.top).offset(10);
}];
相對于NSLayoutConstraint純代碼的添加約束,Masonry使用了block外加鏈式語法,使得調用簡潔和方便了許多。
Masonry源代碼
1.整體結構
Masonry的目錄結構如下:
文件比較多,借用iOS開發之Masonry框架源碼深度解析的類圖:
根據類圖,文件目錄主要分為以下幾類:
1.頭文件和輔助文件
Masonry.h:頭文件
MASUtilities.h:定義宏MASBoxValue,轉換類型
UIView+MASShorthandAdditions.h:定義UIView調用的簡化參數和方法
NSArray+MASShorthandAdditions.h:定義NSArray調用的簡化參數和方法
NSLayoutConstraint+MASDebugAdditions.h/m:DEBUG相關信息轉換2.約束使用接口
UIView+MASAdditions.h/m:UIView的約束接口
UIViewController+MASAdditions.h/m:UIViewController的約束借口
NSArray+MASAdditions.h/m:遍歷NSArray中的UIView的約束接口3.約束建造者builder模式
MASConstraintMaker.h/m:約束構造使用的建造者builder4.約束內部結構和實現
MASLayoutConstraint.h/m:NSLayoutConstraint多了一層key的封裝
MASAttribute.h:NSLayoutAttribute的一層封裝
MASConstraint.h/m:定義鏈式結構體的抽象父類
MASConstraint+Private.h:定義MASConstraint的接口和delegate代理
MASViewConstraint.h/m:繼承MASConstraint,表示view的約束結構體
MASCompositeConstraint.h/m:繼承MASConstraint,表示系列view組合的約束結構體
看了源代碼會發現,Masonry的代碼流程簡單來講就是:提供給用戶一個建造者MASConstraintMaker,讓用戶根據mansory提供的語法,添加約束結構體MASConstraint。最后Masonry解析約束結構體MASConstraint,將真正的約束關系NSLayoutConstraint添加到相應的view上。
2.源代碼探究
我們根據一個實際調用代碼來講Masonry的源代碼。代碼如下:
[view mas_makeConstraints:^(MASConstraintMaker *make){
make.top.mas_equalTo(superView.mas_top).with.mas_offset(10);
}];
這邊其實主要分兩塊
- MASConstraintMaker的鏈式調用
- view的約束函數調用
在講這兩塊前,首先講一下底層的數據結構MASConstraint相關的結構
(1)MASConstraint相關結構
MASConstraint是定義約束的抽象類(雖然OC沒有抽象類的說法)
定義大概如下:
@interface MASConstraint : NSObject
...
- (MASConstraint * (^)(CGFloat offset))offset;
...
- (MASConstraint *)left;
- (MASConstraint *)top;
...
- (void)install;
- (void)uninstall;
這邊只選取了部分方法和參數,來看一下相應的實現
- (MASConstraint * (^)(CGFloat))offset {
return ^id(CGFloat offset){
self.offset = offset;
return self;
};
}
//讓子類去重寫該方法,父類拋出異常達到抽象類的效果
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute __unused)layoutAttribute {
MASMethodNotImplemented();
}
- (MASConstraint *)left {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}
- (void)install { MASMethodNotImplemented(); }
- (void)uninstall { MASMethodNotImplemented(); }
可以看到這邊的實現達到了以下目的:
- 1.鏈式調用的實現
為了實現鏈式調用,并且可以用小括號"()"加入參數,而不是中括號"[]",這邊采用了返回同樣類型的block的方式 - 2.讓子類實現方法,父類作為抽象類存在
雖然oc沒有抽象類的定義,但在MASConstraint定義了一系列方法,讓子類進行重寫,而父類則用宏MASMethodNotImplemented()
拋出異常
而MASConstraint的子類則分別是MASViewConstraint與MASCompositeConstraint
MASViewConstraint
MASViewConstraint定義著一個單獨的約束關系
MASViewConstraint.h中定義如下:
@interface MASViewConstraint : MASConstraint <NSCopying>
//代表第一個item和相應的attribute
@property (nonatomic, strong, readonly) MASViewAttribute *firstViewAttribute;
//代表第二個item和相應的attribute
@property (nonatomic, strong, readonly) MASViewAttribute *secondViewAttribute;
...
這里又多了一個類MASViewAttribute
@interface MASViewAttribute : NSObject
//調用view
@property (nonatomic, weak, readonly) MAS_VIEW *view;
//交互對象
@property (nonatomic, weak, readonly) id item;
//約束NSLayoutAttribute類型
@property (nonatomic, assign, readonly) NSLayoutAttribute layoutAttribute;
MASViewAttribute比較好理解,其實就是一個調用view,交互的item和NSLayoutAttribute的結構體
而為什么在MASViewConstraint中會有兩個MASViewAttribute?
我們再看一下一個標準的約束調用代碼:
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:view //view
attribute:NSLayoutAttributeTop //view.top
relatedBy:NSLayoutRelationEqual //view.top =
toItem:superView //view.top = superView
attribute:NSLayoutAttributeTop //view.top = superView.top
multiplier:1.0 //view.top = 1.0 * superView.top
constant:10.0]; //view.top = 1.0 * superView.top + 10
可以看到,一個約束必須要有兩組<item,NSLayoutAttribute>的結構體,還需要NSLayoutRelation\multiplier\constant。而這兩個MASViewAttribute就是對應兩組兩組<item,NSLayoutAttribute>的結構體。
MASCompositeConstraint
MASCompositeConstraint代表一組MASViewConstraint約束
比如源代碼中的make.top
會轉化為MASViewConstraint約束,而make.top.bottom.left.right.xxxxx
這種寫法會定義多種MASViewConstraint約束,而為了存儲這種寫法,則創建了MASCompositeConstraint。
MASCompositeConstraint.h定義并沒有暴露太多細節,細節都在.m的匿名extension中:
@interface MASCompositeConstraint () <MASConstraintDelegate>
@property (nonatomic, strong) id mas_key;
//存儲內部結構體,都是MASViewConstraint
@property (nonatomic, strong) NSMutableArray *childConstraints;
@end
childConstraints中存儲的是每一條MASViewConstraint約束
(2)MASConstraintMaker調用
MASConstraintMaker匿名Extension中的定義
@interface MASConstraintMaker () <MASConstraintDelegate>
@property (nonatomic, weak) MAS_VIEW *view;
//存儲MASConstraint
@property (nonatomic, strong) NSMutableArray *constraints;
@end
可以看到在有一個MASConstraintMaker中專門有一個@property (nonatomic, strong) NSMutableArray *constraints;
去存儲所有的MASConstraint對象。就是你有好幾行的make.xxx都會存儲在這里。
MASConstraintMaker.h中添加了許多的MASConstraint property
@interface MASConstraintMaker : NSObject
//property 重寫了getter方法,使得每次鏈式調用相當于構造一個約束
@property (nonatomic, strong, readonly) MASConstraint *left;
@property (nonatomic, strong, readonly) MASConstraint *top;
...
并重寫了它們的getter方法:
//通用增加約束的方法
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}
//重寫getter方法,調用.方法相當于構造約束
- (MASConstraint *)left {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}
- (MASConstraint *)top {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
}
相當于我make調用相應的property,就是給給view添加了某個NSLayoutAttribute的結構體
//替換函數
- (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];
}
//通過NSLayoutAttribute添加約束
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
//構造view的MASViewAttribute
MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
//通過MASViewAttribute構造第一個MASViewConstraint
MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute];
//如果存在contraint,則把constraint和newConstraint組合成MASCompositeConstraint
if ([constraint isKindOfClass:MASViewConstraint.class]) {
//replace with composite constraint
NSArray *children = @[constraint, newConstraint];
MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
compositeConstraint.delegate = self;
//替換原來的constraint成新的MASCompositeConstraint
[self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
return compositeConstraint;
}
//不存在則設置constraint到self.constraints
if (!constraint) {
newConstraint.delegate = self;
[self.constraints addObject:newConstraint];
}
return newConstraint;
}
舉一個實際的例子:
make.top.mas_equalTo(superView.mas_top).with.mas_offset(10);
當調用make.top
的時候就會創建一個只只有firstViewAttribute的MASViewConstraint對象,并且進入不存在約束constraint的代碼部分
if (!constraint) {
newConstraint.delegate = self;
[self.constraints addObject:newConstraint];
}
設置delegate,并且將約束添加到self.constraints,同時返回的是剛剛創建的MASViewConstraint對象。
如果調用代碼是make.top.left
到left的時候其實是MASViewConstraint對象.left的調用,會走到我們剛才說的MASViewConstraint中重寫的- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute
方法,實現如下:
MASViewConstraint.m
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation");
return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
}
這邊就調用了MASViewConstraint的delegate方法,而MASViewConstraint的delegate對象則是剛才設置過的MASConstraintMaker對象,回到了剛才添加約束的方法。又新建了一個MASViewConstraint對象,并且因為constraint不是nil,進入
if ([constraint isKindOfClass:MASViewConstraint.class]) {
//replace with composite constraint
NSArray *children = @[constraint, newConstraint];
MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
compositeConstraint.delegate = self;
//替換原來的constraint成新的MASCompositeConstraint
[self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
return compositeConstraint;
}
返回的是一個MASCompositeConstraint對象。其中注意MASCompositeConstraint的初始化方法
- (id)initWithChildren:(NSArray *)children {
self = [super init];
if (!self) return nil;
_childConstraints = [children mutableCopy];
for (MASConstraint *constraint in _childConstraints) {
constraint.delegate = self;
}
return self;
}
會把里面的MASViewConstraint的delegate全部設置到MASCompositeConstraint對象身上。
如果調用的代碼是make.top.left.right
到right的時候,就是MASCompositeConstraint對象.right的調用,會走MASCompositeConstraint中的重寫方法
- (MASConstraint *)constraint:(MASConstraint __unused *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
id<MASConstraintDelegate> strongDelegate = self.delegate;
MASConstraint *newConstraint = [strongDelegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
newConstraint.delegate = self;
[self.childConstraints addObject:newConstraint];
return newConstraint;
}
這邊也會調用MASConstraintMaker的同一個方法,但這次if ([constraint isKindOfClass:MASViewConstraint.class])
與if (!constraint)
都不會進入,只會返回單純的MASViewConstraint對象,然后設置它的delegate,并且將對象存入MASCompositeConstraint的childConstraints中。
之后再有更多的鏈式MASConstraint的組合,都是MASCompositeConstraint的調用,不停的加入childConstraints中而已。同理superView.mas_top的構成也是同樣的方式。
直到調用到mas_equalTo(xx)
在MASConstraint中的實現是:
- (MASConstraint * (^)(id))equalTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
};
}
兩個子類重寫了equalToWithRelation
該方法
MASViewConstraint
- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation {
return ^id(id attribute, NSLayoutRelation relation) {
//如果是array,則是代表是多個組合對象
if ([attribute isKindOfClass:NSArray.class]) {
//如果已經有relation,則報錯
NSAssert(!self.hasLayoutRelation, @"Redefinition of constraint relation");
//組合約束存儲
NSMutableArray *children = NSMutableArray.new;
//遍歷每一個組合對象
for (id attr in attribute) {
//復制自己,這里有個邏輯,因為在copy的時候會設置一下layoutRelation,這邊的setLayoutRelation
//方法會把hasLayoutRelation設置為YES,單layoutRelation是默認的NSLayoutRelationEqual
//這邊其實比較明顯有個bug,就是沒有將relation在這里設置一下,所以所有的關系都會變成默認的
//我提交了一個pull request,等待修改
MASViewConstraint *viewConstraint = [self copy];
//viewConstraint.layoutRelation = relation;
//設置設置second attribute
viewConstraint.secondViewAttribute = attr;
[children addObject:viewConstraint];
}
//初始化MASCompositeConstraint
MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
compositeConstraint.delegate = self.delegate;
//替換原來的位置
[self.delegate constraint:self shouldBeReplacedWithConstraint:compositeConstraint];
return compositeConstraint;
} else {
//判斷是否已經有過relation,有的話則報錯
NSAssert(!self.hasLayoutRelation || self.layoutRelation == relation && [attribute isKindOfClass:NSValue.class], @"Redefinition of constraint relation");
//存儲relation
self.layoutRelation = relation;
//設置second attribute
self.secondViewAttribute = attribute;
return self;
}
};
}
tips:這邊的代碼有array添加的時候,會有一個relation設置問題的bug,少了//viewConstraint.layoutRelation = relation;
這句話。我已經提交了pull request,等待修復該問題。
MASCompositeConstraint
- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation {
return ^id(id attr, NSLayoutRelation relation) {
//遍歷所有的MASViewConstraint并設置
for (MASConstraint *constraint in self.childConstraints.copy) {
constraint.equalToWithRelation(attr, relation);
}
return self;
};
}
可以看到,該方法主要是為了設置layoutRelation與secondViewAttribute。
最后則是調用
- (MASConstraint * (^)(CGFloat))offset {
return ^id(CGFloat offset){
self.offset = offset;
return self;
};
}
設置偏移。
至此,整個構建創建者MASConstraintMaker的調用到此為止。
(3)約束函數調用
例子代碼中的調用函數如下:
UIView+MASAdditions.m
//創建約束
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
//去掉自動autoresizing轉為約束的
self.translatesAutoresizingMaskIntoConstraints = NO;
//構建builder
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
//運行builder
block(constraintMaker);
//附值約束返回
return [constraintMaker install];
}
先是去掉了AutoResizing的自動轉換,然后創建一個MASConstraintMaker對象,調用block去構成builder建造者。最后是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) {
//設置更新key
constraint.updateExisting = self.updateExisting;
[constraint install];
}
//去除所有緩存的約束結構體
[self.constraints removeAllObjects];
return constraints;
}
先判斷是否刪除所有的約束,然后再添加需要更新的約束。
先看一下uninstall方法
MASCompositeConstraint.m
- (void)uninstall {
for (MASConstraint *constraint in self.childConstraints) {
[constraint uninstall];
}
}
MASConstraint.m
//刪除約束
- (void)uninstall {
//判斷是否支持active去設置NSLayoutConstraint
if ([self supportsActiveProperty]) {
//設置active為no
self.layoutConstraint.active = NO;
//刪除installed緩存
[self.firstViewAttribute.view.mas_installedConstraints removeObject:self];
return;
}
//刪除此約束
[self.installedView removeConstraint:self.layoutConstraint];
self.layoutConstraint = nil;
self.installedView = nil;
//刪除installed緩存
[self.firstViewAttribute.view.mas_installedConstraints removeObject:self];
}
其中NSLayoutConstraint的active相當于addConstraint:
和removeConstraint:
。
然后就是install方法
MASCompositeConstraint.m
- (void)install {
for (MASConstraint *constraint in self.childConstraints) {
constraint.updateExisting = self.updateExisting;
[constraint install];
}
}
MASViewConstraint.m
- (void)install {
//已經有約束
if (self.hasBeenInstalled) {
return;
}
//支持active且已經有了約束
if ([self supportsActiveProperty] && self.layoutConstraint) {
//激活約束
self.layoutConstraint.active = YES;
//添加約束緩存
[self.firstViewAttribute.view.mas_installedConstraints addObject:self];
return;
}
//獲得item1,attribute1,item2,attribute2
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)
//如果attribute是sizeattribute并且沒有第二個attribute
if (!self.firstViewAttribute.isSizeAttribute && !self.secondViewAttribute) {
//默認設置item2為superview
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];
//設置priority和mas_key
layoutConstraint.priority = self.layoutPriority;
layoutConstraint.mas_key = self.mas_key;
//如果第二個attribute有view對象
if (self.secondViewAttribute.view) {
//則獲取兩個view的最小公共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);
//設置約束view為此view
self.installedView = closestCommonSuperview;
} else if (self.firstViewAttribute.isSizeAttribute) {
//如果是size attribute則為本身
self.installedView = self.firstViewAttribute.view;
} else {
//其它則是superview
self.installedView = self.firstViewAttribute.view.superview;
}
//已經存在的約束
MASLayoutConstraint *existingConstraint = nil;
//需要更新
if (self.updateExisting) {
//則獲得此生成的約束,返回和installedview的約束是同類的約束
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];
}
}
其中比較有趣的是取得兩個view的最小公共view的方法mas_closestCommonSuperview:
。
至此,簡單的Masonry主調用的調用源代碼也算全部解析過了。
總結
總的來說Masonry的源代碼有以下優點:
- 1.大量簡潔優美的宏(雖然用宏好不好另說)
- 2.鏈式調用的實現
- 3.builder建造者模式構成約束類型和設置約束
- 4.NSAssert語句判斷類型
- 5.構造抽象類MASConstraint(雖然oc沒有抽象類)
- 6.內部接口MASConstraint+Private.h
參考資料
1.Apple-NSLayoutConstraint
2.Auto Layout Guide
3.Visual Format Language
3.深入剖析Auto Layout,分析iOS各版本新增特性
4.iOS開發之Masonry框架源碼深度解析
5.Masonry源代碼分析
6.史上比較用心的純代碼實現 AutoLayout