CGRectMake(<#CGFloat x#>, <#CGFloat y#>, <#CGFloat width#>, <#CGFloat height#>)
复制代码
设置view.frame会当即生效,但明显这种方式会将视图固定死,若是要在不一样尺寸的屏幕上都显示完美比较难,可能须要些几套UI或者设置比例。算法
使用Autoresizing来适配父视图bounds发生改变的状况 数组
使用AutoLayout自动布局技术 苹果自iOS 6开始引入AutoLayout自动布局技术,通过一系列的优化,开发效率大大提升,苹果官方也推荐使用AutoLayout自动布局技术来布局UI界面。使用AutoLayout布局时,能够指定一系列的视图约束,如:高度、宽度。整个界面上的全部约束组合在一块儿就明确的定义了整个界面的布局。bash
NSLayoutConstraint *layoutConstraint = [NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:view2 attribute:NSLayoutAttributeRight multiplier:1 constant:20.f];
复制代码
公式:函数
view1.attribute = view2.attribute * multiplier + constant;
复制代码
API:布局
- (void)addConstraint:(NSLayoutConstraint *)constraint;
- (void)addConstraints:(NSArray *)constraints;
复制代码
约束添加规则:性能
view.translatesAutoresizingMaskIntoConstraints = NO;
只要直到了这些数据就能够固定一个视图了。 使用view.frame就是最直接的方式,而Auto Layout中大部分的约束都是描述性语言,表示视图间的相对距离。如上图:优化
A.left = superView.left + 20;
A.top = superView.top + 25;
A.width = 100;
A.height = 100;
B.left = A.left + 20;
B.top = A.top;
B.width = A.width;
B.height = A.height;
复制代码
咱们经过求解👆的八元一次方程组,是能够将A、B两个视图布局所须要的信息(x、y、width、height)求解出来的。实质上由AutoLayout引擎在运行时求解上述的方程组,最终使用frame来绘制视图。所以也能够知道AutoLayout不能当即生效的(systemLayoutSizeFitting能够获取自动布局结果),不一样于直接设置frame。动画
Auto Layout 其实就是对 Cassowary(将布局问题抽象成线性等式和不等式约束进行求解) 算法的一种实现。ui
使用AutoLayout布局,就是让整个界面上的全部约束(线性等式/不等式)在一块儿明确的、无冲突定义整个界面的布局。(当发生冲突时,AutoLayout会先尝试打破一些优先级低的约束,尽可能知足优先级高的约束)spa
使用AutoLayout的过程是须要先求解方程组的,获得结果再设置frame。这样的时间复杂度就远高于直接设置frame了。当视图层级很深或者对性能要求较高的页面使用自动布局的性能就差强人意了。
NSLayoutConstraint *layoutConstraint1 = [NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:view2 attribute:NSLayoutAttributeRight multiplier:1 constant:20.f];
NSLayoutConstraint *layoutConstraint2 = [NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:view2 attribute:NSLayoutAttributeTop multiplier:1 constant:0];
[view1 addConstraints:@[layoutConstraint1,layoutConstraint2]];
复制代码
NSLayoutConstraint 布局约束对象
NSLayoutAttribute 布局属性对象(左右上下等)
NSLayoutRelation 布局关系对象(>、<、=等待)
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(view2.mas_right).offset(20);
make.top.equalTo(view2.mas_top);
}];
复制代码
能够看出两种方式是类似,Masonry的语言最后能够被转化成AutoLayout代码。
设计到一些关键词:
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
// 1. 要使用AutoLayout,须要关闭autoSizing
self.translatesAutoresizingMaskIntoConstraints = NO;
// 2. 建立ConstraintMaker对象(它负责建立相关约束)
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
// 3. 执行block,即建立各类约束:
// constraintMaker.left.equalTo(view2.mas_right).offset(20);
// constraintMaker.top.equalTo(view2.mas_top);
block(constraintMaker);
return [constraintMaker install];// 添加约束
}
复制代码
MASConstraintMaker
// step 1
- (MASConstraint *)left {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeLeft];
}
// step 2
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}
// step 3
// MASViewAttribute对一个视图和它的布局属性的封装
// MASViewConstraint 一条 约束(view.left和view2.right)封装了两个MASViewAttributes:firstViewAttribute和secondViewAttribute,和以后的约束关系、offset等数值
// 返回约束对象MASViewConstraint
- (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]) {
// constaint 存在(left),newConstraint(right)
//采用了链式约束(make.left.right)就用组合约束(left.right)替换前面的旧约束(left),即用left和right的组合约束替换left约束
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;
}
复制代码
返回的MASViewConstraint对象继承自MASConstraint,这个约束对象仍然有left、top等布局属性。子类继承父类属性,而且它重写了addConstraintWithLayoutAttribute:方法。
// MASConstraint.m 父类
- (MASConstraint *)top {
return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
}
// MASViewConstraint.m 子类
- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
NSAssert(!self.hasLayoutRelation, @"Attributes should be chained before defining the constraint relation");
// 它的代理就是MASConstraintMaker对象
// 可是constraint不是nil而是当前的约束如make.left.top,当前约束是left,进入方法建立的是top约束,重回step3
return [self.delegate constraint:self addConstraintWithLayoutAttribute:layoutAttribute];
}
// 在约束数组中找到旧约束,并用新的组合约束替换
- (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];
}
复制代码
所以Masonry可使用链式调用,如:make.left.top;且布局属性返回的都是MASConstraint对象,它还有布局关系属性equalTo(view2.right)等。 经过添加布局关系属性和后边的offset等,将约束对象MASViewConstraint的约束条件补充完整了。
这样一条条完整的约束就建立完成了。接下来须要将这些完整的约束添加到view上。即又回到了mas_makeConstaints方法上了,它最终执行[constraintMaker install]
- (NSArray *)install {
if (self.removeExisting) {//remake 删除原有已经设置的全部约束
NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];
for (MASConstraint *constraint in installedConstraints) {
[constraint uninstall];
}
}
// self.constaints是以前方法中出现的约束数组:存储关于这个视图的全部约束语句,让每一个约束都执行install方法
NSArray *constraints = self.constraints.copy;
for (MASConstraint *constraint in constraints) {
constraint.updateExisting = self.updateExisting;// 是不是要updateConstraints
[constraint install];
}
[self.constraints removeAllObjects];
return constraints;
}
复制代码
[constraint install]方法取出该约束对象中的全部约束条件,建立了约束对象MASLayoutConstraint,它继承自系统的NSLayoutConstaint:
MASLayoutConstraint *layoutConstraint
= [MASLayoutConstraint constraintWithItem:firstLayoutItem
attribute:firstLayoutAttribute
relatedBy:self.layoutRelation
toItem:secondLayoutItem
attribute:secondLayoutAttribute
multiplier:self.layoutMultiplier
constant:self.layoutConstant];
复制代码
那么这个约束要被添加到哪一个view上呢?
// 查找要被添加约束的installView
// 1. 设置secondView,则按照添加约束规则找出共同父视图
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) { // 2. 未设置secondView,可是当前布局属性是视图width、height。
self.installedView = self.firstViewAttribute.view;
} else { // 3. 以上都不是,则secondView应该默认是当前视图的父视图了
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];
}
复制代码
equalTo()和mas_equalTo()
#define mas_equalTo(...) equalTo(MASBoxValue((__VA_ARGS__)))
复制代码
equalTo()参数是id类型,而mas_equalTo()参数能够是id、数值、CGSize等等,它就是一个宏,经过MASBoxValue函数将其中参数转化成id类型。
left和mas_left
综上能够看出: MASConstraintMaker是一个简单工厂类(或者说工厂方法)用来生产MASConstraint约束对象。
外界经过给工厂传递不一样的类型left、right等,从而生产出不一样类型的约束对象(MASViewConstraint和MASCompositeConstraint),利用多态。