认识UIStackView

前言

首先,我们通过下面这张图片引出今天的主角

大家看到了什么,是爱吗?
不,这不是爱,不是爱,是满满的‘愁绪’?😂
此刻,面对这一堆约束,或许你的内心是淡然的,那么,如果情况是这堆约束里面存在一连串的Conflicting Constraints呢,是否感到崩溃。虽然对于这些警告和冲突,系统会有相应的提示,但错综复杂的约束会让你依然无从下手。一般情况下,对于修复类似稍微复杂的自动布局环境,如果你脑海里没有清晰的约束结构,个人建议还是重新来过,这样一般情况下会比你扯着头皮去解析来得省时省力得多。当然,如果使用UIStackView,我们就不必再为这成串的约束苦恼了,UIStackView能够自动管理它内部子控件的约束,从而会为我们省去大部分的手动约束。

UIStackView介绍

UIStackView 是 iOS 9 中新增的一个控件,它继承于UIView,用来管理一行或一列视图的布局。UIStackView新增了几个属性,这些属性就是子视图布局规则。一旦UIStackView的这些属性发生变化,它的arrangedSubviews就会按照规则重新排布。只要我们掌握这些规则,就可以管理视图布局了。如果能再稍加灵活运用,有时候我们甚至能轻松实现一些精妙布局。

这里说明下UIStackView的arrangedSubviews数组属性,它是subViews数组的子集合,我觉得有必要给大家重复下官方的几条说明:

  • 无论何时 stackView添加了一个视图到它的 arrangedSubviews 数组,其也将把这个视图作为子视图添加,如果还未添加的话。
  • 无论何时一个子视图从 stackVierw中被移除,那么 stackView也会将其从 arrangedSubviews 数组中移除。
  • 从 arrangedSubviews 移除一个视图并不会将其作为子视图移除。stack 视图将不再管理该视图的尺寸和位置,但是该视图仍将是视图结构的一部分,并且当其可见的情况下仍会被渲染到屏幕上。

以下是官方对StackView的布局属性解释:

  • axis(轴向) 属性决定了 stack 的朝向,只有垂直或水平;
  • distribution(分布) 属性决定了其管理的视图在沿着其轴向上的布局;
  • alignment(对齐) 属性决定了其管理的视图在垂直于其轴向上的布局;
  • spacing(空隙) 属性决定了其管理的视图间的最小间隙;
  • layoutMarginsRelativeArrangement 属性决定了 stack 视图平铺其管理的视图时是否要参照它的布局边距;
  • baselineRelativeArrangement 属性决定了 stack 视图平铺其管理的视图时是否要参照它的布局边距;

下图是官方对alignment和distribution以及Spacing三个属性的图解:

这里附上我制作的一个 示例Demo,里面主要使用UIStackView和sizeclass完成了Demo主页与StackView属性展示的iPhone横竖屏的适配,小伙伴们可以通过在属性界面调整StackView的属性来观察属性变化对StackView的布局影响。

UIStackViewDistribution和UIStackViewalignment

axis、spacing、distribution和alignment是比较重要的4个属性,他们都能给布局带来明显的变化。axis和spacing属性作用单一,通过属性解释或者通过视图简单观察我们就能理解他们的作用。distribution和alignment这两个属性相对而言更具灵活性,也更具有难度,尤其是二者的结合使用。
我们先来看一下这两个属性的定义

public enum UIStackViewDistribution : Int {
    case fill
    case fillEqually
    case fillProportionally
    case equalSpacing
    case equalCentering
}
public enum UIStackViewAlignment : Int {
    case fill
    case leading
    public static var top: UIStackViewAlignment { get }
    case firstBaseline
    case center
    case trailing
    public static var bottom: UIStackViewAlignment { get }
    case lastBaseline 
}

这里我介绍一下distribution的各类模式(图片来自官方文档):

  • UIStackViewDistributionFill

将arrangedSubviews填充满整个StackView,他们之间的间隙等于spacing大小。如果减去所有的spacing,所有arrangedSubview的固有尺寸(intrinsicContentSize)之和不能填满StackView,那么就按照Hugging的优先级将其拉伸。反之,如果超出StackView的尺寸则按CompressionResistance的优先级压缩。如果优先级相同,就按排列顺序来拉伸或压缩。

  • UIStackViewDistributionFillEqually

每个arrangedSubview沿axis方向的长度相等,等于StackView沿axis长度减去spacing之和除以arrangedSubviews个数。

  • UIStackViewDistributionFillProportionally

根据arrangedSubview的intrinsicContentSize,将StackView沿axis方向的长度减去spacing之和按比例分配给arrangedSubviews。

  • UIStackViewDistributionEqualSpacing

先按arrangedSubviews的intrinsicContentSize布局,然后余下的空间均分为spacing,如果spacing小于StackView设置的spacing,则按照CompressionResistance的优先级来压缩arrangedSubviews。

  • UIStackViewDistributionEqualCentering

令arrangedSubviews的中心点之间的距离相等,且spacing大于等于StackView设置的spacing(每两个arrangedSubview之间的spacing可能不相等)。如果spacing小于StackView设置的spacing,则按照CompressionResistance的优先级来压缩arrangedSubviews。

对于alignment属性,它决定的是垂直axis方向的对齐方式,从命名上我们也能看出个大概。有两点我们需要了解一下:

1、Top与Leading模式相同,Bottom与Trailing模式相同,从UIStackViewAlignment的定义中我们也能隐约猜出。

2、FirstBaseline和LastBaseline这两种模式是让arrangedSubviews分别按照firstBaseline和lastBaseline对齐。只能出现在axis为水平的StackView中。其实这两种alignment在垂直StackView中也是可以设置的,只是显示时有些怪异罢了)

storyboard添加StackView

方法1:从对象库中拖拽UIStackView到storyboard中,然后往内部扔控件(UIView或其子类)就可以了。

方法2:选择storyboard中的控件,可以用“ommand键 + 单击”进行多选,然后点击下方的stack按钮,这样选中的控件就会被放入一个StackView中


StackView使用技巧

1、嵌套
只要嵌套好UIStackView,就可以用很少的约束达到自动布局界面的目的。

2、增量排版
由于我们StackView中我们常用的alignment模式为Fill,因此会经常出现arrangedSubView被拉伸变形的情况,也许有人会想到给控件添加宽高约束,但这会与StackView自动生成的约束突出,并不能达到目的。这里我们可以将这个arrangedSubView装进一个StackView中,然后添加透明的UIView,通过添加UIView的宽度或者高度约束从而达到约束arrangedSubView的目的。

3、结合sizeClass
查看SatckView的Attributes Inspector,我们会发现StackView的几个主要属性都是可以设置sizeclass模式的,这对我们的屏幕适配将会大有助益。再加上约束的sizeClass,灵活性可以想象。

布局实践

下面我就带大家感受下Demo中是如何利用这三点技巧的。

首先附上Reveal中Demo首页视图结构层次:

在这个页面中,我粗略计算了下大约一共有14条约束,这其中还包括了横屏的适配,视图布局的优化。如果是粗糙布局,我们甚至可以用小于6条的约束完成布局。那么,问题来了,如果这些视图是纯粹用自动布局排版的,请问约束数目几何?

为了方便说明,我将Demo中的主页视图分为5个区域:

制作Demo时我采用的是整体到局部的思路,即先限定容器(最外层StackView)的大小,然后添加子容器(区域StackView),根据需求添加相应的内容以及判断是否还需要子容器。就拿区域1来说,它的结构是这样的:

这里添加UIView就是采用了增量排版,因为如果label的文字直抵屏幕边距影响美观,所以我让UIView为其创造边距。再如区域5的泊学logo,为了不使其被拉伸,我在两边添加了两个UIView,从而达到约束其宽度的目的。

针对横屏适配,我主要采用了以下几条策略

1、设置不同sizeClass模式下区域1的高度

2、区域2由三个模块组成(button+两个StackVIew),两个StackView宽度相同,wAny | hAny 模式下为0。在wAny | hCompact模式下添加Button的宽度约束,StackView就拥有了宽度。

3、通过高度约束,在wAny | hCompact模式下隐藏区域3,因为我们将它放在区域2显示了。

4、调整不同sizeClass下的spacing(横屏下高度资源不容浪费)

最后我们的视图在横屏下是这样的

StackView带给我们的好
大量减少自定义约束以及增强灵活度:曾经的约束就像用绳子将一个个控件捆在了一起,而现在的StackView更像一个个盒子将控件装了起来,我们可以用大盒子装小盒子,也可以将这个盒子的东西转移到另一个盒子。盒子的存在使得我们只要掌握最大的那个盒子,盒子内的器件也终将被我们掌握,毕竟盒子内的器件无论位置怎么变动,它始终存在于盒子之中。

层次关系: UIStackView的嵌套以及对arrangedSubViews的排列使得界面控件之间的结构层次更为清晰。代码布局:有了StackView,代码添加自动布局明显也方便了许多,无需像以前那样为每一条约束写一串语句,只要把视图分层包装好,通过设置UIStackView的属性,就可以自动布局整个界面了。总结

最后,我们在使用stackView的时候,尽量通过设置stackView的属性来完成布局,尽可能的减少内部子控件的约束,这样可以让视图更具灵活性,也避免了许多约束冲突,增强可维护性。灵活利用StackView的嵌套,再添加一点我们的小思维,让视图如同被赋予生命一般跃动起来吧!
stack确实给我们的开发带来种种益处,我们没有理由不去学习掌握它。而且如果大家想在iOS9以下系统使用UIStackView也并不是没有可能,sunny大神和他的同事利用运行时制作的 FDStackView 这个框架让你感觉仿佛可以在iOS6+环境下直接使用UIStackView一样,而且支持Interface build。

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

推荐阅读更多精彩内容

  • 翻译自“Auto Layout Guide”。 1 入门 1.1 理解自动布局 自动布局根据视图层级结构中视图上的...
    lakerszhy阅读 3,470评论 3 26
  • 我的博客, 各位看官有时间赏光 UIStackView UIStackView介绍 随着autolayout的推广...
    VIC_LI阅读 8,621评论 0 17
  • 这是一篇挺老的文章,主要就是介绍在iOS9时推出的控件UIStackView。我发现网上的资料并不算多,而AppC...
    Liberalism阅读 10,973评论 2 26
  • 距离iOS9发布已经接近一年了,我们即将引来新的iOS 10,为何在这个时候来介绍iOS9中新引入的一个布局组件呢...
    CZ_iOS阅读 7,362评论 9 58
  • UIStackView to resize/layout 自适应、适配、布局这几个关键词一直伴随着iOS开发,从以...
    萌新小透明阅读 2,005评论 1 1