iOS学习笔记--界面自动布局总结

本文我们将提到:

1、autolayout(storyboard与xib)

2、autolayout与VFL(代码布局)

3、第三方框架(OC版本的Masonry与Swift版本的SnapKit)

下面是个人使用各种布局的过程及遇到的问题,进行记录并在结尾作下总结:

1、storyboard与xib布局介绍

实际上Xcode提供的可视化布局是根据开发者设定的约束关系,自动生成NSLayoutConstraint对象实现界面的布局,跟通过代码创建NSLayoutConstraint对象本质上是一样的操作。


storyboard截图

通过图片简单讲一下使用方式,添加constraint的方式:

(1)、通过右键选中一个控件,拖到另一个控件上面,此时可以选定两个视图之间的关系

(2)、通过点击右下角,Add New Constraints,注意四个方向的的参数,是指对应方向上跟最近的控件之间的距离 ,如果没有其他控件时,则表示该方向上跟父视图之间的距离。选中Aspect Ratio时,表示固定当前Width跟Height的比例,可以进行编辑

注意到constraint对应的是一个控件对另一个控件之间的约束关系(1对1的关系,可以自己指向自己,表示宽高比例关系),下面列举各种关系进行分析:


约束关系选项图

(1)、Horizontal Spacing与Vertical Spacing :表示两个控件之间的水平距离与垂直距离(举例:水平方向上 spacing = minX2 - maxX1)

(2)、Top与Bottom :表示控件的顶部与另一个控件垂直方向对齐(顶部或者底部对齐,举例 top minY2 - minY1 = 0)

(3)、Leading与Trailing :表示两个控件水平方向对齐(默认是左和右对齐,部分地区是右和左,举例leading  minX2 - minX1 = 0)

(4)、Center Vertically与Center Horizontally : 表示两个控件垂直或者水平的中线对齐(举例水平方向:centerX1 = centerX2)

(5)、Equal Widths与Equal Heights : 表示两个控件的宽相等与高相等(举例水平方向:width1 - width2 = 0),Aspect Ratio表示一个控件的宽高比等与另一个控件的宽高比(width1 / height1 = width2 / height2)

(6)、Baseline :表示基准线,每个堆栈视图都有自己的属性集,它们定义了堆栈视图如何布局它的内容。在布局中不常用,所以不作详细介绍

通过选中图中的view inspector,点击Edit可以对constraint编辑,约束关系包括>=,<=,= 的加减(通过设置正负数)关系,还有multiplier的倍数关系,计算时先按倍数计算,再进行加减计算,Priority表示约束优先级,比如在不等式关系出现的时候,先进行优先级较高的约束。需要动态改变约束关系时,可以将constraint对象作为属性被持有,通过代码对其constant属性进行修改,刷新约束,可以通过-setNeedsUpdateConstraints和-layoutIfNeeded两个方法来刷新约束的改变,使UIView重新布局(适用于xib、storyboard和手写代码布局,常用于改变约束时实现动画效果)。

注意点:

(1)、storyboard和xib的使用基本一致,但是storyboard可以通过添加container view的方式为viewController添加subViewController,同时在storyboard中,可以为UITableView和UICollectionView设置静态单元格,还可以直接添加header、footer或cell进行自定义(相当于Register Reuse View),缺点是这些自定义的控件不能使用在别的UITableView和UICollectionView中,而xib中需要通过添加UITableViewController才能UICollectionViewController才能完成这些操作

(2)、一般控件进行约束时,只需要确定控件的坐标位置(水平和垂直两个约束)和宽高,而UIScrollView除了设置这些约束外,还需要固定自己的Content Size,一般可以通过添加中间视图跟UIScrollView约束为上下左右都对齐,再设置中间视图的宽高,以此来固定其Content Size

2、autolayout与VFL布局介绍

(1)、这里主要讲解iOS6.0之后,系统引入的NSLayoutConstraint对象进行布局,与xib布局原理相似,实际上也是分别创建控件之间的关系对控件进行约束,从而确定其位置与大小。


约束关系图

图中用到了一个核心公式,任何两个控件间的约束都可以通过这个公式算出

view1.property1 =(view2.property2 * multiplier)+ constant value

multiplier和constant 就是向量系数和偏移量(计算时先进行系数计算,再作偏移计算)

创建过程及参数设定跟xib中连线进行编辑大同小异,但是代码创建约束对象可以通过循环的方式进行批量创建,再运用下面的方法对控件添加约束对象

- (void)addConstraint:(NSLayoutConstraint *)constraintNS_AVAILABLE_IOS(6_0);

- (void)addConstraints:(NSArray<__kindof NSLayoutConstraint *> *)constraintsNS_AVAILABLE_IOS(6_0);

(2)、 相比于以上方式创建的NSLayoutConstraint进行两个控件之间的关系,VFL(可视化格式语言)更多的关心垂直与水平方向上,尽可能地将多个控件约束关系描述清楚(实际上约束关系的转换也是系统补全之后,添加到控件上面),下面是VFL的核心方法

+ (NSArray<__kindofNSLayoutConstraint*> *)constraintsWithVisualFormat:(NSString*)format options:(NSLayoutFormatOptions)opts metrics:(nullableNSDictionary *)metrics views:(NSDictionary *)views;

format参数: 这个是具体布局的字符串,形如@"V:|-10-[view1(50.0)]-20-[view2(50.0)]",语句依次解释为,在垂直方向,view1和view2的高度都为50,view1的minY距离父视图10,view2的minY距离view1的maxY20

option参数: 这个参数是一个可复选的参数,  主要用来布局view的对齐方式;

metrics参数: 这个参数是替换VFL字符串中的变量用的,  如果这么写:@{@"topMargin":@10}则第一条中的布局可以替换成下面的写法:@"V:|-10-[view1(50.0)]-20-[view2(50.0)]"其实就是替换字符串中的变量使用;

views参数: 这个参数是用来存储本条VFL语句中所有用到的View,  可以直接创建字典@{@"view1":view1, @"view2":view2}对于这个参数苹果有一个特定的宏,NSDictionaryOfVariableBindings(),因此也可为 NSDictionaryOfVariableBindings(view1, view2);在Swift中,已经没有宏的概念,所以需要通过创建字典的方式

VFL的详细语法 可以在官方文档中 搜 “Visual Format” 就能搜到,下面简单介绍一下

|: 表示父视图-: 表示距离

V: 表示垂直

H: 表示水平

>=: 表示视图间距、宽度和高度必须大于或等于某个值 

 <= :表示视图间距、宽度和高度必须小宇或等于某个值 

 == :表示视图间距、宽度或者高度必须等于某个值

@: 优先级 最大为 1000

注意点:

(1)、为了避免和系统生成的自动伸缩的约束不冲突,需要先将控件的属性translatesAutoresizingMaskIntoConstraints设置为NO,系统默认这个属性是yes。

(2)、Swift关于使用NSLayoutConstraint的居中方式未能生效的问题,采用以下方式解决

let downPartView = UIView.init()

self.bgView?.addSubview(downPartView)     

downPartView.translatesAutoresizingMaskIntoConstraints = false

downPartView.addSubview(self.slider!)

let dic:[String : Any] = ["bgView":self.bgView!, "slider":self.slider!, "tipLabel":self.tipLabel!,"downPartView":downPartView]

self.bgView?.addConstraints(NSLayoutConstraint.constraints(withVisualFormat:"V:[slider(60)]-(<=1)-[downPartView]", options: .alignAllCenterX, metrics:nil, views: dic))

self.bgView?.addConstraints(NSLayoutConstraint.constraints(withVisualFormat:"H:[slider(380)]-(<=1)-[downPartView]", options: .alignAllCenterY, metrics:nil, views: dic))

3、第三方框架

常用的第三方框架包括OC版本的Masony和Swift版本的SnapKit

Masonry下载地址:https://github.com/Masonry/Masonry

SnapKit下载地址:https://github.com/SnapKit/SnapKit

两个框架在使用方式上大同小异,通过代码块中传入的约束对象进行设置,下面通过代码简单介绍SnapKit的使用方法

self.leftImageView?.snp.makeConstraints({ (make) in

            make.left.equalTo(self).offset(6) //表示左边距离父视图6

            make.top.equalTo(self).offset(6) //表示顶部距离父视图6

            make.bottom.equalTo(self).offset(-6) //表示底部距离父视图6(控件maxY小于父视图的maxY,即控件高度没有超过          父视图)

            make.width.equalTo(leftImageView?.snp.height).multipliedBy(16/9.0).offset(-6) //表示控件自身宽高比为16/9,确定宽度之后,再作-6的偏移(这里的约束需要确定高度才能进行约束,不然会因为height属性为空而报错)

//    所有约束的优先级跟在语句最后面,数值越大优先级越高( .priority(666) 优先级小于 .priority(999))

        }) 

通过上面的大致简单布局我们对SnapKit有了一个基本的了解,实际上也是两个控件之间的约束关系,需要掌握相关属性才能熟练使用,那么, 它的布局属性是怎么来的呢?和原生的布局类有什么关联? 下面看一个SnapKit的布局属性表


布局属性表对比

从表中,我们知道,Snapkit的布局属性都是源自于系统的NSLayoutAttribute,那么,NSLayoutAttribute是个什么呢?其实,它在swift中是一个枚举,内部列举了很多布局属性诸如top、left、leading、centerX等,SnapKit的布局属性与它们都存在一一的对应关系。

注意点:makeConstraints是添加约束,updateConstraints是更新约束,重复约束语句会进行覆盖而不是新添加,remakeConstraints是移除现在有的所有约束,必须重新添加

4、总结

(1) 日常开发中,storyboard与xib的优点在于通过可视化特性可以对界面布局进行实时预览,同时对大部分控件属性的设置极为方便,可读性较好,但是通过storyboard进行界面布局不可避免的使该文件过于臃肿,打开该文件极为缓慢,通过xib进行布局时,会新增大量xib文件,导致项目编译变慢。

(2) 手写代码布局可以避免文件加载和编译过慢的问题,但是对控件的布局比较繁琐,约束语句与属性的设置增加了不少的代码量,优点是可以通过循环的方式进行批量操作,复用性和维护性较好。

(3) 建议多关注与使用优秀的第三方开源框架,熟悉其工作原理与实现方式,可以提高开发效率同时增加自己对系统工作原理的理解。

ps:建议或讨论可联系qq69787407

你可能感兴趣的:(iOS学习笔记--界面自动布局总结)