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 源碼解讀(上)
若是以爲我寫的還不錯,請關注個人微博@小橘爺,最新文章即時推送~