iOS9 UIStackView 简介

作者:Umberto Raimondi,原文链接,原文日期:2015-12-08
译者:CoderAFI;校对:Channe;定稿:shanks

示例程序采用 Swift2.0 编写,需要用 Xcode7 进行编译,可以访问 Github 或者 zipped 下载本文源代码

iOS9 新加入了一个非常易用的布局控件 UIStackView,它可以将一组 UIView 视图进行垂直或水平方向的排列,用来替换手工使用 Auto Layout 对视图进行布局。

每个 UIStackView 控件都可以在垂直和水平方向上排列展示一组 subviews,并可以根据当前屏幕大小和方向的变化动态调整它的内容,感觉起来就像是一个隐形的容器。实际上 subviews 的位置是根据设置的对齐、间距和大小属性来决定的。

内部的原理是 UIStackView 类帮你管理了 Auto Layout 约束。想象一下 UIStackView 其实就是一个基于 Auto Layout 的抽象层从而使布局属性的创建简单化。你可以在一个主 UIStackView 中嵌套 UIStackView 从而让视图精确放置到相应的位置。

如果你做过 Android 开发,你会发现 UIStackView 概念跟 Android 中最常用的布局控件 LinearLayout 非常相似,这些布局的想法其实都是从早期的 Java Swing 开发中借鉴过来并加以完善的。

基础

UIStackView 既可以用代码编写也可以在 Interface Builder 中设计。

在 Interface Builder 中你可以从 Object Library 控件选择工具集里找到垂直或者水平对齐的 UIStackView 并添加到相应位置,然后就可以在 UIStackView 上添加新的视图了。

UIStackView 同样也可以对现有的一些视图进行包装,只需要选择他们并点击 Interface Builder 底部工具栏新加的
Stack icon
Stack icon

图标即可。

非常简单,但是我们的教程将用代码的方式实现一个简单的嵌套布局。

在这个简单的示例程序中,我们将会在状态栏下放置一个 UIStackView,里面包含四个控件:两个 UILabel,一个水平方向的 UIStackView 和 一个 UIButton。水平方向的 UIStackView 中包含了三个带有默认图标的按钮。

下面让我们创建一个 Single View Application,记住要将 Deployment Target 设置成 9.0+。

打开 ViewController 类并用下面的代码替换 viewDidLoad 方法:

var stackView:UIStackView!
var nestedStackView = UIStackView()

override func viewDidLoad() {
    super.viewDidLoad()

    stackView.translatesAutoresizingMaskIntoConstraints=false
    self.view.addSubview(stackView)
    // Main UIStackView contraints, nearly fills its parent view
    self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-30-[stackView]-30-|",options: NSLayoutFormatOptions.AlignAllLeading,metrics: nil, views: ["stackView":stackView]))
    self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-10-[stackView]-10-|",options: NSLayoutFormatOptions.AlignAllLeading,metrics: nil, views: ["stackView":stackView]))

    stackView.axis = .Vertical
    stackView.alignment = .Fill
    stackView.spacing = 25
    stackView.distribution = .FillEqually

    var lbl = UILabel()
    lbl.text ="Label 1"
    lbl.backgroundColor = UIColor.redColor()
    stackView.addArrangedSubview(lbl)

    lbl = UILabel()
    lbl.text = "Label 2"
    lbl.backgroundColor = UIColor.greenColor()
    stackView.addArrangedSubview(lbl)

    nestedStackView.axis = .Horizontal
    nestedStackView.alignment = .Fill
    nestedStackView.spacing = 25
    nestedStackView.distribution = .FillEqually
    nestedStackView.addArrangedSubview(UIButton(type: .InfoDark))
    nestedStackView.addArrangedSubview(UIButton(type: .InfoLight))
    nestedStackView.addArrangedSubview(UIButton(type: .ContactAdd))
    stackView.addArrangedSubview(nestedStackView)

    let btn = UIButton(type: .System)
    btn.setTitle("Press Me", forState: .Normal)
    stackView.addArrangedSubview(btn)
}

为了指定主 UIStackView 是垂直方向布局的我们把 axis 属性设置成 .Vertical,前三个控件将会等间距排列,剩下的UIButton会填充剩余的可用空间。在嵌套 UIStackView 中的三个默认按钮也是用同样的方式来排列。alignmentdistributionspacing 三个属性会在下面单独讲解,这里我们先忽略它们。

有时候你可能需要将部分视图隐藏起来或者显示出来,这对于 UIStackView 来说实现起来是非常容易的,你只需要设置相应视图的 hidden 属性就可以。

为了方便测试,我们给 UIButton 添加一个 pressedMe 的点击事件响应方法:

    ...
    btn.setTitle("Press Me", forState: .Normal)
    btn.addTarget(self, action: "pressedMe:", forControlEvents: UIControlEvents.TouchUpInside)
    stackView.addArrangedSubview(btn)

}

func pressedMe(sender: UIButton!){
    UIView.animateWithDuration(0.5) {
        self.nestedStackView.hidden = !self.nestedStackView.hidden
    }
}

当点击这个按钮时,主 UISTackView 和 内部的 UISTackView 将会根据在 viewDidLoad 中设置的属性重新布局内部的子视图并带有短暂的显示或者隐藏动画效果。

如果需要,subviews 也可以完全从 UIStackView 中移除然后剩下的子视图也会根据各自的属性重新布局。

func pressedMe(sender: UIButton!){
   stackView.removeArrangedSubview(nestedStackView)
   nestedStackView.removeFromSuperview()
}

这个移除的操作需要两步,第一步是调用 removeArrangedSubview 方法用来从 UIStackView 删除视图并且重新布局剩余的 subviews 但是实际上并没有从父视图上删除。第二部就是调用 removeFromSuperview 方法以保证该视图从父视图中完全的被删除。

UIStackView: Alignment, Distribution And Spacing 属性

下面让我们详细了解下 UIStackView 的布局属性:

StackProperty
StackProperty

Axis 轴

定义子视图的布局方向,包含 Vertical(垂直) 和 Horizontal(水平)两个枚举值。

Alignment 对齐

alignment 属性指定了子视图在布局方向上的对齐方式,如果值是 Fill 则会调整子视图以适应空间变化,其他的值不会改变视图的大小。有效的值包含:Fill、 Leading、 Top、 FirstBaseline、 Center、 Trailing、 Bottom、 LastBaseline。

Distribution 分布

Distribution 属性定义了 subviews 的分布方式,可以赋值的5个枚举值可以分为两组: Fill 组 和 Spacing 组。

Fill 组用来调整 subviews 的大小,同时结合 spacing 属性来确定 subviews 之间的间距。

  • Fill: subviews 将会根据自己内容的内容阻力(content resistance)或者内容吸附优先级(hugging priority)进行动态拉伸,如果没有设置该值,subviews 中的一个子视图将会用来填充剩余可用空间。

  • FillEqually: 忽略其他的约束,subviews 将会在设置的布局方向上等宽或等高排列。

  • FillProportionally: subviews 将会根据自己的原始大小做适当的布局调整。

Spacing 组指定了 subviews 在布局方向上的间距填充方式,当 subviews 不满足布局条件或有不明确的的 Auto Layout 设置时,该类型的值就会结合相应的压缩阻力(compression resistance) 来改变 subviews 的大小。

  • EqualSpacing: subviews 等间距排列

  • EqualCentering: 在布局方向上居中的 subviews 等间距排列

Spacing 间距

spacing 属性根据当前 distribution 属性的值有不同方向的解释。

如果 UIStackView 的 distribution 属性设置的是 EqualSpacing、 EqualCentering,spacing 属性指的就是 subviews 之间的最小间距。相反如果设置的是 FillProportionally 属性,那么 spacing 的值就是严格的间距值。

iOS7+ 兼容

官方的 UIStackView 只在 iOS9 以上系统可以使用,但是其他的开源组织在低版本的系统上也实现了相应的功能:

本文由 SwiftGG 翻译组翻译,已经获得作者翻译授权,最新文章请访问 http://swift.gg

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,230评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,261评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,089评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,542评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,542评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,544评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,922评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,578评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,816评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,576评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,658评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,359评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,920评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,859评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,381评论 2 342

推荐阅读更多精彩内容