iOS开发之 自动布局

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之上。


对于两个不同层级View之间的约束关系,应该添加到它们最近的共同父View上。

对于有层次关系的两个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<NSString *,id> *)metrics views:(NSDictionary<NSString *, id> *)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

<a href = "https://github.com/SnapKit/Masonry"> Mansory </a> 是目前最流行的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_equalToequalTo: 默认情况下mas_equalTo有自动包装功能,比如自动将20包装为@20。equalTo没有自动包装功能。如果添加了下面的宏,那么mas_equalTo和equalTo就没有区别:

// 注意:这个宏一定要添加到#import "Masonry.h"前面
#define MAS_SHORTHAND_GLOBALS

mas_widthwidth : 默认情况下
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

重点:(设置优先级就是要先保证优先级高的约束)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,186评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,858评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,620评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,888评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,009评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,149评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,204评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,956评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,385评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,698评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,863评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,544评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,185评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,899评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,141评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,684评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,750评论 2 351

推荐阅读更多精彩内容