上一篇文章《iOS 12 Auto Layout界面自动布局系列1》简要介绍了iOS界面布局方式的前世今生。本篇文章将详细介绍如何使用自动布局实现不同屏幕尺寸的适配。

 

添加自动布局约束(下文简称约束)有以下几种方式:

 

  • 使用Xcode的Interface Builder界面设计器添加并设置约束
  • 通过系统原生的NSLayoutConstraint逐条添加约束
  • 通过可视化格式语言VFL添加约束
  • 使用第三方类库(如Masonry)添加约束

 

本系列文章将以一个简单的例子来演示如何使用这几种方式添加约束,如下图所示。程序的界面主要由三部分组成,最上面是一张苹果logo图片,下面是可以滚动的区域,包含公司名称和详细介绍。竖屏:

横屏:

 

打开Xcode(本文使用的版本为10.1),新建项目(快捷键⇧⌘N),选择iOS -> Application -> Single View App。项目可任意命名(如AutoLayoutXcode),语言选择Objective-C。

点击Next完成项目创建。打开Main.storyboard,拖入一个Image View,一个Scroll View以及两个Label,并将两个Label作为ScrollView的子视图。为了便于识别,将Image View的名称改为Logo Image,两个Label的名称修改为Name Label与Description Label。

 

要想正确定位Logo Image,我们需要添加4个约束,分别是:

  • Logo Image左侧与其父视图左侧对齐
  • Logo Image右侧与其父视图右侧对齐
  • Logo Image顶部与其父视图顶部对齐
  • Logo Image的高度为其父视图高度的一半

于是选中Logo Image,点开设计器下方的Add New Constraints菜单,在弹出菜单上方取消勾选Constrain to margins(否则会有间距),并设置左侧、右侧与顶部的约束值均为0(注意要将工字型约束标记由虚线点亮为实线),最后点击弹出菜单下面的Add 3 Constraints按钮,这样就添加了上述三个对齐约束,如下图所示。

 

在Xcode右侧的Size Inspector中可以查看当前选中界面元素的尺寸及所添加的约束。选中Logo Image并点击Size Inspector,可以在下图中红色圆角矩形区域内看到刚刚添加的三个约束,如下图所示。

三条蓝线分别表示尾部、头部与顶部对齐约束。下面的列表也详细列出了这三条约束:

  • Align Trailing to: Safe Area表示所选视图与安全区域尾部对齐
  • Align Leading to: Safe Area表示所选视图与安全区域头部对齐
  • Align Top to: Safe Area表示所选视图与安全区域顶部对齐

这里额外解释一下,安全区域是iOS 11引入的新概念,简单起见可暂且认为其等同于控制器的主视图(关于安全区域,可参考我的另一篇博客《关于iOS中的布局向导(Layout Guide)和安全区域(Safe Area)》)。“头部”与“尾部”针对当前系统的语言文字方向。绝大多数情况下,语言文字方向都是从左到右,因此“头部”等同于“左侧”,“尾部”等同于“右侧”。但是,某些语言文字方向为从右到左,例如阿拉伯语,则其“头部”对应“右侧”,“尾部”对应“左侧"。鼠标双击Align Trailing to: Safe Area约束会显示该约束的详细信息,如下图所示。

 

回忆一下上一篇文章中关于约束的介绍,约束就是一个线性关系:

y = m * x + c

图中的First Item对应公式中的y,表示因变量,这里显示的是Safe Area.Trailing表示安全区域尾部。Relation对应公式中的=,表示相等关系,这里显示的是Equal即相等(这里为什么会有下拉菜单?点开你会发现,除了=之外,还可以设置为>=或者<=,我们以后再详细讲解)。Second Item对应公式中的x,表示自变量,这里显示的是Logo Image.Trailing表示Logo图片的尾部。Multiplier对应公示中的m,表示缩放比例系数,这里的值为1。Constant对应公示中的c,表示偏移常量,这里的值为0。所以完整串下来就很清楚地表达了安全区域尾部与图片尾部对齐这样一个线性关系。

注意,建立约束的双方是对等的,既可以说y相对于x发生变化,亦可以说x相对于y发生变化,所以First Item与Second Item是可以互换的,即反函数:

x = 1/m * y - c/m

点击First Item或者Second Item下拉菜单,选择Reverse First And Second Item即可交换双方的位置。

交换后的Multiplier与Constant会自动根据反函数公式计算得出。

交换前后的效果是一样的。

注意到左侧文档结构窗口(Document Outline)右上角出现了一个红色按钮,表示当前界面存在布局约束错误。

点击该按钮,Xcode将列出错误和警告。错误为红色圆形按钮,警告为黄色三角按钮,点击后会弹出建议的解决方案。

我们现在仅为Logo Image添加了3个约束,还缺少确定图片最下面位置的约束,即上图中所指出的缺少高度约束(Missing Constraints:Logo Image,Need constraints for: height)。这种由于缺少约束而导致的错误,被称作“二义性错误”(Ambiguity Error)。此时如果点击Add Missing Constraints按钮,Xcode会自行添加缺失的约束,但可能并非如我们所愿,甚至造成混乱。因此我们点击Cancel,自己添加约束。

我们希望图片总能够占据上半部分屏幕,因此可以在图片与安全区域之间建立高度约束。在文档结构窗口中,鼠标单击选中Logo Image,按住Control键(或者按住鼠标右键)拖拽到Save Area上。

松开鼠标,在弹出的菜单中选中Equal Height。

保持Logo Image选中,在Size Inspector窗口中双击Equal Height to: Safe Area约束,修改Multiplier的值为0.5。

现在Logo Image的4个约束就齐了。由于我们尚未向ScrollView和Label添加约束,所以Storyboard文件还是会报二义性错误,我们先暂时忽略这些错误。

接着建立Scroll View的约束,它与安全区域头部、尾部、底部对齐,并且Logo Image与Scroll View的垂直间距为0。同理打开Add New Constraints菜单,取消勾选Constrain to margins,将4个方向的值设置为0(暂时先不点击Add 4 Constraints按钮),如下图所示:

 

Xcode会采取就近原则自动选取因变量x,但这有可能并不是我们希望的。例如,我们希望Scroll View顶部和Logo Image底部对齐,于是点击下图中顶部约束的下拉菜单,会列出可供选择的自变量。这里会出现两种情况:

  • 一种可能是,菜单中列出了View、Safe Area和Logo Image,且默认选中了Logo Image,如下图。

那么恭喜你,你可以直接点击Add 4 Constraints,Scroll View的4个约束就加好了。

  • 另一种可能是,菜单中只列出了View(控制器主视图)和Safe Area,并未列出Logo Image,如下图。

此时无法添加想要的顶部对齐约束,那么先暂时点击顶部的红色工字型将其变为虚线(如下图红圈),然后点击Add 3 Constraints。

菜单中未出现Logo Image的原因是Scroll View和Logo Image这两个视图重叠了(上图中双向箭头区域),两者间距为负值,所以Xcode的就近原则不适用于间距为负值的情况。解决办法是将Scroll View与Logo Image二者垂直间距变为正值,将Scroll View的顶部位于Logo Image底部的下方,即二者的垂直间距为正值,这时再点击Add New Constraints菜单添加顶部约束就可以了。

现在Scroll View的约束添加完毕。接着我们为Name Label添加约束,将其头部、顶部与尾部与Scroll View的对应部分对齐,并限定其高度为20。除了使用Add New Constraints菜单添加约束外,还可以通过拖拽添加约束,我们就来试试这种方法。

右键点击Name Label且不松开右键,拖拽到Scroll View并松开鼠标,弹出菜单。按住⇧并点击Leading Space to Container、Top Space to Container、Trailing Space to Container,按下enter键添加头部、顶部、尾部约束。

右键点击Name Label并拖拽到自己身上,松开鼠标,在弹出的菜单中选择Height,添加高度约束。

 

这种方式会以当前界面设计器中的位置尺寸来设置约束的常量值,如果需要可以再修改常量。选中Name Label,在Size Inspector中可以看到刚刚添加的4个约束的常量值都不正确。

点击Trailing Space to: Superview右侧的Edit按钮,在弹出的窗口允许修改约束的m和c值。此处将Constant的值修改为0。

同理,将Leading Space to: Superview和Top Space to: Superview的Constant值修改为0,将Height Equals:的Constant值修改为20即可。

选中Name Label,在Attributes Inspector窗口中设置Label的Text属性为“苹果公司”,设置Background为绿色。

同样为Description Label添加约束,将其头部、底部、尾部与Scroll View的对应部分对齐,并且顶部与Name Label的底部垂直间距为0。在Attributes Inspector窗口中设置Label的Text为:

苹果公司(Apple Inc. )是美国的一家高科技公司。由史蒂夫·乔布斯、斯蒂夫·沃兹尼亚克和罗·韦恩(Ron Wayne)等三人于1976年4月1日创立,并命名为美国苹果电脑公司(Apple Computer Inc. ), 2007年1月9日更名为苹果公司,总部位于加利福尼亚州的库比蒂诺。
苹果公司创立之初主要开发和销售的个人电脑,截至2014年致力于设计、开发和销售消费电子、计算机软件、在线服务和个人计算机。苹果的Apple II于1970年代助长了个人电脑革命,其后的Macintosh接力于1980年代持续发展。该公司硬件产品主要是Mac电脑系列、iPod媒体播放器、iPhone智能手机和iPad平板电脑;在线服务包括iCloud、iTunes Store和App Store;消费软件包括OS X和iOS操作系统、iTunes多媒体浏览器、Safari网络浏览器,还有iLife和iWork创意和生产力套件。苹果公司在高科技企业中以创新而闻名世界。
苹果公司1980年12月12日公开招股上市,2012年创下6235亿美元的市值记录,截至2014年6月,苹果公司已经连续三年成为全球市值最大公司。苹果公司在2014年世界500强排行榜中排名第15名。2013年9月30日,在宏盟集团的“全球最佳品牌”报告中,苹果公司超过可口可乐成为世界最有价值品牌。2014年,苹果品牌超越谷歌(Google),成为世界最具价值品牌 。

设置Lines的值为0,设置Background为黄色。

Xcode 8.0添加了实时预览功能,可以在不启动模拟器/真机的情况下预览不同设备横竖屏的界面情况。注意到设计器下方的工具条:

这里可以选择不同的设备(iPad、iPhone),以及选定屏幕方向(竖屏、横屏),设计器会根据不同的设置显示对应界面。

除此之外,Xcode的Preview工具还支持同时预览不同屏幕大小的界面情况,方便进行对比。打开Assistant Editor,点击快速跳转下拉菜单:

选择Preview -> Main.storyboard:

在右侧预览窗口中就可以看到在不同设备上的效果。可以点击预览窗口左下角的+按钮,在弹出菜单中添加需要预览的设备。

现在我们添加Logo Image的图片。下载苹果Logo图片apple.jpg,并将其拖入项目中。文件下载地址:https://pan.baidu.com/s/1b5AqDo 密码: e4ff

选中Logo Image,在属性窗口(Attributes Inspector)的Image View中,将Image设置为apple.jpg,将Content Mode设置为Aspect Fit,这样就能根据Image View的大小来调整图片的缩放方式。

现在不妨在模拟器中实际运行一下程序,发现Scroll View变成了横向滚动而非纵向滚动,这并不是我们想要的效果。

 

可是我们已经把每个视图的4个必要约束都添加了,怎么还会出现这样的问题呢?

是这样的,当使用自动布局为Scroll View添加约束时,除了需要确定ScrollView本身的位置尺寸之外,还需要明确ScrollView的ContentSize大小。对于上述例子,Scroll View本身会占用下半部分屏幕,但是其ContentSize的大小是不确定的,所以会出现异常滚动效果。因此我们还需要为两个Label确定宽高,这样才能计算出ContentSize。按住⌘键,在文档结构窗口中选中Logo Image、Name Label与Description Label,点击Pin按钮,在弹出的窗口中选中Equal Width,这样就使得两个Label的宽度总与Image View的宽度相等,即Scroll View的ContentSize的宽度总是和Scroll View本身的宽度相同,因此就不会出现横向滚动的情况了。

再次运行,现在只能纵向滚动了。

程序最终项目文件链接:https://github.com/puckerxp/AutoLayoutSeries,在AutoLayoutXcode目录下。

通过这个例子我们不难发现,其实自动布局的概念并不难,而且它能够很好地解决多设备界面适配与屏幕旋转处理等工作。而且在这几种添加约束的方式当中,使用Xcode的Interface Builder来可视化地创建约束,以及使用预览工具来实时调整约束,这种方式是最简单直观而且最常用的方式。

在本系列后续文章中,将会介绍如何通过代码创建NSLayoutConstraint对象,以及如何使用VFL语言来添加约束。

如果你对我写的东西有任何建议、意见或者疑问,请到我的博客留言:

Puzhi的CSDN博客

 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐