版本记录
版本号 | 时间 |
---|---|
V1.0 | 2018.07.18 |
前言
我们做APP界面,也就是布局UI,那么关于布局,我们有很多方法,苹果也都提供了支持,市场上我们用的并不是系统提供原生的layout,对于OC语言一般都是使用一个第三方的布局框架 —— Masonry。接下来几篇我们就一起深入看一下这个框架。感兴趣的看上面几篇文章。
1. Masonry框架详细解析(一) —— 基本概览(一)
2. Masonry框架详细解析(二) —— 基本结构API和约束入口(一)
3. Masonry框架详细解析(三) —— MASConstraintMaker工厂类(一)
MASConstraint、MASViewConstraint和MASCompositeConstraint三者之间的关系
首先MASConstraint
作为约束,是MASViewConstraint
和MASCompositeConstraint
的父类,其中MASViewConstraint
指单个约束,而MASCompositeConstraint
指复合约束。
我们可以先看一下下面的示意图,很清晰的说明了三者的关系。
下面我们就看一下这几个类都是做什么用的以及里面都有什么。MASConstraint本类和分类内容如下所示。
MASConstraint本类
1. API
首先看一下API
#import "MASUtilities.h"
/**
* Enables Constraints to be created with chainable syntax
* Constraint can represent single NSLayoutConstraint (MASViewConstraint)
* or a group of NSLayoutConstraints (MASComposisteConstraint)
*/
// 允许使用可链接语法创建约束。 约束可以表示单个NSLayoutConstraint(MASViewConstraint)
// 或一组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,
// 之一的MASConstraints
- (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
*/
//设置NSLayoutConstraint的multiplier属性
- (MASConstraint * (^)(CGFloat multiplier))multipliedBy;
/**
* Sets the NSLayoutConstraint multiplier to 1.0/dividedBy
*/
//设置NSLayoutConstraint的multiplier为1.0/dividedBy
- (MASConstraint * (^)(CGFloat divider))dividedBy;
/**
* Sets the NSLayoutConstraint priority to a float or MASLayoutPriority
*/
//设置NSLayoutConstraint的优先级为一个浮点数或者MASLayoutPriority
- (MASConstraint * (^)(MASLayoutPriority priority))priority;
/**
* Sets the NSLayoutConstraint priority to MASLayoutPriorityLow
*/
// 设置NSLayoutConstraint的优先级为MASLayoutPriorityLow
- (MASConstraint * (^)(void))priorityLow;
/**
* Sets the NSLayoutConstraint priority to MASLayoutPriorityMedium
*/
// 设置NSLayoutConstraint优先级为MASLayoutPriorityMedium
- (MASConstraint * (^)(void))priorityMedium;
/**
* Sets the NSLayoutConstraint priority to MASLayoutPriorityHigh
*/
//设置NSLayoutConstraint优先级为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.
*/
//设置约束关系为NSLayoutRelationEqual,返回一个block,
//接收一下类型参数MASViewAttribute, UIView, NSValue, NSArray
- (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.
*/
//设置约束关系为NSLayoutRelationGreaterThanOrEqual,返回一个block,
//接收一下类型参数MASViewAttribute, UIView, NSValue, NSArray
- (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.
*/
//设置约束关系为NSLayoutRelationLessThanOrEqual,返回一个block,
//接收一下类型参数MASViewAttribute, UIView, NSValue, NSArray
- (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
*/
// 根据调用的属性创建一个新的MASCompositeConstraint
- (MASConstraint *)left;
- (MASConstraint *)top;
- (MASConstraint *)right;
- (MASConstraint *)bottom;
- (MASConstraint *)leading;
- (MASConstraint *)trailing;
- (MASConstraint *)width;
- (MASConstraint *)height;
- (MASConstraint *)centerX;
- (MASConstraint *)centerY;
- (MASConstraint *)baseline;
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
- (MASConstraint *)firstBaseline;
- (MASConstraint *)lastBaseline;
#endif
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (__TV_OS_VERSION_MIN_REQUIRED >= 9000)
- (MASConstraint *)leftMargin;
- (MASConstraint *)rightMargin;
- (MASConstraint *)topMargin;
- (MASConstraint *)bottomMargin;
- (MASConstraint *)leadingMargin;
- (MASConstraint *)trailingMargin;
- (MASConstraint *)centerXWithinMargins;
- (MASConstraint *)centerYWithinMargins;
#endif
/**
* Sets the constraint debug name
*/
//设置约束constraint的debug name
- (MASConstraint * (^)(id key))key;
// NSLayoutConstraint constant Setters
// for use outside of mas_updateConstraints/mas_makeConstraints blocks
/**
* 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
- (void)setInsets:(MASEdgeInsets)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
- (void)setInset:(CGFloat)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, NSLayoutAttributeHeigh之一的MASConstraints
- (void)setSizeOffset:(CGSize)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之一的MASConstraints
- (void)setCenterOffset:(CGPoint)centerOffset;
/**
* Modifies the NSLayoutConstraint constant
*/
//修改NSLayoutConstraint常量
- (void)setOffset:(CGFloat)offset;
// NSLayoutConstraint Installation support
#if TARGET_OS_MAC && !(TARGET_OS_IPHONE || TARGET_OS_TV)
/**
* Whether or not to go through the animator proxy when modifying the constraint
*/
// 是否在修改约束时通过动画代理
@property (nonatomic, copy, readonly) MASConstraint *animator;
#endif
/**
* 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;
@end
/**
* //用于MASConstraint方法的便捷自动装箱宏
* Convenience auto-boxing macros for MASConstraint methods.
*
* Defining MAS_SHORTHAND_GLOBALS will turn on auto-boxing for default syntax.
* A potential drawback of this is that the unprefixed macros will appear in global scope.
*/
#define mas_equalTo(...) equalTo(MASBoxValue((__VA_ARGS__)))
#define mas_greaterThanOrEqualTo(...) greaterThanOrEqualTo(MASBoxValue((__VA_ARGS__)))
#define mas_lessThanOrEqualTo(...) lessThanOrEqualTo(MASBoxValue((__VA_ARGS__)))
#define mas_offset(...) valueOffset(MASBoxValue((__VA_ARGS__)))
#ifdef MAS_SHORTHAND_GLOBALS
#define equalTo(...) mas_equalTo(__VA_ARGS__)
#define greaterThanOrEqualTo(...) mas_greaterThanOrEqualTo(__VA_ARGS__)
#define lessThanOrEqualTo(...) mas_lessThanOrEqualTo(__VA_ARGS__)
#define offset(...) mas_offset(__VA_ARGS__)
#endif
上面每个属性的意义,我都标注里面了,感兴趣的可以看下,如果有别的需要特别说出的,我会单独列出来。
2. 链式支持
下面就以为例说一下链式支持。
/**
* Modifies the NSLayoutConstraint constant,
* only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following
* NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, NSLayoutAttributeRight
*/
- (MASConstraint * (^)(MASEdgeInsets insets))insets;
下面我们看一下实现
#define MASEdgeInsets UIEdgeInsets
- (MASConstraint * (^)(MASEdgeInsets))insets {
return ^id(MASEdgeInsets insets){
self.insets = insets;
return self;
};
}
- (void)setInsets:(UIEdgeInsets)insets {
Setter(if(!_path){
if (insets.top < 0) insets.top = 0;
if (insets.left < 0) insets.left = 0;
if (insets.bottom < 0) insets.bottom = 0;
if (insets.right < 0) insets.right = 0;
_insets = insets;
});
}
首先更新一下属性self.insets
,然后把自己self返回去。还记得工厂方法里面的maker吗?它就会返回一个MASConstraint
对象,这里我们调用上面的block然后就将自己返回去,你就可以一直用点语法install约束,这就是链式编程。
3. NSLayoutConstraint常数设置
这里以下面方法为例进行说明。
/**
* Modifies the NSLayoutConstraint constant,
* only affects MASConstraints in which the first item's NSLayoutAttribute is one of the following
* NSLayoutAttributeTop, NSLayoutAttributeLeft, NSLayoutAttributeBottom, NSLayoutAttributeRight
*/
- (void)setInsets:(MASEdgeInsets)insets;
这是一个抽象方法,在MASConstraint
类中没有实现,具体如下所示:
- (void)setInsets:(MASEdgeInsets __unused)insets { MASMethodNotImplemented(); }
但是,它的子类MASCompositeConstraint
和MASViewConstraint
中都进行了重写并实现。
// MASCompositeConstraint
- (void)setInsets:(MASEdgeInsets)insets {
for (MASConstraint *constraint in self.childConstraints) {
constraint.insets = insets;
}
}
// MASViewConstraint
- (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;
}
}
它们的实现目标都是用来进行更改约束的。
4. NSLayoutConstraint Installation支持
这里针对iOS就下面四个方法
- (void)activate;
- (void)deactivate;
- (void)install;
- (void)uninstall;
同样这四个方法都是抽象方法,均不在给类中实现,而是在子类MASCompositeConstraint
和MASViewConstraint
中都进行了重写并实现。
- (void)activate { MASMethodNotImplemented(); }
- (void)deactivate { MASMethodNotImplemented(); }
- (void)install { MASMethodNotImplemented(); }
- (void)uninstall { MASMethodNotImplemented(); }
下面看一下在子类中的实现情况。
// MASCompositeConstraint
- (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
- (void)activate {
[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];
}
MASConstraint分类
下面我们就看一下MASConstraint
分类AutoboxingSupport
首先看一下该分类的API文档
1. API
@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
*/
// 一种帮助自动完成的dummy方法
- (MASConstraint * (^)(id offset))mas_offset;
@end
2. 别名方法
下面就以下面方法为例进行说明
- (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
- (MASConstraint * (^)(id, NSLayoutRelation))equalToWithRelation {
return ^id(id attr, NSLayoutRelation relation) {
for (MASConstraint *constraint in self.childConstraints.copy) {
constraint.equalToWithRelation(attr, relation);
}
return self;
};
}
//MASViewConstraint
- (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;
}
};
}
后记
本篇主要讲述了MASConstraint类解析,感兴趣的给个赞或者关注~~~