看一下這兩個(gè)屬性的區(qū)別:
NSLayoutAttributeLeft和NSLayoutAttributeLeftMargin的區(qū)別:
NSLayoutAttributeLeft指的是控件的左邊,具體來(lái)說(shuō)是控件的最左邊;NSLayoutAttributeLeftMargin也是指控件的左邊,但是不是最左邊,具體距離最左邊有多大的Margin和控件的layoutMargins有關(guān)。
NSLayoutAttributeLeftMargin: The object's left margin.For UIView objects, the margins are defined by their layoutMargins property.
注:默認(rèn)是{8,8,8,8},但是如果是viewController的root view則top和bottom的margins為0,左右margins可能是16或者20,這取決于當(dāng)前的view尺寸,并且不能修改。
UIView *subView = [UIView new];
[self.view addSubview:subView];
[subView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view).offset(10.f);
make.right.equalTo(self.view).offset(-10.f);
make.height.mas_equalTo(100.f);
make.left.equalTo(self.view).offset(10.f);
}];
subView.backgroundColor = [UIColor redColor];
UIView *subView1 = [UIView new];
[self.view addSubview:subView1];
[subView1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view).offset(100.f);
make.right.equalTo(self.view).offset(-10.f);
make.height.mas_equalTo(100.f);
make.leftMargin.equalTo(self.view).offset(10.f);
}];
subView1.backgroundColor = [UIColor yellowColor];
可以看到上面代碼對(duì)應(yīng)的結(jié)果
// 約束的不等式關(guān)系
typedef NS_ENUM(NSInteger, NSLayoutRelation) {
NSLayoutRelationLessThanOrEqual = -1,
NSLayoutRelationEqual = 0,
NSLayoutRelationGreaterThanOrEqual = 1,
};
NSLayoutAttributeLeading: 在習(xí)慣由左向右看的地區(qū),相當(dāng)于NSLayoutAttributeLeft;在習(xí)慣從右至左看的地區(qū),相當(dāng)于NSLayoutAttributeRight;
NSLayoutAttributeTrailing: 在習(xí)慣由左向右看的地區(qū),相當(dāng)于NSLayoutAttributeRight;在習(xí)慣從右至左看的地區(qū),相當(dāng)于NSLayoutAttributeLeft;
// ```下一次我們的label是可以使用NSLayoutAttributeLastBaseline 或者NSLayoutAttributeFirstBaseline 來(lái)進(jìn)行設(shè)置約束```
typedef NS_ENUM(NSInteger, NSLayoutAttribute) {
NSLayoutAttributeLeft = 1,// 左邊
NSLayoutAttributeRight, // 右
NSLayoutAttributeTop,
NSLayoutAttributeBottom,
NSLayoutAttributeLeading, //
NSLayoutAttributeTrailing,
NSLayoutAttributeWidth,
NSLayoutAttributeHeight,
NSLayoutAttributeCenterX,
NSLayoutAttributeCenterY,
NSLayoutAttributeLastBaseline, // 文本的下標(biāo)標(biāo)
#if TARGET_OS_IPHONE
NSLayoutAttributeBaseline NS_SWIFT_UNAVAILABLE("Use 'lastBaseline' instead") = NSLayoutAttributeLastBaseline, // 文本的最后一行的距離
#else
NSLayoutAttributeBaseline = NSLayoutAttributeLastBaseline,
#endif
NSLayoutAttributeFirstBaseline API_AVAILABLE(macos(10.11), ios(8.0)), //文本的第一行的距離
#if TARGET_OS_IPHONE // margin 默認(rèn)的margin內(nèi)容
NSLayoutAttributeLeftMargin API_AVAILABLE(ios(8.0)),
NSLayoutAttributeRightMargin API_AVAILABLE(ios(8.0)),
NSLayoutAttributeTopMargin API_AVAILABLE(ios(8.0)),
NSLayoutAttributeBottomMargin API_AVAILABLE(ios(8.0)),
NSLayoutAttributeLeadingMargin API_AVAILABLE(ios(8.0)),
NSLayoutAttributeTrailingMargin API_AVAILABLE(ios(8.0)),
NSLayoutAttributeCenterXWithinMargins API_AVAILABLE(ios(8.0)),
NSLayoutAttributeCenterYWithinMargins API_AVAILABLE(ios(8.0)),
#endif
NSLayoutAttributeNotAnAttribute = 0 // 有關(guān)屬性沒(méi)有
};
實(shí)現(xiàn)距離上面同樣的80距離的效果
設(shè)置約束的權(quán)限
typedef float UILayoutPriority NS_TYPED_EXTENSIBLE_ENUM;
static const UILayoutPriority UILayoutPriorityRequired API_AVAILABLE(ios(6.0)) = 1000; // A required constraint. Do not exceed this.
static const UILayoutPriority UILayoutPriorityDefaultHigh API_AVAILABLE(ios(6.0)) = 750; // This is the priority level with which a button resists compressing its content.
static const UILayoutPriority UILayoutPriorityDragThatCanResizeScene API_AVAILABLE(macCatalyst(13.0)) = 510; // This is the appropriate priority level for a drag that may end up resizing the window's scene.
static const UILayoutPriority UILayoutPrioritySceneSizeStayPut API_AVAILABLE(macCatalyst(13.0)) = 500; // This is the priority level at which the window's scene prefers to stay the same size. It's generally not appropriate to make a constraint at exactly this priority. You want to be higher or lower.
static const UILayoutPriority UILayoutPriorityDragThatCannotResizeScene API_AVAILABLE(macCatalyst(13.0)) = 490; // This is the priority level at which a split view divider, say, is dragged. It won't resize the window's scene.
static const UILayoutPriority UILayoutPriorityDefaultLow API_AVAILABLE(ios(6.0)) = 250; // This is the priority level at which a button hugs its contents horizontally.
static const UILayoutPriority UILayoutPriorityFittingSizeLevel API_AVAILABLE(ios(6.0)) = 50; // When you send -[UIView systemLayoutSizeFittingSize:], the size fitting most closely to the target size (the argument) is computed. UILayoutPriorityFittingSizeLevel is the priority level with which the view wants to conform to the target size in that computation. It's quite low. It is generally not appropriate to make a constraint at exactly this priority. You want to be higher or lower.
#if !TARGET_OS_IPHONE
typedef float NSLayoutPriority NS_TYPED_EXTENSIBLE_ENUM;
#endif /* !TARGET_OS_IPHONE */
NSLayoutConstraint 類提供的方法:
+ (NSArray<NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(nullable NSDictionary<NSString *, id> *)metrics views:(NSDictionary<NSString *, id> *)views API_AVAILABLE(macos(10.7), ios(6.0), tvos(9.0)); 創(chuàng)建約束, 這個(gè)是使用VF的方式
/* Create an array of constraints using an ASCII-art-like visual format string. The values of the `metrics` dictionary should be NSNumber (or some other type that responds to -doubleValue and returns a double).
*/
+ (NSArray<NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(nullable NSDictionary<NSString *, id> *)metrics views:(NSDictionary<NSString *, id> *)views API_AVAILABLE(macos(10.7), ios(6.0), tvos(9.0));
/* This macro is a helper for making view dictionaries for +constraintsWithVisualFormat:options:metrics:views:.
NSDictionaryOfVariableBindings(v1, v2, v3) is equivalent to [NSDictionary dictionaryWithObjectsAndKeys:v1, @"v1", v2, @"v2", v3, @"v3", nil];
*/
#define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)
NSLAYOUTCONSTRAINT_EXTERN NSDictionary<NSString *, id> *_NSDictionaryOfVariableBindings(NSString *commaSeparatedKeysString, __nullable id firstValue, ...) API_AVAILABLE(macos(10.7), ios(6.0)); // not for direct use
```一般上面的兩個(gè)方法是結(jié)合起來(lái)使員工的的 ,binds的返回值作為第一個(gè)方法的最后一個(gè)參數(shù)```
// 最常用的一個(gè)設(shè)置約束的算法, 這個(gè)只是產(chǎn)生一個(gè)約束
+ (instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullable id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c API_AVAILABLE(macos(10.7), ios(6.0), tvos(9.0));
#if TARGET_OS_IPHONE
@property UILayoutPriority priority; // 約束的優(yōu)先級(jí)屬性
#else
@property NSLayoutPriority priority;
#endif
/* When a view is archived, it archives some but not all constraints in its -constraints array. The value of shouldBeArchived informs the view if a particular constraint should be archived by the view.
If a constraint is created at runtime in response to the state of the object, it isn't appropriate to archive the constraint - rather you archive the state that gives rise to the constraint. Since the majority of constraints that should be archived are created in Interface Builder (which is smart enough to set this prop to YES), the default value for this property is NO.
*/
// 有可能view被歸檔,但是這個(gè)contstraints是否需要?dú)w檔, 而xib的約束是自能歸檔的
@property BOOL shouldBeArchived;
/* accessors
firstItem.firstAttribute {==,<=,>=} secondItem.secondAttribute * multiplier + constant
Access to these properties is not recommended. Use the `firstAnchor` and `secondAnchor` properties instead.
*/
通過(guò)第一個(gè)是關(guān)系的左邊約束, 第二個(gè)是關(guān)系的右邊參數(shù)
@property (nullable, readonly, assign) id firstItem;
@property (nullable, readonly, assign) id secondItem;
@property (readonly) NSLayoutAttribute firstAttribute;
@property (readonly) NSLayoutAttribute secondAttribute;
/* accessors
firstAnchor{==,<=,>=} secondAnchor * multiplier + constant
*/
@property (readonly, copy) NSLayoutAnchor *firstAnchor API_AVAILABLE(macos(10.12), ios(10.0));
@property (readonly, copy, nullable) NSLayoutAnchor *secondAnchor API_AVAILABLE(macos(10.12), ios(10.0));
@property (readonly) NSLayoutRelation relation; // 關(guān)系枚舉變量值
@property (readonly) CGFloat multiplier; // 倍率, 多少倍率
/* Unlike the other properties, the constant may be modified after constraint creation. Setting the constant on an existing constraint performs much better than removing the constraint and adding a new one that's just like the old but for having a new constant.
*/
@property CGFloat constant; // 有關(guān)的常量值 , 這個(gè)可以使之新的值
/* The receiver may be activated or deactivated by manipulating this property. Only active constraints affect the calculated layout. Attempting to activate a constraint whose items have no common ancestor will cause an exception to be thrown. Defaults to NO for newly created constraints. */
@property (getter=isActive) BOOL active API_AVAILABLE(macos(10.10), ios(8.0));
/* Convenience method that activates each constraint in the contained array, in the same manner as setting active=YES. This is often more efficient than activating each constraint individually. */
// 設(shè)置約束有效
+ (void)activateConstraints:(NSArray<NSLayoutConstraint *> *)constraints API_AVAILABLE(macos(10.10), ios(8.0));
/* Convenience method that deactivates each constraint in the contained array, in the same manner as setting active=NO. This is often more efficient than deactivating each constraint individually. */
// 設(shè)置約束失效
+ (void)deactivateConstraints:(NSArray<NSLayoutConstraint *> *)constraints API_AVAILABLE(macos(10.10), ios(8.0));
/// 一個(gè)約束的唯一標(biāo)識(shí), 可以在調(diào)試的時(shí)候設(shè)置identifier, 有利于調(diào)試
@property (nullable, copy) NSString *identifier API_AVAILABLE(macos(10.7), ios(7.0));
/*
UILayoutSupport protocol is implemented by layout guide objects
returned by UIViewController properties topLayoutGuide and bottomLayoutGuide.
These guide objects may be used as layout items in the NSLayoutConstraint
factory methods.
*/
@class NSLayoutYAxisAnchor, NSLayoutDimension;、
// 這個(gè)協(xié)議,不知道有什么作用、?
@protocol UILayoutSupport <NSObject>
@property(nonatomic,readonly) CGFloat length; // As a courtesy when not using auto layout, this value is safe to refer to in -viewDidLayoutSubviews, or in -layoutSubviews after calling super
// 錨點(diǎn), 只讀的內(nèi)容
/* Constraint creation conveniences. See NSLayoutAnchor.h for details.
*/
@property(readonly, strong) NSLayoutYAxisAnchor *topAnchor API_AVAILABLE(ios(9.0));
@property(readonly, strong) NSLayoutYAxisAnchor *bottomAnchor API_AVAILABLE(ios(9.0));
@property(readonly, strong) NSLayoutDimension *heightAnchor API_AVAILABLE(ios(9.0));
@end
MASUtilities.h 這個(gè)文件: 定義了一個(gè)mac和ios的統(tǒng)一的聲明 , MASAttachKeys 綁定的方法 , hash值的算法, _MASBoxValue 這個(gè)用來(lái)獲取對(duì)應(yīng)的值
#define NS_NOESCAPE CF_NOESCAPE 關(guān)于這個(gè)定義
[escape 的介紹](https://awesome-tips.github.io/2017/01/01/__attribute__((noescape))%E5%B1%9E%E6%80%A7.html)
[swift 和OC中的escape](http://www.lxweimin.com/p/33707bd27500)
簡(jiǎn)寫的分類 ,下一次我們也可以這樣去寫,不過(guò)一般有前綴
// 常見的拼接代碼
#define MAS_ATTR_FORWARD(attr) \
- (MASViewAttribute *)attr { \
return [self mas_##attr]; \
}
下面是主要的幾個(gè)類:


_______
有了山上面的基本基礎(chǔ)內(nèi)容, 我們可以看一下源碼了;
MASLayoutConstraint 這個(gè)類就是NSLayoutContraint類, 這個(gè)增加了一個(gè)key用來(lái)調(diào)試,其實(shí)現(xiàn)在系統(tǒng)就有了Identifier的;
MASConstraint 設(shè)置約束最為基本的類; 包括基本的元素:方位、不等式關(guān)系、偏差距離
MASCompositeConstraint: MASConstraint 這個(gè)是上面元素的組合方式,一般for循環(huán)多個(gè)
MASViewConstraint:MASConstraint 就是設(shè)置一個(gè)約束的最基本的單元: 又有了約束的所有的基本變量
>```firstViewAttribute(第一個(gè)屬性)、secondViewAttribute(第二個(gè)屬性)、installedView(安裝的View)、 layoutConstraint(布局約束)、layoutRelation(等式關(guān)系)、layoutPriority(權(quán)限優(yōu)先級(jí)) layoutConstant(約束設(shè)置的敞亮) hasLayoutRelation(是否已經(jīng)有了不等式關(guān)系)mas_key(調(diào)試的key) useAnimator```
MASViewAttribute : view + layout(方位) 描述一個(gè)約束的一邊,不等式一邊的內(nèi)容
MASConstraint+Private : 這個(gè)是聲明了一些方法給庫(kù)里面的其他文件調(diào)用;
MASConstraintMaker 約束工廠: NSObject ,最為主要的調(diào)用, 組織對(duì)應(yīng)的內(nèi)容, 然后讓MASViewConstraint 進(jìn)行設(shè)置自己的約束內(nèi)容
NSLayoutConstraint+MASDebugAdditions 測(cè)試的分類
View+MASShorthandAdditions // 便利使用方法
NSArray+MASShorthandAdditions // 擴(kuò)展的遍歷使用方法
uiviewController 里面的一些簡(jiǎn)單使用
MASConstraintMaker 關(guān)鍵方法, 添加單個(gè)約束以及組合約束
maker中調(diào)用安裝約束
MASViewConstraint 類的安裝基本方法
- (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;
}
// 獲取不等式兩邊的內(nèi)容
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;
// 設(shè)置約束
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;
}
// 獲取安裝的view, 這個(gè)安裝的View,兩個(gè)View的公共最近的View
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];
}
}
// 查找對(duì)應(yīng)的類似的方法
- (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;
}
// 查找兩個(gè)View的最近公共父View , 這個(gè)還是使用了這個(gè)View while循環(huán)遍歷,相對(duì)for好一點(diǎn)
- (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;
}
延伸 : 看一下工廠的設(shè)計(jì)模塊式;
簡(jiǎn)單工廠: 簡(jiǎn)單工廠模式
工廠方法: 簡(jiǎn)單工廠方法
抽象工廠:這個(gè)內(nèi)容看看有什么區(qū)別?