Masonry的核心依然是使用原生的NSLayoutConstraint類來進行添加限制,通過統一的封裝和鍊式函數式程式設計的方式讓開發者添加限制布局更加友善。
一、核心的View+MASAdditions類别
這個類别是Masonry中用來添加,更新和重置限制的核心類别。其中提供了我們最常用的布局函數。首先從類别命名上也可以看出,此類别擴充的類是通過宏來設定的:
@interface MAS_VIEW (MASAdditions)
MAS_VIEW宏做到了平台屏蔽的作用,在iOS上,其為UIView,在MacOS上其實NSView。
MASAdditions類别中定義了許多布局屬性,例如上,下,左,右邊距,寬度高度等等。這些屬性被抽象為MASViewAttribute對象,關于這個對象,後面會具體介紹。
//左
@property (nonatomic, strong, readonly) MASViewAttribute *mas_left;
//上
@property (nonatomic, strong, readonly) MASViewAttribute *mas_top;
//右
@property (nonatomic, strong, readonly) MASViewAttribute *mas_right;
//下
@property (nonatomic, strong, readonly) MASViewAttribute *mas_bottom;
//前
@property (nonatomic, strong, readonly) MASViewAttribute *mas_leading;
//後
@property (nonatomic, strong, readonly) MASViewAttribute *mas_trailing;
//寬
@property (nonatomic, strong, readonly) MASViewAttribute *mas_width;
//高
@property (nonatomic, strong, readonly) MASViewAttribute *mas_height;
//水準中心
@property (nonatomic, strong, readonly) MASViewAttribute *mas_centerX;
//垂直中心
@property (nonatomic, strong, readonly) MASViewAttribute *mas_centerY;
//基線
@property (nonatomic, strong, readonly) MASViewAttribute *mas_baseline;
//這個是一個鍊式程式設計的通用轉換方法,使用這個屬性将系統的NSLayoutAttribute轉換成抽象的MASViewAttribute對象
@property (nonatomic, strong, readonly) MASViewAttribute *(^mas_attribute)(NSLayoutAttribute attr);
//基線相關
@property (nonatomic, strong, readonly) MASViewAttribute *mas_firstBaseline;
@property (nonatomic, strong, readonly) MASViewAttribute *mas_lastBaseline;
//安全區 相關
@property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuide API_AVAILABLE(ios(11.0),tvos(11.0));
@property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideTop API_AVAILABLE(ios(11.0),tvos(11.0));
@property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideBottom API_AVAILABLE(ios(11.0),tvos(11.0));
@property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideLeft API_AVAILABLE(ios(11.0),tvos(11.0));
@property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideRight API_AVAILABLE(ios(11.0),tvos(11.0));
//關聯的key值
@property (nonatomic, strong) id mas_key;
下面是3個最常使用的布局方法:
//建立限制
- (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;
這3個函數的具體實作基本一緻,其核心流程都是:關閉視圖Autoresizing特性->建立限制生成器->配置限制生成器->回調開發者限制設定->進行限制加載。這3個函數不同的地方隻在配置限制生成器部分,配置了updateExisting參數為YES,表示要進行已有限制的更新,配置了removeExisting為YES表示要重新建立限制。限制生成器被抽象為MASConstraintMaker對象,下面來具體看這個類。
二、MASConstraintMaker限制生成器
MASConstraint類主要用來建構限制對象。其中雖然和MASAdditions擴充類似,也是定義了限制屬性對象,但是其所有的Get方法都被重新實作了,當我們通過Get方法調用限制屬性時,會執行下面核心函數:
- (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;
}
上面函數的設計可以巧妙的實作複合限制,例如make.width.height.equalTo(@100)這樣一條限制,實際上從width開始後面的屬性都被複合進了MASCompositeConstraint對象。限制的屬性建立出來後,需要對其進行值的設定,下面來看MASViewConstraint對象。
三、MASConstraint限制對象
MASViewConstraint類繼承自MASConstraint類,MASConstraint類還有一個子類為MASCompositeConstraint類。MASConstraint中定義了基礎的限制值設定方法,都是采用block回調的方式,是以可以進行鍊式程式設計:
//位置
- (MASConstraint * (^)(MASEdgeInsets insets))insets;
//尺寸偏移
- (MASConstraint * (^)(CGSize offset))sizeOffset;
//中心位置偏移
- (MASConstraint * (^)(CGPoint offset))centerOffset;
//比例 *
- (MASConstraint * (^)(CGFloat multiplier))multipliedBy;
//比例 /
- (MASConstraint * (^)(CGFloat divider))dividedBy;
//優先級
- (MASConstraint * (^)(MASLayoutPriority priority))priority;
//直接設定為低優先級
- (MASConstraint * (^)(void))priorityLow;
//直接設定為中優先級
- (MASConstraint * (^)(void))priorityMedium;
//直接設定為高優先級
- (MASConstraint * (^)(void))priorityHigh;
//設定絕對等于
- (MASConstraint * (^)(id attr))equalTo;
//大于等于
- (MASConstraint * (^)(id attr))greaterThanOrEqualTo;
//小于等于
- (MASConstraint * (^)(id attr))lessThanOrEqualTo;
閱讀這個屬性的Get方法,你會發現他們最後都傳回了目前對象本身,這也是為鍊式程式設計所準備,MASConstraint中還有兩個屬性比較有趣:
- (MASConstraint *)with;
- (MASConstraint *)and;
這兩個屬性沒有實際的作用也沒有任何影響,他們的實作是直接傳回目前對象,增強代碼可讀性。
MASConstraint類中的install和uninstall函數是核心的限制添加方法,其中會進行系統原生限制對象的轉換添加或者删除操作。核心的install函數解析如下:
- (void)install {
//如果已經被加載 直接傳回
if (self.hasBeenInstalled) {
return;
//如果系統layout對象已經建立 直接添加之後 傳回
if ([self supportsActiveProperty] && self.layoutConstraint) {
self.layoutConstraint.active = YES;
[self.firstViewAttribute.view.mas_installedConstraints addObject:self];
//擷取布局的視圖與屬性
MAS_VIEW *firstLayoutItem = self.firstViewAttribute.item;
NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute;
MAS_VIEW *secondLayoutItem = self.secondViewAttribute.item;
NSLayoutAttribute secondLayoutAttribute = self.secondViewAttribute.layoutAttribute;
//如果不是尺寸布局并且 相對視圖不存在 預設對父視圖進行相對布局
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];
//設定key和優先級
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;
//添加限制
[self.installedView addConstraint:layoutConstraint];
self.layoutConstraint = layoutConstraint;
[firstLayoutItem.mas_installedConstraints addObject:self];