iOS开发之自动布局AutoLayout
目录:
1
iOS自动布局简介
2
iOS自动布局AutoLayout(代码)
3
iOS自动布局之VFL
4
iOS自动布局之Xib
5
自动布局开源库Masonry、PureLayout
6
约束动画
iOS自动布局简介 AutoLayout
自动布局背景
在iOS6之前,布局是通过UI控件的Frame属性和Autoresizing Mask来进行UI布局的。AutoLayout则是苹果公司在iOS6推出的一种基于约束的,描述性的布局系统。尤其当iPhone6机型尺寸退出后,自动布局就被越来越多的人所应用。
自动布局重要概念
1
约束:对控件位置和大小的限定条件
2
参照:对控件位置的约束是相对于哪一个视图而言的
自动布局的核心计算公式
obj1.property1 = (obj2.property2 * multiplier) + constant value
tips:obj1的property1属性等于obj2的property2属性乘以multiplier(系数) 再加 constant (常量)。
约束的优先级
约束的priority
属性表示约束的优先级,取值区间在[0,1000],默认是1000,priority
值越大,表示优先级越高,会优先执行,优先级低的约束会在优先级高的约束执行完成之后执行。
约束警告和错误:
在使用storyboard或者xib的时候,约束可能变成黄色的警告或者红色的错误时,说明约束设置的有问题
1
警告:表示控件的frame和设置的约束不符,更新frame或者更新约束进行解决。
2
错误:表示约束设置不完全或者约束之间有冲突,这时只有把约束设置完全或者解决冲突才能解决错误。
添加约束的三条规则:
对于两个同层级View之间的约束关系,应该添加到它们的父View之上。
代码实现AutoLayout
1
先禁用view的autoresizing功能,设置属性为NO
view.translatesAutoresizingMaskIntoConstraints = NO;
2
添加约束之前,保证view添加到了对应的父控件上
3
使用了AutoLayout就不要考虑frame了。
4
NSlayoutConstraint: 一个NSlayoutConstraint
就代表一个约束对象。
/**
* 添加一个约束,其实就是根据公式来计算约束
* obj1.property1 =(obj2.property2 * multiplier)+ constant value
* @param view1 需要添加约束的View
* @param attr1 需要添加的约束(左边、右边、长宽还是。。。)
* @param relation 约束关系(大于、等于还是小于)
* @param view2 参照View(约束是相对于哪个View而言)
* @param attr2 参照View的哪一个参数(左边、右边、长宽还是。。。)
* @param multiplier 系数
* @param c 常量值
*
* @return 返回一个创建好的约束
*/
+ (instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(nullable id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;
5
添加约束的时候,一定要确定添加到对应的item上。
一般view自身的约束包括(宽度、高度、宽高比等)
view之间或者父子view之间的约束,都应该添加到公共父view上
VFL添加约束 (Visual Format Language)
苹果提供的另外一种通过代码添加约束的方式,官方提供了一些资料,但是也不是很全,严格意义上将VFL并不是一门语言,感觉像是一种解析方式,必须按照定义的规则来编写约束,目前还有很多坑,不建议使用。
1
使用VFL创建约束数组:
/**
* 使用VFL语句来创建约束数组
*
* @param format VFL语句
* @param opts 约束类型
* @param metrics VFL语句中用到的具体数值
* @param views VFL语句中用到的控件
*
* @return 返回创建好的约束数组
*/
+ (NSArray<__kindof NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(nullable NSDictionary *)metrics views:(NSDictionary *)views;
2
VFL语句示例
H:[cancelButton(72)]-12-[acceptButton(50)]
canelButton宽72,acceptButton宽50,它们之间间距12
H:[wideView(>=60@700)]
wideView宽度大于等于60point,该约束条件优先级为700(优先级最大值为1000,优先级越高的约束越先被满足)
V:[redBox][yellowBox(==redBox)]
竖直方向上,先有一个redBox,其下方紧接一个高度等于redBox高度的yellowBox
H:|-10-[Find]-[FindNext]-[FindField(>=20)]-|
水平方向上,Find距离父view左边缘默认间隔宽度,之后是FindNext距离Find间隔默认宽度;再之后是宽度不小于20的FindField,它和FindNext以及父view右边缘的间距都是默认宽度。(竖线“|” 表示superview的边缘)
3
VFL代码示例
NSString* vfl = @"H:|-margin-[imageV]-margin-|";//水平约束
NSDictionary* metrics = @{@"margin":@40};//添加约束时会自动解析这个字典
NSDictionary* views = NSDictionaryOfVariableBindings(imageV);
NSArray* arrayH = [NSLayoutConstraint constraintsWithVisualFormat:vfl options:NSLayoutFormatAlignAllCenterY metrics:metrics views:views];
[self.view addConstraints:arrayH];
NSString* vfl1 = @"V:|-100-[imageV(150)]";//高度
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl1 options:NSLayoutFormatAlignAllCenterY metrics:nil views:views]];
4
效果图
5
学习的地址:
http://www.jianshu.com/p/385070898e77
http://blog.csdn.NET/a1152024140/article/details/40823883
http://www.cocoachina.com/industry/20131108/7322.html
可视化实现自动布局之Xib&SB:
前面通过代码实现自动布局可以发现,如果要通过自动布局编写一个复杂的页面,用代码实现的话代码量太大,而且可读性很差,还好苹果为我们提供了一种可视化添加约束的自动布局方式,大大提升了页面布局的效率。
1
可视化布局提供了苹果UIKit中大部分控件,直接拖拽到对应的view上就可以进行自动布局了。
2
给对应的控件添加约束 (实例:宽度240 高度128,水平垂直居中)
3
效果(居中,高度和宽度约束)
自动布局开源库
Mansory
Mansory 是目前最流行的Autolayout第三方框架,让你可以用简单的代码来编写Autolayout,省去了苹果官方繁复的Autolayout代码,大大提升了开发效率。相对于PureLayout
(为UIView添加分类) 来说 Mansory
拥有自己的描述语法,采用更优雅的链式语法封装自动布局,简洁明了 并具有高可读性,而且同时支持iOS和Max OS X。Mansory
github地址:https://github.com/SnapKit/Masonry
1
官方sample code:
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(superview).with.insets(padding);
}];
可以看到Mansory布局的代码方式通过 block实现,看起来代码比较集中和清晰,然后:make edges equalTo superview with insets 。这种通过链式的自然语言,把约束设置好了,看起来很简单。
2
Mansory提供的属性
@property (nonatomic, strong, readonly) MASViewAttribute *left;
@property (nonatomic, strong, readonly) MASViewAttribute *top;
@property (nonatomic, strong, readonly) MASViewAttribute *right;
@property (nonatomic, strong, readonly) MASViewAttribute *bottom;
@property (nonatomic, strong, readonly) MASViewAttribute *leading;
@property (nonatomic, strong, readonly) MASViewAttribute *trailing;
@property (nonatomic, strong, readonly) MASViewAttribute *width;
@property (nonatomic, strong, readonly) MASViewAttribute *height;
@property (nonatomic, strong, readonly) MASViewAttribute *centerX;
@property (nonatomic, strong, readonly) MASViewAttribute *centerY;
@property (nonatomic, strong, readonly) MASViewAttribute *baseline;
@property (nonatomic, strong, readonly) MASViewAttribute *(^attribute)(NSLayoutAttribute attr);
#if TARGET_OS_IPHONE
@property (nonatomic, strong, readonly) MASViewAttribute *leftMargin;
@property (nonatomic, strong, readonly) MASViewAttribute *rightMargin;
@property (nonatomic, strong, readonly) MASViewAttribute *topMargin;
@property (nonatomic, strong, readonly) MASViewAttribute *bottomMargin;
@property (nonatomic, strong, readonly) MASViewAttribute *leadingMargin;
@property (nonatomic, strong, readonly) MASViewAttribute *trailingMargin;
@property (nonatomic, strong, readonly) MASViewAttribute *centerXWithinMargins;
@property (nonatomic, strong, readonly) MASViewAttribute *centerYWithinMargins;
经常用NSLayoutConstraint
编写约束的同学可能感觉比较熟悉,这里的属性的命名方式和NSLayoutConstraint
类下的NSlayoutAttribute
枚举一致,这样你就会发现,Masonry其实就是把我们系统的NSLayoutConstraint
类等Aotulayout布局相关进行了封装,曝露出比较简单易懂(链式的自然语言)的Aotulayout接口。
typedef NS_ENUM(NSInteger, NSLayoutAttribute) {
NSLayoutAttributeLeft = 1,
NSLayoutAttributeRight,
NSLayoutAttributeTop,
NSLayoutAttributeBottom,
NSLayoutAttributeLeading,
NSLayoutAttributeTrailing,
NSLayoutAttributeWidth,
NSLayoutAttributeHeight,
NSLayoutAttributeCenterX,
NSLayoutAttributeCenterY,
NSLayoutAttributeBaseline,
NSLayoutAttributeLastBaseline = NSLayoutAttributeBaseline,
NSLayoutAttributeFirstBaseline NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeLeftMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeRightMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeTopMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeBottomMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeLeadingMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeTrailingMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeCenterXWithinMargins NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeCenterYWithinMargins NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeNotAnAttribute = 0
};
3
Mansory示例代码
UIImageView* imageV = [[UIImageView alloc]init];
imageV.image = [UIImage imageNamed:@"img_tip"];
imageV.contentMode = UIViewContentModeScaleAspectFill;
imageV.clipsToBounds = YES;
[self.view addSubview:imageV];
__weak __typeof (&*self)weakSelf = self;
[imageV mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(weakSelf.view).offset(40);
make.centerY.equalTo(weakSelf.view);
make.right.mas_equalTo(weakSelf.view).offset(-40);
make.height.equalTo(imageV.mas_width * 3.0/4);
}];
UILabel* titleL = [[UILabel alloc]init];
titleL.text = @"Masonry自动布局";
titleL.textAlignment = NSTextAlignmentCenter;
titleL.font = [UIFont systemFontOfSize:15];
titleL.textColor = [UIColor blackColor];
[self.view addSubview:titleL];
[titleL mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(imageV);
make.bottom.equalTo(imageV.mas_top).with.offset(-20);
}];
4
效果图
可以看到,Mansory虽然也谢了不少代码,但是看起来简单易懂,在实际编写约束中,可以用Mansory作为xib的辅助工具,编写一些动态的约束。
tips:
1
Mansory添加约束的三个函数
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block;
- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block;
- (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block;
2
Mansory的一些小细节
mas_equalTo
和equalTo
: 默认情况下mas_equalTo
有自动包装功能,比如自动将20包装为@20。equalTo没有自动包装功能。如果添加了下面的宏,那么mas_equalTo和equalTo就没有区别:
// 注意:这个宏一定要添加到#import "Masonry.h"前面
#define MAS_SHORTHAND_GLOBALS
mas_width
和width
: 默认情况下
width是make对象的一个属性,用来添加宽度约束用的,表示对宽度进行约束。mas_width是一个属性值,用来当做equalTo的参数,表示某个控件的宽度属性。如果添加了下面的宏,mas_width也可以写成width:
#define MAS_SHORTHAND
mas_height、mas_centerX以此类推。
自动布局第三方库之 PureLayout
相对于Mansory来说,PureLayout就是一个轻量级的自动布局框架,没有自己再去封装一层语法,而是直接通过为UIView添加分类的方式实现,内部也是直接调用NSLayoutConstraint添加约束。
1
官方示例代码
下面是通过PureLayout给view添加约束(view1的顶部等于view2的底部)
[view1 autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:view2]
如果不使用PureLayout,下面是通过使用Apple's Foundation API 达到相同的效果:
[NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:view2 attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0];
很多PureLayout的APIS默认帮你创建了多个约束(约束数组),帮助你编写可读性高的布局代码。
// 2 constraints created & activated in one line!(创建了水平居中和垂直居中两个约束)
logoImageView.autoCenterInSuperview()
// 4 constraints created & activated in one line!(创建上左下右约束数组)
textContentView.autoPinEdgesToSuperviewEdgesWithInsets(UIEdgeInsets(top: 20.0, left: 5.0, bottom: 10.0, right: 5.0))
PureLayout总是返回你创建的约束/约束数组,方便你进行操作:
//返回单个约束
NSLayoutConstraint* constraint = [view1 autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:view2];
//返回约束数组
NSArray* constraints = [view1 autoSetDimensionsToSize:CGSizeMake(100, 100)];
PureLayout supports all Auto Layout features including inequalities, priorities, layout margins, identifiers, and much more. It's a comprehensive, developer-friendly way to use Auto Layout.
2
PureLayout Demo:
UIImageView* imageV = [[UIImageView alloc]init];
imageV.image = [UIImage imageNamed:@"img_tip"];
imageV.contentMode = UIViewContentModeScaleAspectFill;
imageV.clipsToBounds = YES;
[self.view addSubview:imageV];
[imageV autoCenterInSuperview];
[imageV autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:40];
[imageV autoPinEdgeToSuperviewEdge:ALEdgeRight withInset:40];
[imageV autoMatchDimension:ALDimensionHeight toDimension:ALDimensionWidth ofView:imageV withMultiplier:0.75];
UILabel* titleL = [[UILabel alloc]init];
titleL.text = @"PureLayout自动布局";
titleL.font = [UIFont systemFontOfSize:17];
titleL.textColor = [UIColor blackColor];
titleL.textAlignment = NSTextAlignmentCenter;
[self.view addSubview:titleL];
[titleL autoPinEdge:ALEdgeBottom toEdge:ALEdgeTop ofView:imageV withOffset:-20];
[titleL autoPinEdge:ALEdgeLeft toEdge:ALEdgeLeft ofView:imageV];
[titleL autoPinEdge:ALEdgeRight toEdge:ALEdgeRight ofView:imageV];
3
效果图
约束动画
在执行动画时记得调用以下方法:
//在修改了约束之后,只要执行下面代码,约束就能做出动画效果
[UIView animateWithDuration:0.5 animations:^{
[self.view layoutIfNeeded];
}];
送上本文所有demo的地址:
1、UITableView-FDTemplateLayoutCell 自动计算布局
2、masonary进行自动布局,然后再实现自动计算cell行高
问题解疑:
1、preferredMaxLayoutWidth
1)为什么设置这个属性: 首先UILabel显示几行跟label的最大布局宽度有关
2)storyboard:
1)默认没有勾选这个选项,因为系统会自动计算(但是官方说只在ios8
之后有用,所以ios7没用)
2)勾选:有时候会发现勾选后的值不对,所以建议不要管
3)在代码中设置(最靠谱)
3)iOS7以及以下的操作系统上,UILabel显示多行文本是又不足的,你需要设置UILabel的
preferredMaxLayoutWidth为一个固定值才能显示多行文本。在iOS8以后就不再需要设置这个了
4)根据实践结果,很多时候UILabel多行不适配的时候就是这个属性设置错误或者
没有设置导致的,尽量去设置。
2、算出autolayout的最终高度,但是返回高度需要加1.0
1)原因:由于cell分割线的问题,cell的高度比contentview的高度多1,所以需要加上
3、content Hugging(通俗来讲就是防止控件变大)和 content Compression Resistance(防止控件变小)
1)这两个属性是针对有intrinsic size属性的控件,也就是拥有intrinsic size属性的控件知道自己(自己可以计算)的大小,例如一个lable,知道自己的text,和font就可以自己计算自己的大小
2)对应代码中的API(就是设置优先级)
contentCompressionResistancePriorityForAxis
setContentCompressionResistancePriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis
contentHuggingPriorityForAxis:(UILayoutConstraintAxis)axis
setContentHuggingPriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis
3)Hugging priority确定view有多大的优先级组织自己变大,,Compression Resistance priority确定有多大的优先级阻止自己变小,其实content Hugging就是维持当前view在它的optimal size(intrinic size),相当于给view添加一个width约束,这个约束防止view变大。content Hugging优先级默认是250,Content Compression就是维持view在他的optimal size(intrinic size),相当于添加一个width约束,防止view变小,compression priority默认是750
重点:(设置优先级就是要先保证优先级高的约束)