UIStackView上手教程

原文:UIStackView Tutorial: Introducing Stack Views
最初教程由Jawwad Ahmad原创。Kevin Colligan更新支持iOS 11、Xcode 9和Swift 4。
转载注明:http://www.rockerhx.com/2018/08/21/2018-08-21-UIStackView-Tutorial/

iOS开发当中是不是遇到需要动态添加或者删除视图元素的需求。你是上基hub去嗨一遍第三方库还是自行撸frame,或者用Auto Layout的约束更新。就这点破需求,反正我是坚决不选前两者,即便是万不得已,最多也是更新约束(虽然Auto Layout的所见即所得极大缩短了开发耗时,增加我撸猫的快乐时光,但不得不承认我是很反感用代码去改约束,可视化编码还要去code约束,脑子有病!!!)。
处理这类需求,总之一句话。


image

好在,现在大家伙应该都只支持到iOS9了,用上UIStackView来处理这类需求简直是美滋滋,艾玛真香~~~
在本教程中,阔以了解UIStackView如何提供一种简单的方式来处理水平或垂直布局。还阔以了解如何通过使用对齐、分布和间距等属性来获取视图,以便自我调整方便自适应。

本教程假定观者基本熟悉Auto Layout。如果不熟悉,请移步传送门Beginning Auto Layout

教程开始

image

在这份UIStackView教程中,我们将使用一个名为“Vacation Spots”的应用程序。这个简单的应用阔以展示度假地点的一些基础信息。
不要方方脏脏的就开始动手,因为有几个问题需要我们先用UIStackView来处理下,而且比单独使用自动布局要简单得多。首先下载这个UIStackView教程的入门项目。打开项目并在iPhone模拟器上运行。你会看到一份度假地点清单。

image

点击London那一栏,进入伦敦的信息视图。乍一看,好像还不错,但有几个问题。

  • 看看视图底部的一排按钮。他们目前的位置是固定的,所以他们不适应屏幕宽度。不信按Command - left将模拟器横向来看看。


    image
  • 点击WEATHER旁边的Hide按钮。它成功地隐藏了文本,但是它没有重新定位它下面的部分,留下一块空白。
    image
  • 可以改进这些部分的展示顺序。如果what to see部分正好位于why visit部分之后,而不是在两者之间有weather部分,这将会更合乎逻辑。
  • 在横屏模式下,按钮的底部离视图的底部边缘太近了一点。如果能减少元素之间的间距,那就更好看。

既然我们已经罗列好了改进工作,那就动手开始吧!!!

打开Main.storyboard。第一次这样做时,会要求您选择初始设备视图。这个视图在运行时没有效果,它针对不同的设备调整大小,也只是让故事板的使用变得更加容易。iPhone 7或者8就行了。

image

你阔以随时点击故事板的操作区域的正下方,选择View As: iPhone 7 ,或者随便改变也行。
image

现在打开
Spot Info View Controller
看看,这东一块西一坨的颜色是什么鬼?
image

这些标签和按钮的各种背景色,运行时会被清除。在Storyboard中,它们只是视觉上的辅助,帮助显示改变StackView的各种属性将如何影响其嵌入视图的布局。

你现在不用管这些,如果在运行应用程序的时候你真的想看到背景色,你可以在SpotInfoViewController内部的viewDidLoad()中临时注释出以下几行。

// Clear background colors from labels and buttons
for view in backgroundColoredViews {
    view.backgroundColor = UIColor.clear
}

此外,所有的UI元素都使用了明确的占位内容,以便链接属性的时候更加明确方便,减小连错的可能性。

@IBOutlet weak var whyVisitLabel: UILabel!

叨逼叨完了,let’s get started!

第一次

使用stack view第一件事是修复按钮底部行之间的间距。stack view可以以各种方式沿其轴分布其视图,其中一种方式是每个视图之间的间距相等。
苹果为了推广新控件,我们可以直接把想要的处理的控件元素直接一键压入stack view。打开Spot Info View Controller故事版场景,按住Command选中底部三个按钮。

image

点击右下角Show Document Outlin

image

在左侧控件集列表内确认下是不是选对了按钮:


image

选中后,单击故事板画布右下角自动布局工具栏中的“新建堆栈”按钮:


image

按钮将嵌入到新的堆栈视图中:


image

现在出现了约束警告,stack view只是一种排版形式,是继承自UIView,它也是需要约束来确定frame,只有确定好自身的frame以后才能处理内部控件布局。

stack view中嵌入控件时,将删除对其的任何约束。例如,在将按钮嵌入stack view之前,Submit Rating按钮的顶部有一个连接到Ratinglabel的垂直间距约束:

image

点击选中Submit Rating按钮可以看到它不在包含任何约束。

image

也阔以通过Size inspector (⌥⌘5)来查看控件约束情况:

image

接下来我们需要为stack view添加约束控制,如果整个页面的控件太多导致不好选中某个控件,直接从左侧的控件列表就好。

image

另外有个小技巧,按住Shift再单击右键,Xcode会帮大忙。

image

最后通过右下角的Auto Layout toolbar添加约束即可。

image

保持Constrain to margins选中,然后添加如下约束。

Top: 20, Leading: 0, Trailing: 0, Bottom: 0

按照如图所示输入数字直接tab就好,简单而快速的添加约束才是敏捷开发的首选。


image

控件之间现在看起来像是这样,stack view会拉伸第一个控件来填充空间。

image

决定stack view内部控件的分布属性为distribution。目前,它被设置为Fill,这意味着被包含的控件将沿着其轴完全填充。为了实现这一点,stack view将只拉伸其中一个视图来填充额外的空间;具体来说,它会拉伸content hugging优先级最低的视图,或者如果所有控件优先级相同,它拉伸第一个视图。

然鹅,我们只是向他们等距分布就好。切换到Attributes inspector属性编辑器,把Distribution的值从Fill改到Equal Spacing即可:

image

跑一下看看底部按钮是不是等距分布了,无论是竖屏还是横盘都是稳稳的,不过还不够完美,当你切换到小屏幕上,比如使用iPhone SE模拟器来跑一下看看。
[图片上传失败...(image-b4cef2-1535097314156)]

好在苹果也考虑到这种情况,我们改变Distribution属性,从Equal Spacing改为Fill Proportionallyspacing值改为10

image

现在,在iPhone SE模拟器上再跑一次,这次必须棒棒哒。


image

咋样,第一次上手stack view484真香,so easy。

image

在没有stack view,你必须使用间隔视图来占位,每对按钮之间有一个占位。要正确定位间隔视图,您必须向所有间隔视图添加等宽约束以及许多附加约束。
它看起来会像下面这样。为了在屏幕截图中可见,间隔视图被赋予了浅灰色背景:

image

如果只是偶尔这么做一次其实还好,如果做多了,小伙子身体可吃不消啊。特别是动态添加视图这种。比如隐藏例子中的WEATHER这种。

现在我们只是简单的设置间距值,如果你想在某个视图中设置特别的间距,在iOS11里提供的新的Api: setCustomSpacing:afterView

沙场老司机

经过上面的演练,下面我们就能很轻松的把SpotInfoViewController里需要的结构转换成stack view

评级部分

选中评级RATING部分的两个控件。

image

接着还是同样的点击Stack按钮将其嵌入到stack view里。

image

这次我们只需要添加三个约束即可:

Top: 20, Leading: 0, Bottom: 20
image

spacing值这是为8

image

你可能会看到一个错位的视图警告,如下图所示,其中星星标签已经超出了视图的范围(这种警告可能因为某些beta版本的原因才会出现,没出现那就恭喜你继续):


image

如果确定自己的约束是正确的,因为Xcode某些版本的bug引起的视图错位,直接用右下角的Refresh Layout按钮矫正即可。

image

现在看起来就正常了。


image

还原视图

如果发现有的时候手残,或者是处于实验性目的,多上了stack view,直接选中需要撤销的控件,按住Option,左击StackUnembed即可撤销。

image

或者选中控件从菜单Editor \ Unembed撤销也行。

试试第一个垂直方式

现在我们来创建垂直方式的堆栈视图,选中WHY VISIT<whyVisitLabel>两个文本控件。

image

Xcode会根据控件的分布排列来确实方向,就比如现在这样,直接生成的就是垂直分布的堆栈视图。


image

现在我们来确定约束,只需要把这部分的上左右约束都设置为0,只不过底部约束的对象是相对WEATHER控件,间距是20,一看即懂。

image

设置完后默认对齐是左对齐,如下图所示:


image

对齐属性

对齐属性确定堆栈视图如何垂直于其轴布局。对于垂直堆栈视图,可以设置的对齐方式为:

  • 填充·Fill
  • 靠左对齐·Leading
  • 居中对齐·Center
  • 靠右对齐·Trailing

水平堆栈视图对齐属性的值略有不同:


image

水平布局的对齐方式分别为:

  • 顶部对齐·Top
  • 靠左对齐·Leading
  • 底部对齐·Bottom
  • 靠右对齐·Trailing
  • 第一元素基准线·FirstBaseline:意思是以第一个UI元素的的内容基准线为准,后面元素内容以此基准线对齐。
  • 最后元素基准线·LastBaseline:意思是以最后一个UI元素的的内容基准线为准,前面元素内容以此基准线对齐。

大家阔以选中刚才设置好的垂直堆栈视图,调整下对齐属性看下变化就浅显易懂了。
填充·Fill:

image

靠左对齐·Leading:
image

居中对齐·Center:
image

靠右对齐·Trailing:
image

跑起来看一下布局稳当冇问题。

转换“what to see”部分

这部分转换和前面一节没啥区别,直接操作关键点即可。

  • 首先选中WHAT TO SEE<whatToSeeLabel>两个文本元素。
  • 其次压入堆栈视图。
  • 对齐方式选中填充·Fill。
  • 最后设置好如下约束即可。
Top: 20, Leading: 0, Trailing: 0, Bottom: 20

设置完以后大概就长这样:


image

转换“weather”部分

由于需要隐藏天气,所以这部分是比较难搞的。

我们先把这部分的所有元素都压入堆栈视图。先选中WEATHER文本栏和Hide按钮,压入水平堆栈视图,然后在按住Command选中下面的<weatherInfoLabel>文本栏部分,将水平堆栈视图和这部分一起压入垂直堆栈视图。

image

其实可以发现,水平的堆栈视图被Hide按钮给顶开了,如果觉得不喜欢,阔以选中底部对齐的方式来看看效果。

image

也还是不太满意,那根据我的经验,要不就是修改水平堆栈视图内UI元素的约束优先级,要不就是把Hide按钮给拆出去,单独处理约束。为了容易理解我们还是单独处理。

自行撤回到天气部分的原始转态。选中WEATHER<weatherInfoLabel>两个文本栏部分:

image

将其压入垂直堆栈视图:


image

然后设置好如下约束:

Top: 20, Leading: 0, Trailing: 0, Bottom: 20

对齐方式为填充:


image

因为Hide按钮被拆出去,所以我们需要对其添加约束以保证按钮位置的正确性。因为需要设置约束的相对控件为WEATHER文本栏,所以我们还得再改造改造。
直接在控件列表栏选中WEATHER或使用组合拳Control-Shift-click

image

压入堆栈视图:


image

对齐方式·Alignment选中为靠左对齐·Leading排版·Axis确定为垂直·Vertical排版:

image

运行看看是啥情况:


image

我顶你个肺,啥情况,按钮跑偏了?实际上是我们拆出Hide按钮之后忘记给设置约束。
按住Control选中Hide按钮拖动指向到WEATHER文本栏添加约束:

image

只要完成如下两条约束,间距和纵向位置的约束即可:


image

再重新跑一下,这回就必须妥当了。


image

堆栈化

在左侧控件列表里选中我们之前所有做个堆栈化的控件:


image

全部压入堆栈:


image

然后给最外层堆栈视图添加约束,确保勾选上Constrain to margins,四边边距约束为0即可。
然后间隙·Spacing设置为20对齐方式·Alignment设置为填充·Fill

image

再次运行还是会有刚才那个屌问题。


image

只要记住如果有外设相对控件的约束,只要堆栈化,约束就会被干掉,只能再次添加了:


image

完善层级

最后我们需要把what to see的部分挪动到weather部分上面去,最好的方式是在左侧控件列表里拖动:

image

如果在Storyboard里直接去拖动,很可能会因为选错控件元素而导致打乱结构。

根据屏幕大小配置间隙

由于在横屏的情况下,垂直方向上的空间比较珍贵,所以针对横屏,把间隙改为10比较稳妥。
找到间隙·Spacing的左侧的+号按钮:

image

选择Any Width > Compact Height,然后添加·Add Variation

image

wAny hC栏添加值为10即可:

image

最后运行一下看看,包括横屏(⌘←)也看看,484看起来比较舒爽了。


image

动画

当前的隐藏和呈现动画效果看起来有木有点生硬,感觉怪怪的。我们还是添加点动画让细节看起来比较丝滑为妙。
打开SpotInfoViewController.swift,定位到updateWeatherInfoViews(hideWeatherInfo:animated:)方法,现在看起来长这样:

weatherInfoLabel.hidden = shouldHideWeatherInfo

替换成如下代码即可:

if animated {
    UIView.animate(withDuration: 0.3) {
        self.weatherInfoLabel.isHidden = shouldHideWeatherInfo
    }
} else {
    weatherInfoLabel.isHidden = shouldHideWeatherInfo
}

找到@IBAction func weatherHideOrShowButtonTapped(_ sender: UIButton)方法,替换成如下代码:

@IBAction func weatherHideOrShowButtonTapped(_ sender: UIButton) {
    let shouldHideWeatherInfo = sender.titleLabel!.text! == "Hide"
    updateWeatherInfoViews(hideWeatherInfo: shouldHideWeatherInfo, animated: shouldHideWeatherInfo)
    shouldHideWeatherInfoSetting = shouldHideWeatherInfo
}

这样跑一下看,隐藏起来就不会辣么尬了。。。

写在最后

一切照旧,懒癌患者或者阅读理解不过关的胖友阔以直接下载最终代码

有关UIStackView的使用就到这里,关于使用UIStackView做高级动画是稍稍有点复杂的,以后有空还是需要专门开一篇来做介绍。

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

推荐阅读更多精彩内容

  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明先生_X自主阅读 15,969评论 3 119
  • 今天阳光正好 我想说 你的阔腿裤在风中 显得有些臃肿 但你的好心情我是知道的 今天你有点愤世嫉俗 牢骚发了一路 但...
    喜宁123阅读 254评论 2 4
  • 人的一生当中,最有意义的事情就是工作,如果说,与同事相处是一种缘分,那么与顾客、生意伙伴见面就是一种乐趣。 ...
    3e1094b2ef7b阅读 1,216评论 1 1
  • 2.17.4.10 星期一 累计71 一、目标: 本期目标实现财富收入50万元。通过我的目标实现,希望能帮助更...
    鹊曾阅读 241评论 0 1
  • 今年入职了某厂,金三银四嘛,流动大,入职后部门还在招人,能看到后续其他人的面试反馈邮件,我进来当时的面试体验,感觉...
    lxfeng阅读 325评论 0 1