前言
自适应、适配、布局这几个关键词一直伴随着iOS开发,从以前的单一尺寸屏幕,到现在的多尺寸屏幕,Apple一直致力于让开发人员尽可能少在这些事上耗费过多的精力,所以Apple在2012年推出了Auto Layout特性,2014年又推出了Adaptive Layout、Size Classes,2015年又推出了Stack View。这些无一不是我们开发者做适配的利器。今天就让我们看看StackView是怎么一回事。
注:示例开发环境为Xcode7 Beta 2
我们先来看看下面这个常见的布局:
这是一种很常见的汉堡布局,如果我们需要让它自适应不同尺寸的屏幕,我们要添加哪些约束呢?
看看图中这些约束,你们会不会感到莫名的烦躁呢,至少我是的。更让人抓狂的是,如果需要在中间再添加一个长方形,那么你要修改关联的好几个约束,有没有掀桌的冲动呢?
不过,我们开发人员总是幸运的,Apple在Xcode7中添加了一个新的特性StackView,它能完美的解决这个闹心的问题。
StackView其实一个视图容器,不过它会对它的子视图根据一定规则自动布局,将子视图按栈的排列方式进行布局,并且有几个主要的属性:
方向
StackView有水平和垂直两个方向的布局模式:
间隔
StackView可以设置子视图之间的间隔:
对齐方式
StackView可以设置子视图的对齐方式(水平方向和垂直方向的该属性值有所区别):
- Fill:子视图填充StackView。
- Leading:靠左对齐。
- Trailing:靠右对齐。
- Center:子视图以中线为基准对齐。
- Top:靠顶部对齐。
- Bottom:靠底部对齐。
- First Baseline:按照第一个子视图中文字的第一行对齐。
- Last Baseline:按照最后一个子视图中文字的最后一行对齐。
分布比例
StackView可以设置子视图的分布比例:
- Fill:默认分布方式。
- Fill Equally:子视图的高度或宽度保持一致。
- Fill:Proportionally:StackView自己计算出它认为合适的分布方式。
- Equal Spacing:子视图保持同等间隔的分布方式。
- Equal Centering:每个子视图中心线之间保持一致的分布方式。
一切让示例来说话,在这篇文章中我们将逐步开发一个电影影评的应用来详细讲解如何使用StackView。
第一个StackView
打开Xcode,新建一个项目,命名为MovieRate,语言选择Swift。打开Storyboard,删掉当前的ViewController视图,重新拖入TableViewController视图,然后内嵌一个NavigationController,配色大家随意:
ViewController.swfit
文件,新建一个UITableViewController名为MovieListTableViewController.swift
,以及一个UITableViewCell名为MovieTabelViewCell.swift
:
这两个文件的作用不言而喻:
Movie.plist
,当做TableView的数据源:
MovieListTableViewController
,给MovieTableViewCell
设置一个标示符MovieCell
,往MovieTableViewCell
中拖入两个UIImageView和一个UILabel,不用理会他们的位置和尺寸:
按着command键,同时选中这三个视图,然后点击右下角的Stack按钮:
这时,我们便创建的一个UIStackView,包含刚才选中的那三个视图,或者说我们将选中的那三个视图组合成了一个UIStackView:
选中这个StackView,打开右侧的属性编辑窗口,可以看到在文章前言中讲过的四个主要属性:
虽然被包含的两个的UIImageView和UILabel可以由UIStackView的属性设置布局,但是就UIStackView本身而言,我们还是需要设置它的相关约束:
这两个UIImageView一个展示电影封面图片,一个展示评分,我们在StoryBoard中设置它们的image属性,并将Model设置为Aspect Fit,看看效果:
理想总是很丰满,现实总是很骨感,设置了图片后并不是我们想要的结果,图片的大小以及与文字的间隔都有问题。而且还出现了约束错误:
意思是StackView里的这三个内容没有指定x坐标的约束或者宽度的约束。但是作为StackView的子视图,它们的布局情况是由StackView通过各个属性设置的,断然不能在自身上添加约束,那么如何解决这个问题呢?其实很简单,我们选中中间的UILabel,然后打开右侧的尺寸属性面板,找到这两栏:
Content Hugging Priority是水平、垂直方向的拉伸优先级,我们将Horizontal设置为250。这样就可以告诉StackView,中间的UILabel水平拉伸的优先级是最高的,当StackView的宽度变大时优先拉伸UILabel的宽度,这样前后两个UIImageView的宽度是保持不变的,否则会默认拉伸StackView中的第一个子视图,所以我们看到第一个UIImageView的宽度被拉伸了。
Content Compression Resistance Priority是水平、垂直方向的压缩优先级,道理和上面的拉伸优先级是一样的,我们将UILabel的水平压缩优先级设为749。
现在你们会发现约束错误消失了,第一个UIImageView的宽度也正常了,而中间的UILabel被拉伸了:
MovieTableViewCell
中添加这两个UIImageView和UILabel的IBOutlet
:
MovieListTableViewController
中实现TableView的数据展示:
然后编译运行程序看看效果:
横屏也很完美:
一个完美的Movie List页面已经展现在我们眼前,电影封面、电影名称、电影评分被StackView按合理的布局组织在TableViewCell里,我们只是给StackView设置了简单的四个约束而已。可见StackView在视图布局方面可以给我们带来很大的便利。
StackView的嵌套
既然UIStackView继承了UIView,那么UIStackView是否可以看做是一个UIView而被包含在UIStackView内呢?答案是肯定的,这一节向大家介绍UIStackView的嵌套。
一些界面发杂的App,只要你仔细分析,总会发现StackView嵌套的布局,或看似相似的布局:
在MovieRate这个示例中,我们将StackView的嵌套运用在电影详情页面里:
上图是电影详情页的布局图,从图中我们可以看到一共有三个StackView,一个嵌套在一个里面。我们在Storyboard中把这个页面创建出来(StackView的创建方法在上一节中已经介绍过了,这里就不再累赘了):
详情页的数据直接写在Storyboard中了,因为只是说明StackView的嵌套特性,所以界面比较粗糙。点击TableViewCell跳转到详情页的过程不再累赘了,编译运行看看效果:
虽然有点丑,但是这个页面展示出了StackView的嵌套特性。
总结
诚然UIStackView不是万能的,但是无疑它可以在布局和自适应方面给开发者带来便利,在恰当的情形下使用StackView可以事半功倍。而且因为UIStackView是UIView的子类,所以也可以将动画效果作用于UIStackView上,在方便布局之余还能提高用户体验,何乐而不为呢,那么让我们用起来吧。