Masonry介绍
Masonry是一个对系统NSLayoutConstraint进行封装的第三方自动布局框架,采用链式编程的方式提供给开发者API。系统AutoLayout支持的操作,Masonry都支持,相比系统API功能来说,Masonry是有过之而无不及。
Masonry框架的基本结构
首先我们看Masonry.h
文件
#import
//! Project version number for Masonry.
FOUNDATION_EXPORT double MasonryVersionNumber;
//! Project version string for Masonry.
FOUNDATION_EXPORT const unsigned char MasonryVersionString[];
#import "MASUtilities.h"
#import "View+MASAdditions.h"
#import "View+MASShorthandAdditions.h"
#import "ViewController+MASAdditions.h"
#import "NSArray+MASAdditions.h"
#import "NSArray+MASShorthandAdditions.h"
#import "MASConstraint.h"
#import "MASCompositeConstraint.h"
#import "MASViewAttribute.h"
#import "MASViewConstraint.h"
#import "MASConstraintMaker.h"
#import "MASLayoutConstraint.h"
#import "NSLayoutConstraint+MASDebugAdditions.h"
MASConstraint约束抽象类
MASCompositeConstraint试图组合约束类
MASViewAttribute试图属性类,用于创建约束
MASViewConstraint试图约束类,依赖于视图属性类
MASConstraintMaker创建约束的工厂方法
MASLayoutConstraint约束属性,对NSLayoutConstraint的封装
约束入口
我们都是对视图进行约束,所以入口就是一个UIView的分类 View+MASAdditions.h
/**
* following properties return a new MASViewAttribute with current view and appropriate NSLayoutAttribute
*/
@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;
@property (nonatomic, strong, readonly) MASViewAttribute *(^mas_attribute)(NSLayoutAttribute attr);
@property (nonatomic, strong, readonly) MASViewAttribute *mas_firstBaseline;
@property (nonatomic, strong, readonly) MASViewAttribute *mas_lastBaseline;
#if TARGET_OS_IPHONE || TARGET_OS_TV
@property (nonatomic, strong, readonly) MASViewAttribute *mas_leftMargin;
@property (nonatomic, strong, readonly) MASViewAttribute *mas_rightMargin;
@property (nonatomic, strong, readonly) MASViewAttribute *mas_topMargin;
@property (nonatomic, strong, readonly) MASViewAttribute *mas_bottomMargin;
@property (nonatomic, strong, readonly) MASViewAttribute *mas_leadingMargin;
@property (nonatomic, strong, readonly) MASViewAttribute *mas_trailingMargin;
@property (nonatomic, strong, readonly) MASViewAttribute *mas_centerXWithinMargins;
@property (nonatomic, strong, readonly) MASViewAttribute *mas_centerYWithinMargins;
@property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuide NS_AVAILABLE_IOS(11.0);
@property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideLeading NS_AVAILABLE_IOS(11.0);
@property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideTrailing NS_AVAILABLE_IOS(11.0);
@property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideLeft NS_AVAILABLE_IOS(11.0);
@property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideRight NS_AVAILABLE_IOS(11.0);
@property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideTop NS_AVAILABLE_IOS(11.0);
@property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideBottom NS_AVAILABLE_IOS(11.0);
@property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideWidth NS_AVAILABLE_IOS(11.0);
@property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideHeight NS_AVAILABLE_IOS(11.0);
@property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideCenterX NS_AVAILABLE_IOS(11.0);
@property (nonatomic, strong, readonly) MASViewAttribute *mas_safeAreaLayoutGuideCenterY NS_AVAILABLE_IOS(11.0);
#endif
这里,这些属性返回一个当前view的一个新的MASViewAttribute以及合适的NSLayoutAttribute。
MASViewAttribute这个对象是一个存储视图和相关NSLayoutAttribute的不可变元组。描述约束方程左侧或右侧的一部分。
make构建约束
/**
* Creates a MASConstraintMaker with the callee view.
* Any constraints defined are added to the view or the appropriate superview once the block has finished executing
*
* @param block scope within which you can build up the constraints which you wish to apply to the view.
*
* @return Array of created MASConstraints
*/
- (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
该方法根据传入的view创建一个MASConstraintMaker
,一旦block结束执行,任何定义的约束都被添加到view或者合适的superview
上。返回值就是一个创建的MASConstraints
数组。
update约束
/**
* Creates a MASConstraintMaker with the callee view.
* Any constraints defined are added to the view or the appropriate superview once the block has finished executing.
* If an existing constraint exists then it will be updated instead.
*
* @param block scope within which you can build up the constraints which you wish to apply to the view.
*
* @return Array of created/updated MASConstraints
*/
- (NSArray *)mas_updateConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
该方法根据传入的view创建一个MASConstraintMaker
,一旦block结束执行,任何定义的约束都被添加到view或者合适的superview
上。如果一个约束存在,那么它会替代到本次更新的约束。返回值是一个创建的/更新的约束的数组。
remake约束
/**
* Creates a MASConstraintMaker with the callee view.
* Any constraints defined are added to the view or the appropriate superview once the block has finished executing.
* All constraints previously installed for the view will be removed.
*
* @param block scope within which you can build up the constraints which you wish to apply to the view.
*
* @return Array of created/updated MASConstraints
*/
- (NSArray *)mas_remakeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
该方法用于根据传入的view创建一个MASConstraintMaker
,一旦block结束执行,任何定义的约束都被添加到view或者合适的superview
上。该view的所有以前的约束都将被remove掉,返回值是一个创建的/更新的约束的数组。该方法与update的区别是,update是更新相同的约束,而remake是完全删除以前的约束,使用本次重新创建的约束。
MASConstraintMaker工厂类
这里MASConstraintMaker
就是工厂方法,用于生成约束。具体负责创建MASConstraint
类型的对象(依赖于MASConstraint
接口,而不依赖于具体实现)。mas_makeConstraints
方法中的Block的参数就是MASConstraintMaker
的对象。用户可以通过该Block回调过来的MASConstraintMaker
对象给View指定要添加的约束以及该约束的值。该工厂中的.m文件中的constraints属性数组就记录了该工厂创建的所有MASConstraint对象。
1.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
};
该枚举其实就是对NSLayoutAttribute枚举的一种封装。
MASConstraint属性
- 布局属性定义
/**
* The following properties return a new MASViewConstraint
* with the first item set to the makers associated view and the appropriate MASViewAttribute
*/
@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;
@property (nonatomic, strong, readonly) MASConstraint *trailing;
@property (nonatomic, strong, readonly) MASConstraint *width;
@property (nonatomic, strong, readonly) MASConstraint *height;
@property (nonatomic, strong, readonly) MASConstraint *centerX;
@property (nonatomic, strong, readonly) MASConstraint *centerY;
@property (nonatomic, strong, readonly) MASConstraint *baseline;
@property (nonatomic, strong, readonly) MASConstraint *firstBaseline;
@property (nonatomic, strong, readonly) MASConstraint *lastBaseline;
#if TARGET_OS_IPHONE || TARGET_OS_TV
@property (nonatomic, strong, readonly) MASConstraint *leftMargin;
@property (nonatomic, strong, readonly) MASConstraint *rightMargin;
@property (nonatomic, strong, readonly) MASConstraint *topMargin;
@property (nonatomic, strong, readonly) MASConstraint *bottomMargin;
@property (nonatomic, strong, readonly) MASConstraint *leadingMargin;
@property (nonatomic, strong, readonly) MASConstraint *trailingMargin;
@property (nonatomic, strong, readonly) MASConstraint *centerXWithinMargins;
@property (nonatomic, strong, readonly) MASConstraint *centerYWithinMargins;
#endif
下面就以left属性为例看一下实现。
- (MASConstraint *)left {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}
这里,不管是left还是right或者center都会调用方法- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute
,然后就是添加约束。这里依赖了两个类,分别是MASViewAttribute和MASViewConstraint。
主要看一下下面这个方法,这个走的MASConstraint+Private.h
文件中的代理MASConstraintDelegate
,该代理中有两个方法。
@protocol MASConstraintDelegate
/**
* Notifies the delegate when the constraint needs to be replaced with another constraint. For example
* A MASViewConstraint may turn into a MASCompositeConstraint when an array is passed to one of the equality blocks
*/
- (void)constraint:(MASConstraint *)constraint shouldBeReplacedWithConstraint:(MASConstraint *)replacementConstraint;
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute;
@end
maker工厂方法中都实现了这个代理方法。
//代理方法实现
- (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];
}
- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
//实例化MASViewAttribute
MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute];
//实例化MASViewConstraint
MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute]; //判断传入的参数是不是MASViewConstraint类型,如果是进入if分支
if ([constraint isKindOfClass:MASViewConstraint.class]) {
//replace with composite constraint
NSArray *children = @[constraint, newConstraint];
//MASCompositeConstraint实例化并设置代理
MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
compositeConstraint.delegate = self;
//调用代理方法进行约束替换
[self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
return compositeConstraint;
}
//如果constraint为nil,那么就设置代理,并将新的约束添加到属性数组constraints里面
if (!constraint) {
newConstraint.delegate = self;
[self.constraints addObject:newConstraint];
}
return newConstraint;
}
这里需要说明MASViewConstraint
是MASConstraint
的子类。通过NSLayoutAttribute
创建一个MASConstraint并将创建的约束加入到数组constraints中,如果设置的是size、center等复合型的约束,则创建包含多个子约束的MASCompositeConstraint
。
- block
/**
* Returns a block which creates a new MASCompositeConstraint with the first item set
* to the makers associated view and children corresponding to the set bits in the
* MASAttribute parameter. Combine multiple attributes via binary-or.
*/
@property (nonatomic, strong, readonly) MASConstraint *(^attributes)(MASAttribute attrs);
该只读属性返回一个block,该块使用第一个item集创建新的MASCompositeConstraint与makers关联的视图和与MASAttribute参数中的设置位对应的子项。 通过binary-or组合多个属性。
//该block是一个带参数和带返回值的block,传入的是MASAttribute类型的参数返回的是MASConstraint,最后都是调用下面方法进行具体的实现。
- (MASConstraint *(^)(MASAttribute))attributes {
return ^(MASAttribute attrs){
return [self addConstraintWithAttributes:attrs];
};
}
- (MASConstraint *)addConstraintWithAttributes:(MASAttribute)attrs {
// 这里是列举出所有可能的MASAttribute类型的布局
__unused MASAttribute anyAttribute = (MASAttributeLeft | MASAttributeRight | MASAttributeTop | MASAttributeBottom | MASAttributeLeading
| MASAttributeTrailing | MASAttributeWidth | MASAttributeHeight | MASAttributeCenterX
| MASAttributeCenterY | MASAttributeBaseline
| MASAttributeFirstBaseline | MASAttributeLastBaseline
#if TARGET_OS_IPHONE || TARGET_OS_TV
| MASAttributeLeftMargin | MASAttributeRightMargin | MASAttributeTopMargin | MASAttributeBottomMargin
| MASAttributeLeadingMargin | MASAttributeTrailingMargin | MASAttributeCenterXWithinMargins
| MASAttributeCenterYWithinMargins
#endif
);
//断言,如果传入的布局参数不再上面列举出的布局类型中就抛出异常
NSAssert((attrs & anyAttribute) != 0, @"You didn't pass any attribute to make.attributes(...)");
//定义一个可变数据,并判断attrs的类型,并将相应的布局添加到可变数组中
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
// 定义另外一个局部可变数组
NSMutableArray *children = [NSMutableArray arrayWithCapacity:attributes.count];
//遍历上面的attributes可变数组,用其中的元素实例化MASViewConstraint对象并添加到新的children数组中
for (MASViewAttribute *a in attributes) {
[children addObject:[[MASViewConstraint alloc] initWithFirstViewAttribute:a]];
}
//根据数组children实例化MASCompositeConstraint对象,并作为返回值返回
MASCompositeConstraint *constraint = [[MASCompositeConstraint alloc] initWithChildren:children];
constraint.delegate = self;
[self.constraints addObject:constraint];
return constraint;
}
- edges、size和center
/**
* Creates a MASCompositeConstraint with type MASCompositeConstraintTypeEdges
* which generates the appropriate MASViewConstraint children (top, left, bottom, right)
* with the first item set to the makers associated view
*/
@property (nonatomic, strong, readonly) MASConstraint *edges;
/**
* Creates a MASCompositeConstraint with type MASCompositeConstraintTypeSize
* which generates the appropriate MASViewConstraint children (width, height)
* with the first item set to the makers associated view
*/
@property (nonatomic, strong, readonly) MASConstraint *size;
/**
* Creates a MASCompositeConstraint with type MASCompositeConstraintTypeCenter
* which generates the appropriate MASViewConstraint children (centerX, centerY)
* with the first item set to the makers associated view
*/
@property (nonatomic, strong, readonly) MASConstraint *center;
edges
:根据MASCompositeConstraintTypeEdges类型创建一个MASCompositeConstraint对象,产生适合的MASViewConstraint子对象 (top, left, bottom, right)
size
:根据MASCompositeConstraintTypeSize类型创建一个MASCompositeConstraint对象,产生适合的MASViewConstraint子对象 (width, height)
center
:根据MASCompositeConstraintTypeCenter类型创建一个MASCompositeConstraint对象,产生适合的MASViewConstraint子对象 (centerX, centerY)
- updateExisting、removeExisting
/**
* Whether or not to check for an existing constraint instead of adding constraint
* 是否去检查一个存在的约束,而不是添加约束
*/
@property (nonatomic, assign) BOOL updateExisting;
/**
* Whether or not to remove existing constraints prior to installing
* 在安装约束前是否移除约束
*/
@property (nonatomic, assign) BOOL removeExisting;
下面我们看一下在maker工厂方法中的使用,如下所示:
- (NSArray *)install {
if (self.removeExisting) {
//取出当前view安装的所有约束,并调用uninstall方法卸载约束。
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;
}
在maker工厂方法里面,遍历属性数组self.constraints
,并调用MASConstrain
t的install
方法安装约束。在将设置好添加到constraints数组中的约束都设置好后就要开始把约束加到View上起作用,方法为install,逻辑如下:
如果是remake
,也就是removeExisting =YES
时,将已经添加上的约束全部uninstall
,把本次maker里的所有创建的contraint设置好updateExistin
g,然后一个一个的install。
//MASConstraint.m文件中
- (void)install { MASMethodNotImplemented(); }
- (void)uninstall { MASMethodNotImplemented(); }
#define MASMethodNotImplemented() \
@throw [NSException exceptionWithName:NSInternalInconsistencyException \
reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)] \
userInfo:nil]
- group合并约束
该方法可以将多个maker中的多个约束合并为一个约束。
- (MASConstraint * (^)(dispatch_block_t))group;
//实现方法
- (MASConstraint *(^)(dispatch_block_t group))group {
return ^id(dispatch_block_t group) {
NSInteger previousCount = self.constraints.count;
group();
NSArray *children = [self.constraints subarrayWithRange:NSMakeRange(previousCount, self.constraints.count - previousCount)];
MASCompositeConstraint *constraint = [[MASCompositeConstraint alloc] initWithChildren:children];
constraint.delegate = self;
return constraint;
};
}
MASConstraint类解析
首先MASConstraint作为约束,是MASViewConstraint和MASCompositeConstraint的父类,其中MASViewConstraint指单个约束,而MASCompositeConstraint指复合约束。
#import "MASUtilities.h"
/**
* Enables Constraints to be created with chainable syntax
* Constraint can represent single NSLayoutConstraint (MASViewConstraint)
* or a group of NSLayoutConstraints (MASComposisteConstraint)
*/
@interface MASConstraint : NSObject
// Chaining Support
/**
* Modifies the NSLayoutConstraint constant,
* only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following
* NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, NSLayoutAttributeRight
* 修改NSLayoutConstraint常数,仅仅影响第一个item的NSLayoutAttribute
* NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, NSLayoutAttributeRight之一的MASConstraints
*/
- (MASConstraint * (^)(MASEdgeInsets insets))insets;
/**
* Modifies the NSLayoutConstraint constant,
* only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following
* NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, NSLayoutAttributeRight
* 修改NSLayoutConstraint常数,仅仅影响第一个item的NSLayoutAttribute
* NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, NSLayoutAttributeRight之一的MASConstraints
*/
- (MASConstraint * (^)(CGFloat inset))inset;
/**
* Modifies the NSLayoutConstraint constant,
* only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following
* NSLayoutAttributeWidth, NSLayoutAttributeHeight
* 修改NSLayoutConstraint常数,仅仅影响第一个item的NSLayoutAttribute
* NSLayoutAttributeWidth, NSLayoutAttributeHeight 之一的MASConstraints
*/
- (MASConstraint * (^)(CGSize offset))sizeOffset;
/**
* Modifies the NSLayoutConstraint constant,
* only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following
* NSLayoutAttributeCenterX, NSLayoutAttributeCenterY
* 修改NSLayoutConstraint常数,仅仅影响第一个item的NSLayoutAttribute
* NSLayoutAttributeCenterX, NSLayoutAttributeCenterY 之一的MASConstrain
*/
- (MASConstraint * (^)(CGPoint offset))centerOffset;
/**
* Modifies the NSLayoutConstraint constant
* 修改NSLayoutConstraint常数
*/
- (MASConstraint * (^)(CGFloat offset))offset;
/**
* Modifies the NSLayoutConstraint constant based on a value type
* 基于值类型,修改NSLayoutConstraint常数
*/
- (MASConstraint * (^)(NSValue *value))valueOffset;
/**
* Sets the NSLayoutConstraint multiplier property
*/
- (MASConstraint * (^)(CGFloat multiplier))multipliedBy;
/**
* Sets the NSLayoutConstraint multiplier to 1.0/dividedBy
*/
- (MASConstraint * (^)(CGFloat divider))dividedBy;
/**
* Sets the NSLayoutConstraint priority to a float or MASLayoutPriority
*/
- (MASConstraint * (^)(MASLayoutPriority priority))priority;
/**
* Sets the NSLayoutConstraint priority to MASLayoutPriorityLow
*/
- (MASConstraint * (^)(void))priorityLow;
/**
* Sets the NSLayoutConstraint priority to MASLayoutPriorityMedium
*/
- (MASConstraint * (^)(void))priorityMedium;
/**
* Sets the NSLayoutConstraint priority to MASLayoutPriorityHigh
*/
- (MASConstraint * (^)(void))priorityHigh;
/**
* 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;
/**
* Sets the constraint relation to NSLayoutRelationGreaterThanOrEqual
* returns a block which accepts one of the following:
* MASViewAttribute, UIView, NSValue, NSArray
* see readme for more details.
*/
- (MASConstraint * (^)(id attr))greaterThanOrEqualTo;
/**
* Sets the constraint relation to NSLayoutRelationLessThanOrEqual
* returns a block which accepts one of the following:
* MASViewAttribute, UIView, NSValue, NSArray
* see readme for more details.
*/
- (MASConstraint * (^)(id attr))lessThanOrEqualTo;
/**
* Optional semantic property which has no effect but improves the readability of constraint
*/
- (MASConstraint *)with;
/**
* Optional semantic property which has no effect but improves the readability of constraint
*/
- (MASConstraint *)and;
/**
* Creates a new MASCompositeConstraint with the called attribute and reciever
*/
- (MASConstraint *)left;
- (MASConstraint *)top;
- (MASConstraint *)right;
- (MASConstraint *)bottom;
- (MASConstraint *)leading;
- (MASConstraint *)trailing;
- (MASConstraint *)width;
- (MASConstraint *)height;
- (MASConstraint *)centerX;
- (MASConstraint *)centerY;
- (MASConstraint *)baseline;
- (MASConstraint *)firstBaseline;
- (MASConstraint *)lastBaseline;
#if TARGET_OS_IPHONE || TARGET_OS_TV
- (MASConstraint *)leftMargin;
- (MASConstraint *)rightMargin;
- (MASConstraint *)topMargin;
- (MASConstraint *)bottomMargin;
- (MASConstraint *)leadingMargin;
- (MASConstraint *)trailingMargin;
- (MASConstraint *)centerXWithinMargins;
- (MASConstraint *)centerYWithinMargins;
#endif
......省略
@end
1. 链式支持
#define MASEdgeInsets UIEdgeInsets
//MASConstraint.h
- (MASConstraint * (^)(MASEdgeInsets insets))insets;
//MASConstraint.m
- (MASConstraint * (^)(MASEdgeInsets))insets {
return ^id(MASEdgeInsets insets){
self.insets = insets;
return self;
};
}
//这是一个抽象方法,在MASConstraint类中没有实现
- (void)setInsets:(MASEdgeInsets __unused)insets { MASMethodNotImplemented(); }
#define MASMethodNotImplemented() \
@throw [NSException exceptionWithName:NSInternalInconsistencyException \
reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)] \
userInfo:nil]
首先更新一下属性self.insets,然后把自己self返回去。还记得工厂方法里面的maker吗?它就会返回一个MASConstraint对象,这里我们调用上面的block然后就将自己返回去,你就可以一直用点语法install约束,这就是链式编程。
2.NSLayoutConstraint常数设置
MASConstraint的子类MASCompositeConstraint和MASViewConstraint中都进行了重写并实现。
//MASCompositeConstraint.m
- (void)setInsets:(MASEdgeInsets)insets {
for (MASConstraint *constraint in self.childConstraints) {
constraint.insets = insets;
}
}
//MASViewConstraint.m
- (void)setInsets:(MASEdgeInsets)insets {
NSLayoutAttribute layoutAttribute = self.firstViewAttribute.layoutAttribute;
switch (layoutAttribute) {
case NSLayoutAttributeLeft:
case NSLayoutAttributeLeading:
self.layoutConstant = insets.left;
break;
case NSLayoutAttributeTop:
self.layoutConstant = insets.top;
break;
case NSLayoutAttributeBottom:
self.layoutConstant = -insets.bottom;
break;
case NSLayoutAttributeRight:
case NSLayoutAttributeTrailing:
self.layoutConstant = -insets.right;
break;
default:
break;
}
}
都是用来进行更改约束的。
3.NSLayoutConstraint Installation支持
/**
* Activates an NSLayoutConstraint if it's supported by an OS.
* Invokes install otherwise.
* 如果OS支持就激活一个NSLayoutConstraint,否则就调用install
*/
- (void)activate;
/**
* Deactivates previously installed/activated NSLayoutConstraint.
* 销毁前面安装或者激活的NSLayoutConstraint
*/
- (void)deactivate;
/**
* Creates a NSLayoutConstraint and adds it to the appropriate view.
* 创建一个NSLayoutConstraint并将它添加到合适的view上
*/
- (void)install;
/**
* Removes previously installed NSLayoutConstraint
* 移除以前安装的NSLayoutConstraint
*/
- (void)uninstall;
//MASConstraint.m
- (void)activate { MASMethodNotImplemented(); }
- (void)deactivate { MASMethodNotImplemented(); }
- (void)install { MASMethodNotImplemented(); }
- (void)uninstall { MASMethodNotImplemented(); }
这四个方法都是抽象方法,均不在给类中实现,而是在子类MASCompositeConstraint和MASViewConstraint中都进行了重写并实现。
下面看一下在子类中的实现情况。
//MASCompositeConstraint.m
- (void)activate {
for (MASConstraint *constraint in self.childConstraints) {
[constraint activate];
}
}
- (void)deactivate {
for (MASConstraint *constraint in self.childConstraints) {
[constraint deactivate];
}
}
- (void)install {
for (MASConstraint *constraint in self.childConstraints) {
constraint.updateExisting = self.updateExisting;
[constraint install];
}
}
- (void)uninstall {
for (MASConstraint *constraint in self.childConstraints) {
[constraint uninstall];
}
}
//MASViewConstraint.m
[self install];
}
- (void)deactivate {
[self uninstall];
}
- (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];
}
}
- (MASLayoutConstraint *)layoutConstraintSimilarTo:(MASLayoutConstraint *)layoutConstraint {
// check if any constraints are the same apart from the only mutable property constant
// go through constraints in reverse as we do not want to match auto-resizing or interface builder constraints
// and they are likely to be added first.
for (NSLayoutConstraint *existingConstraint in self.installedView.constraints.reverseObjectEnumerator) {
if (![existingConstraint isKindOfClass:MASLayoutConstraint.class]) continue;
if (existingConstraint.firstItem != layoutConstraint.firstItem) continue;
if (existingConstraint.secondItem != layoutConstraint.secondItem) continue;
if (existingConstraint.firstAttribute != layoutConstraint.firstAttribute) continue;
if (existingConstraint.secondAttribute != layoutConstraint.secondAttribute) continue;
if (existingConstraint.relation != layoutConstraint.relation) continue;
if (existingConstraint.multiplier != layoutConstraint.multiplier) continue;
if (existingConstraint.priority != layoutConstraint.priority) continue;
return (id)existingConstraint;
}
return nil;
}
- (void)uninstall {
if ([self supportsActiveProperty]) {
self.layoutConstraint.active = NO;
[self.firstViewAttribute.view.mas_installedConstraints removeObject:self];
return;
}
[self.installedView removeConstraint:self.layoutConstraint];
self.layoutConstraint = nil;
self.installedView = nil;
[self.firstViewAttribute.view.mas_installedConstraints removeObject:self];
}
4.MASConstraint分类
@interface MASConstraint (AutoboxingSupport)
/**
* Aliases to corresponding relation methods (for shorthand macros)
* Also needed to aid autocompletion
*/
- (MASConstraint * (^)(id attr))mas_equalTo;
- (MASConstraint * (^)(id attr))mas_greaterThanOrEqualTo;
- (MASConstraint * (^)(id attr))mas_lessThanOrEqualTo;
/**
* A dummy method to aid autocompletion
*/
- (MASConstraint * (^)(id offset))mas_offset;
@end
以- (MASConstraint * (^)(id attr))mas_equalTo;
为例进行说明
- (MASConstraint * (^)(id))mas_equalTo {
return ^id(id attribute) {
return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
};
}
- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation { MASMethodNotImplemented(); }
可见,这个在本抽象类还是没有实现,在子类MASCompositeConstraint和MASViewConstraint中都进行了重写并实现。
//MASCompositeConstraint.m
- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation {
return ^id(id attr, NSLayoutRelation relation) {
for (MASConstraint *constraint in self.childConstraints.copy) {
constraint.equalToWithRelation(attr, relation);
}
return self;
};
}
//MASViewConstraint.m
- (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;
}
};
}
MASCompositeConstraint类解析
#import "MASConstraint.h"
#import "MASUtilities.h"
/**
* A group of MASConstraint objects
*/
@interface MASCompositeConstraint : MASConstraint
/**
* Creates a composite with a predefined array of children
*
* @param children child MASConstraints
*
* @return a composite constraint
*/
- (id)initWithChildren:(NSArray *)children;
@end
该类只有一个实例化方法,用于实例化对象,传入的参数children,为约束数组,利用这个数组进行合成约束。
在其.m中不仅实现了这个实例化方法,还重写了很多MASConstrain方法。并设置代理和实现了MASConstraintDelegate。我们首先看一下这个实例化方法。
@property (nonatomic, strong) NSMutableArray *childConstraints;
- (id)initWithChildren:(NSArray *)children {
self = [super init];
if (!self) return nil;
_childConstraints = [children mutableCopy];
for (MASConstraint *constraint in _childConstraints) {
constraint.delegate = self;
}
return self;
}
下面看一下内部的组织结构,如下图所示:
从这我们就可以看到Masonry的组织结构非常清爽,很多方法都在父类定义并在子类重写,子类只保留了一个自定义的示例化方法。
MASViewConstraint类解析
该类是一个单约束,包含创建NSLayoutConstraint所需要的属性,并将其添加到适合的view上.
/**
* A single constraint.
* Contains the attributes neccessary for creating a NSLayoutConstraint and adding it to the appropriate view
* NSLayoutConstraint的第一个item/view和第一个属性
*/
@interface MASViewConstraint : MASConstraint
/**
* First item/view and first attribute of the NSLayoutConstraint
* NSLayoutConstraint的第二个item/view和第二个属性
*/
@property (nonatomic, strong, readonly) MASViewAttribute *firstViewAttribute;
/**
* Second item/view and second attribute of the NSLayoutConstraint
*/
@property (nonatomic, strong, readonly) MASViewAttribute *secondViewAttribute;
/**
* initialises the MASViewConstraint with the first part of the equation
*
* @param firstViewAttribute view.mas_left, view.mas_width etc.
*
* @return a new view constraint
*/
- (id)initWithFirstViewAttribute:(MASViewAttribute *)firstViewAttribute;
/**
* Returns all MASViewConstraints installed with this view as a first item.
*
* @param view A view to retrieve constraints for.
*
* @return An array of MASViewConstraints.
*/
+ (NSArray *)installedConstraintsForView:(MAS_VIEW *)view;
@end
- 实例化方法
@property (nonatomic, assign) MASLayoutPriority layoutPriority;
@property (nonatomic, assign) CGFloat layoutMultiplier;
- (id)initWithFirstViewAttribute:(MASViewAttribute *)firstViewAttribute {
self = [super init];
if (!self) return nil;
_firstViewAttribute = firstViewAttribute;
self.layoutPriority = MASLayoutPriorityRequired;
self.layoutMultiplier = 1;
return self;
}
- 返回所有安装的约束
+ (NSArray *)installedConstraintsForView:(MAS_VIEW *)view {
return [view.mas_installedConstraints allObjects];
}
这里要用到一个MAS_VIEW(其实就是UIView)的一个分类。
@interface MAS_VIEW (MASConstraints)
@property (nonatomic, readonly) NSMutableSet *mas_installedConstraints;
@end
@implementation MAS_VIEW (MASConstraints)
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;
}
@end
同MASCompositeConstraint类一样,在.m文件里面MASViewConstraint也重写了很多的父类的方法,结构很清晰。
下面看一下该类的结构
在.h中只暴露出来一个实例化方法和返回所有已安装约束的方法。在.m中重写了很多父类的方法