第2章:规划一个分页的应用程序¶
在本书的第1节中,你将建立一个应用程序来帮助你进行高强度的间歇训练。即使你已经在使用Apple Fitness+或许多锻炼应用程序中的一个,也可以通过这些章节来学习如何使用Xcode、Swift和SwiftUI来开发一个iOS应用程序。
在这一章中,你将计划你的应用程序,然后设置分页界面。您将开始使用SwiftUI属性检查器来添加修改器。在接下来的两章中,你将学习更多的Swift和SwiftUI来布置你的应用程序的视图,创建你的应用程序的
制作列表:视图和行动¶
这个应用程序有几个屏幕。下面是一个样本,向你展示你完成后的应用程序的样子。

这些屏幕上有很多事情,特别是有锻炼视频的那一个。你可能会感到不知所措,不知道该从哪里开始。好吧,你听说过"分而治之"这句话,这就是解决构建应用程序问题的最佳方法。
首先,你需要清点一下你要划分的内容。最顶层的划分是用户看到的东西和应用程序做的东西。许多开发者从布置屏幕开始,通常是在一个设计或原型应用程序中,让他们表明基本功能。例如,当用户点击这个按钮时,应用程序会显示这个屏幕。你可以向客户或潜在用户展示一个原型,看看他们是否理解你的应用程序的控制和功能。例如,如果他们点击标签以为是按钮,你应该重新考虑标签的设计,或者把它们实现为按钮。
列出你的用户看到的东西¶
首先,列出你需要创建的屏幕并描述其内容:
- 一个带有文字、图像和一个按钮的欢迎屏幕。
- 标题和页码在
Welcome屏幕的顶部,History按钮在底部。这些也都在练习视频的屏幕上。页码表示在这一页之后还有四个编号的页面。挥舞的手的符号被突出显示。 - 练习视频的屏幕上也有一个计时器,一个
Start/Done按钮和评级符号。其中一个页码被突出显示。 History屏幕以列表和柱状图的形式显示用户的练习历史。它有一个标题,但没有页码,也没有History按钮。High Five!屏幕有一个图像,一些大的文字和一些小的灰色文字。与History屏幕一样,它没有页码,也没有History按钮。
在本章和下一章中,你将布置这些屏幕的基本元素。在第10章"完善你的应用程序"中,你将对外观进行微调,使其看起来像上面的屏幕截图。
列出你的应用程序的功能¶
接下来,列出每个屏幕的功能,从最后两个开始。
History和High Five!屏幕是模版,在Welcome或Exercise的屏幕上滑起来。每个屏幕都有一个按钮,用户可以点击它来解散它,要么是一个圈起来的X,要么是一个Continue按钮。- 在
Welcome和Exercise屏幕上,匹配的页码是黑色背景上的白色文字或轮廓。点击History按钮会显示History屏幕。 Welcome页的Get Started按钮显示下一页。- 在
Exercise页面上,用户可以点击播放按钮来播放练习的视频。 - 在
Exercise页面上,点击Start按钮会启动一个倒计时,按钮的标签会变成Done。理想情况下,Start按钮是禁用的,直到计时器达到0。点击Start会将这个练习添加到用户当天的历史中。 - 在
Exercise页面上,点击五个评级符号中的一个,可以改变该符号和前面所有符号的颜色。 - 在最后一个练习上点选
Done,会显示High Five!屏幕。 - 不错的选择。点击一个页码就可以进入该页。在
Exercise页上点击Done可进入下一个Exercise页。关闭High Five!屏幕,返回到Welcome页。
你将在第六章"为你的应用程序添加功能"中实现所有这些功能。
还有HIITFit的基于页面的总体结构。这在SwiftUI中很容易实现,所以你将在创建任何屏幕之前先做这个。
创建页面¶
本节将学习的技能:向现有项目添加Git仓库;SwiftUI视图的可视化编辑;使用弹出式属性检查器;TabView样式。
本节的主要目的是设置HIITFit的基于页面的结构,但你也会学到很多关于使用Xcode、Swift和SwiftUI的知识。每一节开头的技能的简短列表可以帮助你追踪到什么地方。
将源码控制添加到一个现有的项目中¶
在第1章"检查你的工具"中,你创建了一个新项目。但HIITFit有一个带有一些资产和实用代码的启动项目。你要向这个启动项目添加文件和代码。
➤ 在Finder中打开本章的starter文件夹,找到HIITFit/HIITFit.xcodeproj。双击-此文件,在Xcode中打开它。
这个项目没有一个Git仓库。你可以使用git init命令行,但Xcode提供了一个快速的方法。
➤ 在Xcode菜单中,选择Source Control ▸ New Git Repositories...。

出现一个窗口,选中你的项目。

➤ 点击创建。
现在,当你修改或添加文件时,你会看到标记,而且你可以在构建这个应用时提交每一个主要的添加内容。我建议先进行几次提交,然后由你来确保在进行新任务之前提交一个工作副本。
画布和编辑器始终保持同步¶
你即将体验到SwiftUI的最佳功能之一。编辑画布的同时也在编辑代码,反之亦然!
这是你的第一个SwiftUI词汇。你在设备屏幕上看到的所有东西都是一个视图,较大的视图包含子视图。
你的下一个SwiftUI术语是修改器。SwiftUI有大量的方法,你可以用来修改视图的外观或行为。
➤ 首先,在ContentView.swift中,从body闭包中删除.padding():这是一个修改器,在Text视图周围添加空间,你现在不需要它。
➤ 在画布中,如果没有看到ContentView就点击Resume,然后双击Text视图。你已经在代码中选择了Hello world:

➤ 现在输入欢迎:你已经改变了画布和代码中的文本。

Xcode
在输入Welcome后,不要按回车键。如果有必要,刷新预览。
Text视图只是显示一串字符。它对于列出你计划创建的视图很有用,就像一种大纲一样。现在你将使用多个Text视图,以了解如何实现分页行为。
➤ 在Text视图之外的任何地方单击,取消选择Welcome,然后再次选择Text视图。按Command-D:

正如你可能预料的那样,你已经重复了Text视图。但请看代码:
VStack {
Text("Welcome")
Text("Welcome")
}
你的两个Text视图现在被嵌入到一个VStack中! 当你有一个以上的视图时,你必须指定如何在画布上安排它们。Xcode知道这一点,所以它提供了默认的安排,即在一个垂直的堆栈中显示两个Text视图。
➤ 将V改为H,可以看到两个视图以水平堆叠的方式显示:

➤ 键入Command-Z来撤销此更改。SwiftUI的默认值往往与大多数人想做的事情相吻合。
➤ 将第二个Welcome改为Exercise 1。然后重复Exercise 1,将第三个字符串改为Exercise 2。

现在你有三种不同的视图可以在TabView中使用。
使用TabView¶
下面是创建一个TabView的简单方法。
➤ 将VStack改为TabView:

你的练习去哪里了!?嗯,它们现在是标签视图的第二和第三标签,屏幕底部有一个标签栏。它是空白的,因为你还没有给标签贴上标签。
这里是你如何标记标签的。实际上,这样做非常快,但看起来很费劲,因为你将学习如何使用SwiftUI属性检查器。
➤ 在画布或编辑器中,用Control-Option单击Welcome``Text视图,弹出其属性检查器。

Swift
show-inspectors按钮(右上角的工具栏)打开右侧的面板。属性检查器是这个面板中最右边的标签。如果你在一个小的屏幕上工作,并且只想编辑一个属性,Control-Option-click一个视图来使用弹出的检查器。它使用的空间较小。
➤ 在添加修饰符字段中单击,然后输入选项卡,并从菜单中选择选项卡项目:

一个新的tabItem修改器出现在编辑器中,有一个项目标签的占位符:
Text("Welcome")
.tabItem { Item Label }
并在标签栏中出现一个蓝色的标签:

➤ 选择项目标签占位符并输入Text("Welcome"):
.tabItem { Text("Welcome") }
它就在标签栏里:

➤ 用以下内容替换整个TabView,以添加其他标签的标签:
TabView {
Text("Welcome")
.tabItem { Text("Welcome") }
Text("Exercise 1")
.tabItem { Text("Exercise 1") }
Text("Exercise 2")
.tabItem { Text("Exercise 2") }
}
现在你可以看到三个标签:

画布预览是获得视图外观持续反馈的好方法,但是,在这一点上,你可能想看到它的实际效果。这就是实时预览可以帮助的地方。
➤ 单击实时预览按钮,看看这在设备上是如何工作的:

第一次需要一些时间,但随后的实时预览会刷新得更快。如果出现恢复按钮,请点击它或按Option-Command-P。
现在,点击一个选项卡标签来切换到该选项卡。这就是标签视图通常的操作方式。要使标签的行为像页面一样,请将此修改器添加到TableView中:
.tabViewStyle(PageTabViewStyle())
现在你的标签不见了!
页面风格使用小的索引点,但它们是白底的,所以你看不到它们。
➤ 为了使它们显示出来,在tabViewStyle下面添加这个修改器:
.indexViewStyle(
PageIndexViewStyle(backgroundDisplayMode: .always))
现在你可以看到索引点了:

➤ 查看实时预览:只要向左或向右滑动,每个页面就会卡住。

➤ 你不会在这个应用中使用tabItem标签,所以要删除它们。这就是TabView闭包内的所有代码:
TabView {
Text("Welcome")
Text("Exercise 1")
Text("Exercise 2")
}
好了,你已经设置了分页行为,但你希望这些页面是实际的Welcome和Exercise视图,而不仅仅是文本。为了使你的代码有条理并易于阅读,你将在自己的文件中创建每个视图,并将所有的视图文件放在一个文件夹中。
分组文件¶
本节将学习的技能:创建和分组项目文件
你即将通过组合较小的子视图来创建Welcome和Exercise子视图。SwiftUI鼓励你创建可重复使用的子视图,这与你创建函数的原因相同。不要重复自己的工作。即使你不重复使用一个子视图,它也会使你的代码更容易阅读。而且,SwiftUI将子视图编译成高效的机器代码,因此您可以创建您需要的所有子视图,而不必担心性能问题。
➤ 在项目导航器中选择ContentView.swift。创建一个名为WelcomeView.swift的新SwiftUI视图文件。然后,创建另一个新的SwiftUI视图文件,名为ExerciseView.swift。
你的项目导航器现在包含三个视图文件:

你会再创建几个视图文件,所以现在你要用这三个文件创建一个组文件夹,并命名为Views。
➤ 选择这三个视图文件,然后右键单击并选择从选择New Group from Selection:

➤ 命名组Views。
组文件夹只是帮助你组织项目中的所有文件。在第5章,"组织您的应用程序的数据 "中,您将为您的应用程序的数据模型创建一个文件夹。
传递参数¶
本节将学习的技能:default initializers;Array;let, var, Int;错误信息中的修复按钮;自动完成中的占位符
➤ 回到ContentView中,用你的新视图替换前两个Text占位符:
TabView {
WelcomeView() // was Text("Welcome")
ExerciseView() // was Text("Exercise 1")
Text("Exercise 2")
}
Swift
View是一个结构体,在Swift代码中被简称为struct。像一个类一样,它是一个复杂的数据类型,封装了属性和方法。如果一个View没有未初始化的属性,你可以用它的默认初始化器创建一个实例。例如,WelcomeView()创建一个WelcomeView的实例。
现在呢?你的应用程序将使用ExerciseView来显示几个不同练习的名称和视频,所以你需要一个方法来索引这些数据,并将每个索引传递给ExerciseView。
实际上,首先你需要一些练习的样本数据。在Videos文件夹中,你会发现四个视频:

Note
如果你喜欢使用你自己的视频,把它们从Finder拖到项目导航器中。一定要选中添加到目标复选框。

➤ 在第5章"组织你的应用程序的数据"中,你将创建一个Exercise数据类型,但对于这个原型,在ExerciseView.swift中,只需在ExerciseView的顶部创建两个数组,就在var body上方:
let videoNames = ["squat", "step-up", "burpee", "sun-salute"]
let exerciseNames = ["Squat", "Step Up", "Burpee", "Sun Salute"]
Swift
数组是一个结构实例或类对象的有序集合。数组中的所有实例或对象都是同一类型。
视频名称与视频文件的名称一致。练习名称对用户来说是可见的,所以要使用标题大写和空格。
➤ 还是在ExerciseView里面,在var body属性上面,添加这个属性:
let index: Int
Swift
Swift区分了用let创建常量和用var创建变量。
Xcode现在抱怨previews中的ExerciseView(),因为它缺少index参数。
➤ 单击红色错误图标以显示更多信息:

Xcode经常建议一个或多个方法来修复一个错误。很多时候,它的建议是正确的,这就是其中之一。
➤ 点击修复,让Xcode填入index参数。
➤ 现在有一个index值的占位符 - 一个灰色的Int。点击它,使其变成蓝色,然后输入0。所以现在你有了这行代码:
ExerciseView(index: 0)
Swift
像其他C语言的后代一样,Swift数组从0开始计数,而不是1。
现在使用你的index属性来显示每个练习的正确名称。
➤ 将Hello, World!改为此index值的练习名称。
Text(exerciseNames[index])
回到ContentView.swift中,Xcode也在抱怨ExerciseView()中缺少index参数。

➤ 用你在ExerciseView.swift中的同样方法来修复这个错误。
现在有一个占位符,用来表示index值。你应该在这里输入什么?
循环¶
本节将学习的技能:ForEach, Range与ClosedRange;开发者文档;带参数的初始化器
好吧, 你可以传递第一个数组索引:
ExerciseView(index: 0)
然后复制-粘贴和编辑,指定其他三个练习,但有一个更好的方法。你可能迫不及待地想使用一个循环。下面是你如何挠这个痒痒的。]
➤ 用这段代码替换TabView中的第二和第三行:
ForEach(0 ..< 4) { index in
ExerciseView(index: index)
}
ForEach在0到4的范围内循环,但由于<符号的存在,不包括4。每个整数值0、1、2和3都会创建一个index值的ExerciseView。本地变量的名称index由你决定。你可以写这样的代码代替:
ForEach(0 ..< 4) { number in
ExerciseView(index: number)
}
开发者文档¶
➤ 这是一个很好的机会来检查Xcode的内置文档。按住Option键,然后点击..<。

您正在查看Xcode弹出的半开范围运算符的快速帮助。您也可以在快速帮助检查器中查看这些信息。
➤ 现在选择点击ForEach:

➤ 这里的信息不多。单击在开发人员文档中打开以查看更详细的信息。

创建一个视图集合的主题包含三个初始化器。第一个是你用来在数组索引上循环的那个。
init(Range<Int>, content: (Int) -> Content)
➤ 这个ForEach初始化器需要Range<Int>。点击这一行,打开init(_:content:)页面,然后点击其声明中的Range,打开Range页面。果然,Range是"从一个下限到但不包括一个上限的半开区间",这与你看到的快速帮助中的..<相匹配。
➤ 关闭文档窗口。
➤ 你将不需要TabView的索引点。打开ContentView.swift并更改:
.tabViewStyle(PageTabViewStyle())
.indexViewStyle(
PageIndexViewStyle(backgroundDisplayMode: .always))
为:
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
现在,你将永远不会显示索引点了。
这是一个很好的地方,可以提交你对你的项目所做的修改,并写上提交信息,如"设置分页标签视图"。
Xcode
要提交修改到你的本地Git仓库,选择Source Control ▸ Commit... 或按Option-Command-C。如果有要求,请检查所有更改的文件。输入一个提交信息,然后点击提交。
你仍然在ContentView中,所以可以实时预览你的应用程序。从一个页面扫到下一个页面,看看不同的练习名称。

关键点¶
- 通过列出用户将看到什么和应用程序将做什么来规划你的应用程序。
- 用视图和子视图构建你的应用程序,用修改器进行自定义。
- 画布和代码编辑器始终是同步的。你在其中一个中所作的修改也会出现在另一个中。
- 在
VStack中垂直布置多个视图,或在HStack中水平布置。 - 属性检查器帮助你修改一个视图或预览。
ForEach让你在一个半开的数字范围内循环。TabView可以表现得像一个标签视图或像一个页面控制器。- 你可以在画布上预览或实时预览你的视图。
从这里开始,该往哪里走?¶
你已经学习了很多关于Xcode、Swift和SwiftUI的知识,只是为了创建你的应用程序的分页界面。带着你的用户看到的清单,你将在接下来的两章中创建HIITFit原型的视图。