自从AUTO LAYOUT在iOS 6中引入以来,我一直深深的为之着迷,特别是通过直接在代码中使用它,而不是使用故事板。但它在代码中使用非常冗长。我写了一些类,使代码更加紧凑。但我不满意我的解决方案。所以我计划在WWDC期间去一个实验室,咨询一个苹果AUTO LAYOUT工程师对这个问题的看法。但是事实证明我不需要!苹果解决了大部分我在AUTO LAYOUT中遇到的问题在iOS 9的API中!在这篇文章中我想突出的新API更容易使用自动布局在代码和故事板! 在这片文章中,我将分别用代码和Storyboard展示用新的API使用AUTO LAYOUT是如何简单易用。
什么是AUTO LAYOUT
对于那些AUTO LAYOUT的新手,我想快速的过一下这东西到底是什么。AUTO LAYOUT是一个机制,用它可以很容易地支持不同的屏幕尺寸。
AUTO LAYOUT的核心是极其简单的。它使用“约束”来确定视图在屏幕上的x,y,宽度和高度属性。约束是描述一个视图的位置和大小的规则。一个视图需要多个约束来正确的定位和限制大小。约束和所代表它的类youtConstraint仅仅是遵循这个公式:
view1.attribute1 = view2.attribute2 x A + B
这是AUTO LAYOUT的核心,所有的约束都遵循这种模式。使用一系列遵循这种模式的约束你可以布局一切,它将在巧妙的缩放来适应不同的屏幕尺寸。
例如:
label.Top = container.Top x 1 + 20(列出了一个标签的y值低于容器的顶部边缘视图20像素点)
label.Width = container.Width x 0.5 + 0 (一个标签的宽度设置为一半的容器的宽度)
为什么使用AUTO LAYOUT?
在很长的一段时间内,iOS应用程序并没有在关心屏幕大小适配。iPhone 3g,4和4s基本上都是分辨率为320×480的逻辑点。随着视网膜屏幕的引入并没有改变,因为作为一名开发人员你仍然可以假装屏幕是320×480,而系统会为你转换为640×960。当然你也有iPad,但你不必为之做更多的工作,因为你将为iPad设计一个特定的用户界面,这将是特定的屏幕分辨率1024×768。
支持不同的分辨率
随着iPhone 4s到来的iOS 6.0,给我们介绍了自动布局。我们不觉得有必要使用它,但在它的目的很明显:使它容易支持iPhone更大的屏幕!在那一刻唯一的真正好处来自屏幕的旋转,从竖屏到横屏,这实际上也是一个分辨率的变化。
iPad的多任务模式
随着iOS 9的到来,AUTO LAYOUT变得如此重要的一个原因是:在iPad上支持多任务!现在你可以观看并排的两个应用程序在同一时间,这意味着应用程序要能够缩放自己的尺寸为四分之一屏幕或者半个屏幕。用户决定显示每个应用程序的大小。使用AUTO LAYOUT可以很容易适应这些变化。
STACKING VIEWS
Before iOS 9, we had to add many different constraints to all different views within a single screen to position them correctly and let them handle screen size changes accordingly. This was easy, but a little hard to manage. There were two ways to do it:
在iOS 9之前,我们不得不在一个屏幕上对所有子视图添加许多不同的约束来使它们适应屏幕大小的变化。这是一件简单的事情,但是难于管理。有两种方式可以实现:
- 使用 storyboard
- 使用代码
Storyboard
使用故事板可以单击并拖动来创建约束,你最终将得到的大量的蓝线,这代表了约束。现在你想要插入另一个标签,在“车库乐队”和“苹果”之间。你该怎么做?你删除一些约束连接现有的标签,插入你的新标签和你现有的两个标签连接到你的新标签。这足以说明约束维护的复杂性。
用代码实现约束
用代码实现的话也存在同样的问题,这里没有蓝线,取而代之的是:
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(
"V:|[titleLabel]-padding-[descriptionLabel]-padding-[imageView]",
options: nil,
metrics: ["padding": 10],
views: [
"titleLabel": titleLabel,
"descriptionLabel": descriptionLabel,
"imageView": imageView])
)
view.addConstraint(NSLayoutConstraint(
item: titleLabel,
attribute: .Leading,
relatedBy: .Equal,
toItem: self,
attribute: .Leading,
multiplier: 1,
constant: 10)
)
这仍然不是最佳的管理方案。
UIStackView
随着iOS 9的一声炮响,UIStackView应运而生!你可以把一组子视图放入UIStackView,并将它们垂直或水平布局,这不费吹灰之力。不再需要为每个子视图添加约束。这一切UIStackView都已经为你做好。
在上边儿的例子中你只需要标示那里需要使用stack view。你只需找出子视图需要堆积的位置。几乎所有你能想到的UI都可以都可以通过嵌套stack views的方式建立视图。苹果强烈建议我们为每一个布局使用stack views,只有那些用stack views实在无法实现的才需要用单独的约束。
使用stack view的示例:
let stackView = UIStackView(arrangedSubviews: [titleLabel, descriptionLabel, imageView])
stackView.distribution = .Fill
stackView.alignment = .Leading
stackView.axis = .Vertical
stackView.spacing = 10
就是这样!在titleLabel和descriptionLabel之间添加一个label就像将其添加到数组的“arrangedSubviews”一样简单!
一个额外的好处:你可以很容易地通过动画动画隐藏/显示视图通过修改UIView的隐藏属性,而该属性通常不能做成动画。
所有需要的是修改动画的隐藏属性,如下:
UIView.animateWithDuration(0.5, animations: {
someSubview.hidden = true
})
我自己实现的UIStackView
多么完美的UIStackView,但是它只能用在iOS 9及以上版本。但是我想用在iOS 7和iOS 8之中。UIStackView的接口看起来足够简单:用一个视图数组初始化它,配置它的一些属性如‘distribution’, ‘alignment’ 和 ‘axis’。这就是它!我想,这有多难?然而事实证明它比我想象的要难得多,但是我做到了!我完全重写了整个过程咋iOS 7和iOS 8之下。我为之自豪:TZStackView!
TZStackView git地址
我将它设计为和UIStackView一模一样的接口,以便于将来某一天被替代。使用TZStackView的实现上面的例子:
let stackView = TZStackView(arrangedSubviews: [titleLabel, descriptionLabel, imageView])
stackView.distribution = .Fill
stackView.alignment = .Leading
stackView.axis = .Vertical
stackView.spacing = 10
LAYOUT GUIDES
另一个新的iOS 9Auto Layout的概念是“layout guide”。是“constrain negative space”。什么意思?看个例子:
你有三个固定宽度的视图用来填补可用空间,你想让这些矩形之间的间距相等。
The empty space in between the boxes is called ‘negative space’ and you want to express using constraints that you want those spaces to be equal to one another. Long story short: you can’t express that directly because constraints can only be applied on views, not on margins between those views. Before iOS 9 what you could do is add 4 transparent subviews in between those boxes, also known as ‘spacer views’ and put constraints on those. That works like a charm!
The problem with this approach is that it’s kind of a hack. You pollute your view hierarchy with things that are just there to position other views with no purpose of their own. This also impacts performance.
This brings me to layout guides, represented by the UILayoutGuide class. You can add them to your view like this:
视图之间的空的空间被称为“negative space”,你的目的是使用约束来使这些空间相等。长话短说:你不能直接表达,因为约束只能应用于视图,而不是那些视图之间的间距。在iOS 9之前你可以做的是添加4个透明子视图在这些视图之间,这些也被称为“间隔视图”,然后给这些视图添加约束。
这种方法的问题在于它是一种黑客技术。这污染了你的视图层次,它们仅仅是为了固定其他视图而没有自己的意义。这也影响了性能。
为此,引入了“layout guide”,由UILayoutGuide类来实现。你可以将它们添加到你的视图,像这样:
let layoutGuide = UILayoutGuide()
view.addLayoutGuide(layoutGuide)
Next, you can use the layout guide in your constraints, just as you would do using ‘spacer views’.
接下来,你可以在你的约束中使用layout guide,就像使用‘spacer views’。
view.addConstraint(NSLayoutConstraint(
item: layoutGuide1,
attribute: .Width,
relatedBy: .Equal,
toItem: layoutGuide2,
attribute: .Width,
multiplier: 1,
constant: 0)
)
这样做的好处是,我们不污染我们的视图层次和替换‘spacer views’用更轻量级的‘layout guides’。它们不能回应触摸或直观地呈现在屏幕上。它们只是在那里给其他视图提供约束。
CONSTRAINTS FLUENT INTERFACE
这也是一个功能在不久的将来会普遍使用的功能。它在WWDC的两节演讲中被提到。它简化了你创建单独约束的过程。在此重复我之前给出的一个例子:
view.addConstraint(NSLayoutConstraint(
item: titleLabel,
attribute: .Leading,
relatedBy: .Equal,
toItem: self,
attribute: .Leading,
multiplier: 1,
constant: 10)
)
现在你可以用下边儿的代码来达到同样的效果:
view.leadingAnchor.constraintEqualToAnchor(titleLabel.leadingAnchor, constant: 10)
当我第一次看到这种新的写约束的方式时我非常激动。易读、易记忆更易于管理。这和UIStackView配合使用将大大简化编写自动布局代码的数量。
RIGHT-TO-LEFT LANGUAGE SUPPORT
从右到左的语言支持已经在这儿了!我不知道这是否令人着迷,但显然使用从右到左的语言,例如阿拉伯语的人更期望用户界面右对齐。
它的实现是很简单的。他们仅仅使用了“Leading”和“Trailing”的布局属性抽象“Left”和“Right”。在iOS 9之前“Leading”和“Trailing”的布局属性已经存在,但他们相当于“Left”和“Right”。事实上,苹果公司推荐使用“Leading”和“Trailing”来替代“Left”和“Right”。这样你的应用程序支持从右到左的语言只是分分钟的事情!
总结
Auto Layout在iOS 9上做了很多该进,总结下:
- stack view
- layout guides
- layout anchors (fluent interface)
- right-to-left language support
推荐观看Mysteries of Auto Layout – part 1. 来学习怎样调试Auto Layout问题, 观看Mysteries of Auto Layout – part 2来了解更多细节. 想了解right-to-left language support, 阅读New UIKit Support for International User Interfaces.
本文翻译自WWDC 2015: AUTO LAYOUT IMPROVEMENTS,转载请注明出处。