本文翻译自 shinobicontrols iOS9 Day-by-Day :: Day 4 :: UIStackView
在 iOS 9 中,Apple引入了UIStackView,它能让你在应用中更简单地水平或垂直排列view。在底层,UIStackView使用自动布局来管理子view的位置和大小,这也使构建自适应UI更简单。
在这之前,如果你想创建一个这种的并排或并列view的布局,你需要很多约束。你还需要基于方向来管理很多padding、height和 x /y position 的布局约束。
现在UIStackView会来做这些事情。甚至在添加、隐藏和删除view,以及改变UIStackView本身的布局属性时,都直接支持流畅的动画。
现在我们会构建一个例子来展示怎么使用UIStackView。最终完成的代码在Github( Swift 或 Objective-C )上可以找到。Demo在底部用segmented control来控制UIStackView的alignment和distribution属性。
上图就是我们要构建的demo。能看到我们展示了4个头像,还有底部的两个segmented control。UI使用自动布局,可以适应任何屏幕大小。当创建时你可能会感到吃惊,因为我们只手动添加了4个位置布局约束!
上面用到的基本都是UIStackView。我们总共会用到4个UIStackView,只有第一个需要我们手动添加约束,来将它放到root view上。
拖一个垂直stack view 到view controller上,然后打开Interface Builder 右下角的constraint pinning tool ,添加如上图所示的约束。这是让stack view保持居中,并且大小正确。
再拖3个水平stack view到之前创建的垂直stack view中。顶部的stack view会包含4个image view,每个展示一个头像。所有要做的就是拖4个image view 到顶部的stack view。我们用的每张图片的大小都有些许不同,并且也不想让图片失真,所以我们将每个image view 的content mode 设置为Aspect Fit。这意味着不论image view的大小,图片都会保持正确的宽高比。
你可能还注意到,在最终的实现中,每个image view之间会有小的缝隙。这是在IB中的attributes inspector设置stack view的spacing 属性。在这你还可以设置alignment和distribution属性。现在把这两个属性都设置为Fill,我们之后会在segmented control中来修改这些值。
在root stack view 中的另外两个也是水平stack view。这两个比较简单,每一个都有一个label和一个segmented control。在你添加完两个label和segmented control之后,把segmented control配置成下面的内容。
(译注:你需要在IB中为2个segmented control 添加height=28的约束,并将第一个segmented control 的Auto-Size Mode 设置为Proportional to Content)
一会我们就能动态演示这些属性到底是干嘛的了,但其实看字面意思应该就已经很明白了。值得注意的是,有些属性会严重依赖stack view中内容的contentSize。很幸运在这个例子中比较简单,image view ?的大小就是image本身的大小。
现在已经设置好UI,我们就需要实际处理用户选择不同segment的情况了。首先,从顶部包含image view的stack view 拖一个IBOutlet到你的view controller子类,并命名peopleStackView。然后再从每个segmented Control的value changed事件拖一个IBAction。在每个方法中,你要根据用户选择的segment来设置peopleStackView的alignment或distribution属性。
@IBAction func alignmentSegmentSelected(sender: UISegmentedControl) { UIView.animateWithDuration(1.0, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.2, options: .CurveEaseInOut, animations: { () -> Void in if sender.selectedSegmentIndex == 0 { self.peopleStackView.alignment = .Fill } else if sender.selectedSegmentIndex == 1 { self.peopleStackView.alignment = .Top } else if sender.selectedSegmentIndex == 2 { self.peopleStackView.alignment = .Center } else if sender.selectedSegmentIndex == 3 { self.peopleStackView.alignment = .Bottom } }, completion: nil) } @IBAction func distributionSegmentSelected(sender: UISegmentedControl) { UIView.animateWithDuration(1.0, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.2, options: .CurveEaseInOut, animations: { () -> Void in if sender.selectedSegmentIndex == 0 { self.peopleStackView.distribution = .Fill } else if sender.selectedSegmentIndex == 1 { self.peopleStackView.distribution = .FillEqually } else if sender.selectedSegmentIndex == 2 { self.peopleStackView.distribution = .FillProportionally } else if sender.selectedSegmentIndex == 3 { self.peopleStackView.distribution = .EqualSpacing } else if sender.selectedSegmentIndex == 4 { self.peopleStackView.distribution = .EqualCentering } }, completion: nil) }
能看到我把这些代码放在了animation block里,这样看起来更好一些,但不是必需的。如果删除动画代码,这些改变会立即执行。现在就剩构建和运行了。
结果应该和下面的视频中类似。
尝试一下distribution和alignment的不同组合。这应该能展示UIStackView在帮助你创建适合不同设备的UI方面是多么强大了。
如果你已经有一些UI,并且想把它们转换成使用UIStackView来布局,只需删除这些view的约束,选中它们,然后点击IB窗口右下角一排按钮最左边的那一个就行了。这会在一个新的UIStackView中快速排列这些view。
这会让原来基于约束的布局转换成简单的stack view,stack view会为你处理大部分的约束布局。
关于Xcode 7 中Storyboard Reference的更多内容,我推荐观看WWDC session 218, Mysteries of Auto Layout, Part 1 。Jason Yao在视频的前15分钟讲到了UIStackView的基础,并创建了一个demo,展示现在能多快速地创建UI,而且所需的约束还比以前少。