这是对于主动布局的第一篇文章。
>> Stack View的应用

主动布局(Auto Layout)可能依据对视图的束缚(Constraint),动静地计算视图层次结构中所有视图的大小和地位。

基于束缚的Auto Layout使咱们可能搭建动静响应外部、内部变动的用户界面。内部变动包含用户扭转窗口大小(OS X)、旋转设施(iOS)、在iPad上进入或来到宰割视图(iOS)、不同屏幕尺寸,外部变动包含app显示内容长度变动、字体大小变动、对国际化的反对等。

大部分的内部变动会在运行时产生,这就要求app要动静的调整视图布局。只管屏幕尺寸不会扭转,但创立一个主动布局的界面就能够实用于iPhone SE、iPhone 7 Plus、甚至iPad不同尺寸屏幕的设施。

当用户界面中视图或控件大小变动时会导致外部变动。如对国际化的反对,把用户界面上的文字扭转为其余语言时,新的语言可能占用不同大小空间;不同的语言有不同布局方向,如英语、中文都是自左向右,而阿拉伯语自右向左,这时中、英文界面右下角的按钮在阿拉伯语中应在左下角。如app反对调整字体大小,调整后用户界面中任何文本的高度和宽度都会发生变化,此时须要调整布局。

1. Auto Layout与Frame-Based Layout比拟

布局用户界面形式有三种,第一种是代码形式,第二种是应用Autoresizing masks,第三种是应用Auto Layout。

通过代码形式来布局用户界面时须要设定视图在其父视图坐标系中的地位和大小。

为布局界面,你必须计算视图层级中每一个视图的地位和大小。如果其中一个产生了扭转,你就须要再次计算所有受影响的视图。在许多方面,以代码形式构建的界面会更灵便、更弱小。当界面有变动时,你能够操作其它视图的变动。也正因为你须要管制其它视图的变动,构建一个简略的界面可能须要大量工作,构建一个自适应的界面就变的更加艰难。

第二种形式是应用Autoresizing masks来构建界面,Autoresizing masks指定视图如何随父视图的变动而扭转,这简化了适应内部变动布局的复杂度。Autoresizing masks用于较小数量的可能布局,对于简单的用户界面,须要联合应用代码布局。另外,Autoresizing masks只实用于内部变动,不响应外部变动。

第三种形式为应用Auto Layout增加一系列束缚来构建界面,这些束缚代表两个视图间的关系,最初Auto Layout依据这些束缚计算出视图的地位和大小。Auto Layout动静的响应外部、内部变动。

视图层次结构布局被定义为一系列线性方程。每个束缚示意一个方程。指标是申明一系列方程,最初只有一个可能的布局计划。下图是一个示例方程。

这个束缚示意Red视图的Leading必须在Blue视图Trailing8points,该方程由以下几局部组成:

  • Item 1:方程式中的第一项,这里指Red视图。第一项必须是视图或布局参考线(Layout Guide)。
  • Attribute 1:在Item 1增加束缚的地位。在这里是Red视图的Leading
  • Relationship:左右两侧的关系,能够是equalgreater than or equalless than or equal之一。在这里左右两侧相等。
  • Multiplier:第二项的值乘以该浮点值。在这里,乘数是1.0
  • Item 2:方程式中的第二项,在这里是Blue视图。与第一项不同,这一项能够为空。
  • Attribute 2:在Item 2增加束缚的地位。在这里是Blue视图的Trailing。如果Item 2为空,这里也要为空。
  • Constant:浮点类型的常量。在这里是8.0,该值被增加到Attribute 2

通过创立这样的方程能够创立多种束缚,能够定义两个视图间的间隔,对齐视图、定义两个视图大小关系、定义视图宽高比(Aspect Ratio)。

须要阐明的是下面方程式中的=是相等,不是赋值。当Auto Layout布局界面时,会计算Attribute 1Attribute 2的值直到等式成立,而不会间接把右侧的值赋值给左侧。因而咱们能够调换等式左右两侧的值,但恪守上面规定的束缚更易于保护。不合乎上面规定的束缚能够通过调换等式两侧解决。

  • Multiplier优先应用整数而非分数。
  • 常量优先应用负数而非正数。
  • 如果可能,视图更应该自上而下,自左向右布局。

束缚既能够用来形容界面中两个视图间的关系,也能够定义一个视图中不同属性间的关系。例如设置视图高度和宽度的高宽比。

在主动布局中,可供束缚的属性有左右高低四边(leading,trailing,top和bottom)、宽、高、程度居中和垂直居中等。

2. 创立demo

这里创立一个Single View Application模板的利用,Product NameAutoLayoutLanguageObjective-CDevicesUniversal,抉择文件地位,创立工程。

上面通过几个示例来体验Auto Layout。

3. 示例1

关上Main.storyboard,增加5个UILabel。如下图所示,一个在核心,其余每个角各一个。拖放UILabel时会呈现蓝色虚线,放在这些蓝色虚线地位。

点击Xcode右上角的Assistant editor,这时个别显示的是你所选中视图控制器的代码,点击左上角的Automatic,从弹出菜单中选择Preview > Main.storyboard(Preview)

能够通过点击上图左下角的+按钮增加多个设施的预览,这里只增加iPhone SE。

通过上图能够看到,在4英寸的iPhone SE中,只有左上角一个Label显示失常。这是因为如果视图没有任何束缚,在Build Time零碎会主动为视图增加至左上角、目前大小的束缚。当你增加第一个束缚时,零碎会主动革除所有零碎增加的束缚,此时零碎会呈现红色正告提醒束缚有余。当你增加了一个束缚时,你就须要增加可满足布局的所有束缚。

还记得之前咱们说到的蓝色虚线吗?它们不是这里说到的束缚,它们是Xcode提供的布局参考线。

Interface Builder里有三种增加束缚的形式。第一种是在视图间control+鼠标左键拖拽,也能够在Document Outline拖拽,在视图间拖拽的益处是弹出菜单会依据拖拽方向扭转。第二种是应用AlignAdd New Constraints工具,第三种是让Interface Builder设置束缚之后咱们批改束缚。主动布局菜单共以下五项:

自左向右顺次为:

  • Update Frame:用于在束缚更新后,更新frame
  • Embed in Stack:用于嵌入Stack View,会依据目前选中视图主动嵌入Horizontal Stack ViewVertical Stack View
  • Align:用于设置视图程度核心、垂直核心、基线等。
  • Add New Constraints:用于设置视图与最近视图、或控制器Margin间隔,视图宽、高、宽高比等。
  • Resolve Auto Layout issues:增加倡议束缚、删除束缚,其中上半局部对选中视图无效,下半局部对所有视图无效。

选中左上角Label,点击Add New Constraints,在弹出的菜单顶部一栏左侧和上部别离填写020,点击底部Add 2 Constraints增加束缚。

用同样形式为另外三个角落处的Label增加束缚,束缚均是到间隔本人最近的边缘,其中LeadingTrailing0TopBottom20

应尽量应用LeadingTrailing,防止应用LeftRight,这样布局会适应视图的浏览方向。浏览方向随用户设定的以后语言而变,你也能够本人设定。

最初选中视图核心的Label,点击Align,勾选弹出窗口中Horizontally in ContainerVertically in Container选项框,前面数字均为0,点击Add 2 Constraints增加束缚。

当初进入Preview查看,一切正常。能够点击红色标记处的旋转按钮,查看横屏(Landscape)状态下Label地位、大小。

4. 示例2

持续在方才的demo中进行操作,关上Main.storyboard,从对象库拖拽出一个View Controller,在下面增加两个UIView,左右各一个,左侧色彩为Blue,右侧色彩为Orange

两个视图布局束缚是:Blue视图与TopBottom间隔是20,与Leading间隔0,与Orange视图间隔是standardOrange视图与TopBottom间隔是20,与Trailing间隔0。同时Orange视图宽度是Blue视图宽度的二倍。

因为这里设置LeadingTrailingTopBottom束缚与后面雷同,不再叙述。如果遇到问题,能够在我的Github中查看。另外,文章底部也会提供demo地址。

上面增加两个视图间间距的束缚。选中Blue视图,点击Add New Constraints按钮,在弹出窗顶部点击Trailing文本框内的下三角,抉择Use Standard Value,最初点击Add 1 Constraint。另外,Standard Value默认为8

当初增加两个视图宽度的束缚。同时选中两个视图,点击Add New Constraints按钮,在弹出窗中勾选上图中的Equal Widths选项。最初点击Add 1 Constraint。如果视图呈现黄色正告线,示意以后地位与视图束缚地位不同,点击Update Frames更新视图地位。

当初增加Orange视图宽度为Blue视图宽度二倍的束缚。选中任意一个视图,点击底部=束缚线,右侧会主动关上Attribute Inspector,鼠标放在First Item会看到选中的是Orange视图,所以这里的等式为

Orange View.Width = 2.0 x Blue View.Width

所以,批改Multiplier2

Preview查看如下,在模拟器查看成果一样。

Add New Constraints一项中顶部有Constrain to margins选项框。如果勾选上,束缚增加到父视图的margin属性;如果勾销勾选,束缚增加到父视图的edge属性。如果在一个控制器上增加一个UIView,设置背景色为Red,到四边的束缚均为0,上面第一个图为勾选Constrain to margins,第二个为不勾选。想要理解具体区别,点击这里。

5. 示例3

持续对示例2中的视图进行操作,这次限定Blue视图宽度大于或等于150。只有当屏幕足够宽时Orange视图宽度为Blue视图宽度二倍才实现,也就是可选实现。

选中Blue视图,点击Add New Constraints增加宽度为150的束缚。放弃Blue视图选中状态,关上Size Inspector,双击Constraints一栏中宽度为150的束缚。也能够单击Edit按钮。

双击后弹出如下:

把上图中的Equal批改为Greater than or Equal,这样宽度就会大于等于150

上图中的Priority为束缚优先级。所有束缚默认必须实现(Required),也能够创立可选实现(Optional)的束缚。

所有束缚有一个优先级属性,它的值在11000间。优先级为1000的束缚必须实现,其它优先级的束缚为可选实现。当主动布局计算布局形式时,会优先满足高优先级的束缚。如果不能满足低优先级的束缚,该束缚会被跳过,但被跳过的束缚不是齐全有效,Auto Layout计算布局计划时会抉择最靠近被跳过束缚的计划。

尽管优先级的值是11000,但零碎把优先级划分成了低(250)、中(500)、高(750)和必须(1000)四类,你只须要把束缚优先级设定为高于或低于这些值一到两个点即可。

当初storyboard呈现红色正告,提醒Blue视图宽度大于等于150Orange视图宽度为Blue视图宽度二倍抵触,把后者优先级设定为750Preview显示如下:

能够看到,在iPhone的竖屏(Portrait)中两个视图宽度简直一样宽,在横屏(Landscape)中,Orange视图宽度是Blue视图宽度二倍。即优先满足了Blue视图宽度大于等于150这一束缚。

6. 示例4

目前为止,所有示例均用束缚指定视图的地位和大小,但有些视图的大小依据内容变动,这称为固有内容大小(Intrinsic Content Size)。例如,UIButtonintrinsic content size是题目大小外加很小margin。

不是所有的视图都有intrinsic content size。如:UIViewNSView不具备固有内容大小,UILabelUIButtonUISwitchUITextField高和宽都具备固有内容大小,UIImageView没有增加图片时不具备intrinsic content size,增加图片后固有内容大小与图片大小统一。

Auto Layout在每个维度应用一对束缚来示意视图的intrinsic content size。内容拥抱(Content hugging)将视图向内拉,使其紧贴内容。内容压缩阻力(Content Compression Resistance)将视图向外推,使其内容可能残缺显示、不被压缩。

固有内容大小的束缚有本身的优先级。个别,content hugging优先级为250content compression resistance优先级750。因而,个别拉伸视图比压缩视图容易。例如:你能够把button拉伸一些,不影响应用。但如果压缩一些,内容可能无奈残缺显示。

持续在方才的demo里操作,从对象库拖拽出一个View Controller,在顶部增加一个UILabel和一个UITextField

增加束缚,让LabelLeading间隔为0,与Text FieldLeading间隔为Standard值,与Text Field基线在同一程度地位;让Text FieldTop间隔20,与Trailing间隔为0。两个视图宽和高均为固有内容大小。

这里增加束缚与后面示例雷同,不再叙述增加过程。只说一下增加基线束缚。选中两个视图,点击Align,勾选Baselines,点击增加Add 1 Constraint

UILabelUITextField在一起时,个别UILabel放弃本身宽度,拉伸UITextField,以此填充可用空间。所以,该当设置UILabelcontent hugging优先级为251UITextField的为250

Size Inspector查看content hugging,你会发现Interface Builder曾经设置好了优先级。另外,如果你是用代码增加主动布局,须要手动设定这里的优先级。

点击Update Frames更新frame。在Preview查看布局。

只有当高为固有内容大小时基线才能够正确对齐。如果高被压缩或拉伸,基线将不能正确对齐。

个别在解决示例4中的束缚时应该应用Stack ViewStack View提供了一种轻松的形式利用主动布局的性能,而不会引入简单的束缚,在下一篇文章咱们将具体介绍Stack View的应用。

Demo名称:AutoLayout
源码地址:https://github.com/pro648/Bas...

参考资料:

  1. Auto Layout Guide
  2. What is “Constrain to margin” in Storyboard in Xcode 6

欢送更多斧正:https://github.com/pro648/tips

本文地址:https://github.com/pro648/tip...