iOS 12 Swift 编程基础:介绍与第一章
学习如何使用 Xcode 创建 iOS 应用,同时学习 Swift。(如果你曾经想了解 iOS 应用开发,可以看看截图。)
引言
我最喜欢的一句名言/格言来自天文学家、《布谷鸟的蛋》一书的作者克利福德·斯托尔。在那本书中,他给出了天文学家的经验法则:
克利福德·斯托尔如果你没有写下来,那它就没有发生过。
我认为软件开发也是如此。我过去做过的一些事情,现在已经如此遥远,仿佛是别人编写的那些程序。作为开发者,我们常常急于投入一些新的技术、语言或 API,试图让某些东西运行起来,然后我们转向其他事情,并在一个月或更短的时间内忘记了大部分所学。
我也喜欢记录我正在学习的东西,以帮助这些材料融会贯通。因为我太容易忘记我做过和学过的东西,所以我喜欢一边学习一边记录,作为一种工程师笔记。最后,我喜欢分享我学到的东西,以此作为与对相同话题感兴趣的人建立联系和社区的方式。
最后,在记录事情时,我喜欢尽可能地对材料进行逐步讲解,我发现最好的方法是使用大量的截图,所以你会在第一章中找到三十多张图片。这提供了一种在您甚至打开开发环境之前就能体验材料的方式。
背景
我的第一个 iOS 应用
几年前,我开发了一个 iOS 应用(适用于 iPhone 和 iPad),当时我只学了足够的 Xcode(iOS 开发环境)和 Swift,以尽快让它运行起来。
我还将该应用开发为 WinForm 应用、Web 应用、Android 应用和 UWP(通用 Windows 平台)应用。
然而,现在我有很多其他想为 iOS 编写的应用,我发现我几乎不记得 Swift、Xcode 和其他东西了,所以我决定需要再次专注于学习 iOS 开发。
我想学习创建应用
最近,我开始阅读一本非常棒的书,它突然将重点放在纯 Swift 和 Swift 的函数式编程部分,以至于到了第二章中间就变得无聊了。我正在寻找一些不同的东西。我想要一本能教我语言和开发环境如何应用于创建应用的书。
我对一本书有很多期望,但我无法完全找到它们,所以我决定自己尝试一下,这就是这本书的由来。
在第一章中,我们将立即开始构建一个应用,它将为您提供 Xcode IDE(集成开发环境)的概述和一些非常基本的 Swift 语言概念。之后,每一章都将引导您构建应用,帮助您了解 iOS 开发的感觉,并希望为您提供一些以后可能会用到的 iOS 实用工具应用。
最佳学习方式:编写代码并构建
我发现学习编程和编程语言最好(也是最令人难忘)的方式是使用该语言构建应用。
当然,在此过程中解释代码也很重要,所以我们添加的每一段代码我都会这样做。我将引导您完成我们遇到的步骤。这有点像您和我坐在一起,在我设计应用和编写代码时,您从我身后看着(全程都有截图)。然后,在您看完我的演示之后,您可以尝试自己学习这些材料,这样您就拥有了这些材料。
不重复,不提供安装指南
然而,一旦我涵盖了某个内容,我就不会重复,因为你总是可以回顾并弄清楚如何操作。此外,我不会花费宝贵的时间向您展示如何安装 Xcode 开发环境(Apple 的 IDE(集成开发环境))等事情。这些内容在互联网上都有很好的文档记录,如果您对开发 iOS 应用感兴趣,您已经有足够的动力和经验来知道如何按照向导在您的 Mac 上安装应用程序。
这是您将在本章中构建的简单应用的 GIF 动图。
本书的主要目标
本书的主要目标是帮助您学习设计和构建实用应用。
您可能正在尝试创建一个应用来帮助您完成某些事情,并且您不感兴趣将这些应用分享到 Apple Store。这没关系,您也可以这样做,但是,我们还将学习如何部署您的应用,以便您可以与拥有 iPhone 和/或 iPad 的所有人分享它们。
考虑到所有这些,让我们开始制作您的第一个应用吧。
启动 Xcode
当您在 Mac 上启动 Xcode 应用程序时,您会看到一个类似于下面所示的窗口。
在左侧,您可以选择以下三个操作之一。
我们将选择第二个,“创建一个新的 Xcode 项目”,但我会解释其他两个,只是让您知道它们是什么。
从 Playground 开始
第一个是“从 Playground 开始”。这是一个迷你开发环境,允许您使用 Swift 编程语言进行实验,这样您就可以编写代码片段并尝试该语言。该选项不需要(也不允许)您创建应用或任何 GUI(图形用户界面)项,以便您只学习 Swift 编程语言。由于我告诉过您本书的重点是创建 iOS 应用,因此我们不会在本书中使用 Playground。但是,在您学习 Swift 概念时,如果您愿意,可以在那里尝试它们。请记住,并非所有代码都可以在那里运行。这是因为与 iOS 应用直接相关的代码包含在 Playground 中不可用的特殊 GUI 组件。Playground 专门用于学习 Swift 代码,不支持这些特殊组件。
克隆现有项目
最后一个选择是与在 Git(版本控制软件)中已创建的项目一起工作。Git 是一种特殊的软件,用于跟踪对源文件(Swift 代码)所做的更改,以便多个用户可以协作处理一个项目并共享他们所做的更改。稍后我们将学习更多关于 Git 的知识,因为它是一个非常有用的系统,可以确保我们不会丢失代码。Git 也与 Github.com 相关,因为您可以在那里创建 Git 仓库并共享它们。如果现在这些都说不通,也没关系。再说一次,我们正在专注于中间的选择,“创建一个新的 Xcode 项目”,因为我们的主要目标是让您创建 iOS 应用。
Xcode 启动屏幕右侧
您在屏幕右侧看到的项目是我已经创建的项目。您稍后将使用该列表作为快速打开您之前处理过的项目的方式。
现在,继续点击“创建一个新的 Xcode 项目”,Xcode 将启动并显示另一个包含多个选项的窗口。
单视图应用
您的项目很可能会默认选择 iOS 选项卡并自动选择“单视图应用”。这将是我们创建的第一个应用。但是请花点时间注意,您的应用有多种不同的项目模板。游戏模板提供了一些额外的资源,使游戏开发更容易(例如绘图库等)。
您还可以看到像“标签式应用”这样的选项,它将为您的应用提供多个视图,您可以切换到不同的功能。甚至还有一个 iMessage 应用模板可以帮助您开始使用。
选择 iOS 单视图应用后,请继续点击 [下一步] 按钮,这将显示另一个对话框,允许您命名项目。
您在 [产品名称] 文本框中输入的任何内容都将是您的项目/产品的名称。
名称中使用的字符有一些限制。尽可能避免空格。作为开发人员,您会发现通常会避免名称中出现空格,因为它们通常会使事情变得困难。
[团队] 文本框与您的 Apple ID 相关。但是,您很可能没有,所以您可以将其留空。
[组织名称] 文本框用于标识负责此应用的组织。在这种情况下,您可以只填写您的姓名。您稍后会看到,这些大部分只有在将应用部署到 App Store 时才真正重要。
[组织标识符] 通常是组织的网站反向顺序。在截图中,您可以看到我输入了 com.happy,这实际上与网站 happy.com 相关。
我的网站是 raddev.us,所以我通常会输入 us.raddev。再说一次,如果您没有网站域名,您现在可以随意使用。这只有在您将应用部署到 App Store 时才重要。
Xcode 将组织标识符和 ProductName
拼接在一起,以尝试创建一个全局唯一的 [Bundle Identifier],您可以在截图中看到它。
但是,由于我们尚未添加 [产品名称],它具有通用名称。
最后,我们现在关心的这个对话框的最后一个选项是 [语言] 选项。当然,我们要选择 Swift,因为本书的重点将始终放在 Swift 语言上。如果您点击列表,您会看到您还可以选择 Apple 以前用于构建应用的 Objective-C 语言。
让我们像下面这样填写对话框,然后点击 [下一步] 按钮创建我们的第一个应用。
我是这样填写的。您(显然)应该更改 [组织名称](使其包含您的姓名)和 [组织标识符](如果您有域名,则使其包含您的域名)。
您会看到您将拥有一个有效的 [Bundle Identifier]。我的是 us.raddev.chpt1-UIKitExamples
。
填写所有适当的字段后,请继续点击 [下一步] 按钮。
当您点击 [下一步] 按钮时,Xcode 将会提示您一个 Finder 对话框(文件位置),以便您可以选择要保存项目的位置。Xcode 将在您选择的目标文件夹中创建一个项目文件夹(以您的项目命名)。您可以看到我选择了 *Dev* 文件夹。我还选择允许 Xcode 在我的 Mac 上 [创建 Git 仓库]。这仅仅意味着将在项目文件夹下创建一个特殊的 *.git* 文件夹,并使用 Git 版本控制系统跟踪更改。稍后我们将学习更多关于这方面的内容。
选择要创建项目的文件夹后,请继续点击 [创建] 按钮创建您的项目。
Xcode 创建项目后,它将显示主项目视图。
我已在 Xcode 左上角用红框突出显示了项目文件夹,因为它非常不起眼。
初始视图只是项目设置,但这些对我们目前来说并不是那么有趣。我们正在尝试获取一些代码。要查看一些代码,我们必须检查项目模板为我们创建的项。
这些项目列在左侧,在以我们项目命名的黄色文件夹下。
AppDelegate.swift
列出的第一项是 AppDelegate.swift 文件。该文件包含所有模板代码,以便我们的项目将构建为 iOS 应用。其中大部分只是一堆空函数,如果需要,我们以后可以使用它们。
您可以点击左侧的文件名,Xcode 将在右侧的编辑器中打开该文件,以便您查看文件。
应用启动后,它将加载一个可显示视图,这就是用户将看到的内容。iOS 应用遵循 MVC(模型-视图-控制器)模式,将代码分成可管理的部分供开发者使用。
什么是 MVC?
ViewController.swift
ViewController
是应用程序代码,它运行并响应用户交互,例如用户在文本框中输入文本或用户点击按钮。在 ViewController
处理完操作后,它还可以告诉 View
重新绘制自身或采取其他操作,以确保用户界面反映应用程序中正在发生的操作。
ViewController.swift 文件大部分是空的,但我们稍后会在这里添加一些代码。
现在,ViewController
中只有一个名为 viewDidLoad()
的函数。当应用程序最初加载 View
时,会调用该函数。这允许我们作为开发人员在 View
加载后执行初始化 UIPickerView
等操作。
Xcode 试图通过仅使用拖放样式(而不是强制开发人员编写代码来创建图形元素)来帮助开发人员创建用户界面。
当我们选择 Main.storyboard 文件时,图形设计器(Interface Builder)会加载,它表示 View
对象作为图形用户界面。当您在左侧选择 Main.storyboard
时,Xcode 会在右侧加载 UI(用户界面)。
-
视图控制器场景
请注意,中间部分(层级视图)显示“视图控制器场景”以蓝色突出显示。这表示该项目当前已选中。
如果您单击下面的下一个项目 [视图控制器],您已选中该项目,并且您的选择也会在图形显示中更新(在右侧)。
接下来,您可以点击 [视图] 项,您会看到右侧的预览会以蓝色高亮显示。
这试图向您展示,您对该区域所做的任何操作都适用于整个用户界面,甚至包括顶部标题栏(显示时间电量状态的区域)。请注意,其下方有一个 [安全区域]。该安全区域表示您可以在其中进行正常的应用操作,而不会影响操作系统(Operating System)可能尝试发送给用户的任何标题栏消息或通知。
如果您点击安全区域,您会看到该区域下方的 UI 会以蓝色高亮显示。
基本上,显示时间和电池寿命的区域现在不再高亮显示。
这是我们要将控件拖放到视图中的区域。
向视图中添加控件
现在我们要向视图中添加一个控件。然而,在执行此操作之前,我只想确保您了解整个 Xcode 窗口的外观,因为它包含很多内容。
我用(红色方框)突出显示了视图中需要关注的几个项目。首先,我突出显示的顶部按钮在点击时实际上会显示另一个窗口。该窗口将包含我们可以添加到表单中的控件列表(标签、按钮、文本编辑框和许多其他控件)。
下面的红色方框突出显示了一个由一系列图标组成的子菜单,这些图标以文件夹图标开头。目前,该项目显示了 storyboard 中所选内容的详细信息。现在,您可以看到它正在详细说明一些与 storyboard 相关的内容。稍后,当我们添加一个控件并选中它时,它将为选中的单个控件提供更多详细信息。
显示可用控件列表
继续点击我高亮显示的顶部按钮,当您这样做时,一个新对话框将弹出在 Xcode 主窗口上方。
当新的对话框出现时,Xcode 会将其置于焦点并把 I 型光标放在搜索输入框中(您看到“Objects
”字样的地方)。
这样做是因为有很多控件可供选择。在我上面的截图中,您可以看到我向下滚动了一点,以显示一些更常见的控件(Label
、Button
、Text Field
、Slider
)。
让我们向视图添加一个简单的 Label
。Label
提供了一种向 View
添加静态文本(不变的文本)的方法。Label
通常也不响应像轻触这样的事件。而按钮和其他控制对象则会响应。
继续从对话框中点击并拖动一个标签到 View
中。当您点击 Label
项目时,它会瞬间变成蓝色,然后当您拖动时,对话框会消失,您将把标签拖到 View 区域。
最后,您可以松手,Label
将会掉落到 storyboard 上,您会看到一些大小调整手柄出现。
--
您还可以看到 Label
已添加到左侧层级视图中 View
下方。
再次,我突出显示了一些项目(红色方框)。底部突出显示只是为了引起您对 Location
元素的注意。Xcode
storyboard 对该项目使用了 [Relative Group] 类型的定位。这意味着 Label
只是相对于添加到表单中的一组控件进行定位。
我还用红色高亮显示了左上角的警告图标。该图标警告我们可能有些地方不对劲。如果我们点击它,我们将看到一个警告,提示我们没有使用约束布局,并且项目可能会从屏幕上掉落到不可见的区域。
要获取有关标签的更多详细信息,我们需要点击我突出显示的右上方按钮(上面有一个 slider
控件)。现在就去执行此操作。
现在您可以在右侧看到有关 Label
的一些详细信息。第一个是它使用的是纯文本,第二个文本框显示 Label
将显示文本“Label
”。这是我们可以更改 Label
中显示文本的地方。
让我们把它改成“Keys
”这个词。
当您输入新文本并按 <ENTER> 时,更改将应用,您将看到 storyboard 已使用您的新 Label
文本更新。
我已经用(红色方框)高亮显示了您将看到更改的所有位置。
iPhone 模拟器
目前这是一个非常简单的应用,但让我们继续构建它并在 iPhone 模拟器上运行它。运行我们的应用将让我们有机会设置模拟器并看到它的实际运行效果。
Xcode 提供了一种在 Mac 上运行完整的模拟 iPhone 的方法。这样,您就不必连接 iPhone(甚至不必拥有 iPhone),您仍然可以在模拟设备上测试您的应用,它会像真实设备一样运行。
在 Xcode 窗口的顶部附近,开发环境建议了一个用于应用的 iPhone 版本。我的最初是 iPhone XR。您的可能会有所不同,具体取决于您阅读本书的时间以及发布的 iPhone 版本。
该项目也可用作下拉列表,可点击以选择其他 iPhone 或 iPad 图像。
这是我点击列表时出现的设备详尽列表。同样,您的可能会略有不同。
我将我的更改为 iPhone XS,这样我们就可以看到当您第一次选择一个尚未运行的模拟器映像时会发生什么。这与您第一次构建应用并尝试在模拟器上运行它时所看到的情况类似。
选择图像后,我将点击“运行”按钮(右指向三角形)来构建应用并启动模拟器。
当您按下该按钮时,您会在 Xcode 顶部看到一些活动,因为它正在构建您的程序。接下来,Xcode 将启动模拟器,然后应该会弹出一个新窗口。
起初,窗口是空白的,操作系统加载可能需要一些时间,因为您的 Mac 现在也在运行一个完整的 iPhone 实例,同时还在处理其他任务。
在某个时候,您会在设备上看到熟悉的 iOS 加载屏幕出现。
最终,iPhone 操作系统 (Operating System) 将启动并运行您的应用。
是的,这很无聊,因为应用所做的只是显示我们添加的那个 Label
。但是,如果您已经走到这一步,您实际上已经做了很多,因为我们现在已经:
- 创建了一个新的 iOS 项目
- 熟悉了一些 Xcode IDE(集成开发环境)
- 查看了一些代码(
ViewController.swift
和AppDelegate.swift
)。 - 看到了如何向屏幕添加 UI 元素
- 构建了项目
- 创建并运行了一个 iPhone 模拟器
- 运行了我们的基本程序
添加一些功能
但是,在我们结束本章之前,让我们添加一个按钮,让它通过定时器激活的计数器来更新我们的 Label
。它不会很漂亮,但至少我们会编写一些 Swift 代码并在我们的应用中实现一些功能。
首先,在您的 View
上放置一个按钮,并将按钮的文本更改为“开始计时器”。
现在我们希望按钮在点击时执行一些操作。
我们还希望在计时器每次触发时更新标签上显示的文本。我们将使用标签作为计数器值的输出。这意味着每次计时器触发时,我们都会递增一个计数器变量,然后将该值输出到标签。这样,用户会看到标签随着计数器的递增而变化。我们还会希望在计时器运行时(点击后)更改“开始计时器”按钮上的文本,使其显示“停止计时器”,以便用户可以在计数器运行和不运行之间切换。
我们希望在 ViewController.swift 文件中添加一些代码来表示 Label
和 Button
,以便我们可以通过代码操作它们。
为此,我们为视图中的每个项目添加一个 Outlet
。
切换回 ViewController.Swift 文件(选择右侧列出所有项目文件的文件),我们将添加以下代码。
@IBOutlet var counterLabel : UILabel!
@IBOutlet var counterButton : UIButton!
var isRunning : Bool = false
var counter : Int : = 0
您可以在下图的文件中看到在哪里添加这些代码。
类中的前两行(第 13 和 14 行)添加了我们一直在谈论的 IBOutlets
。
@IBOutlet
以 IB 为前缀,代表 Interface Builder。这个项目并非真正的 Swift 代码,而是一个特殊的宏,它告诉 Interface Builder 这个变量将作为一个特殊项目使用。所有这些都由第一个 @
符号表示。稍后,我们将看到 @IBAction
,它也是同类型的东西。
接下来,在第 15 行,我们添加了一个布尔变量(保存 true
或 false
值),我们将用它来确定计数器是否正在运行。由于应用启动时它没有运行,我们将该值初始化为 false
。
最后在第 16 行,您可以看到我们添加了一个计数器变量,它将保存每次计时器触发时递增的计数器的值。我们将计数器的值初始化为零。
Swift 语法
现在我们已经看到了一些 Swift 变量的声明和初始化,让我们来谈谈声明 Swift 变量的具体细节。
声明变量
我们声明变量,以便 Swift 编译器知道我们将在程序中使用的各种事物的名称和类型(数据类型,如字符、整数等)。
Swift 编译器需要知道它们的名称,以便它可以将您的值存储在内存中,然后为您提供一种简单的方式来读写相同的内存(使用变量名而不是神秘的内存地址)。
在 Swift 中,您可以声明一个变量并明确告诉编译器您希望在其中存储什么类型。这就是我们声明变量时所做的事情。
例如,我们可以声明一个名为 isRunning
的变量并告诉编译器它将是布尔值。
var isRunning : Bool
然而,我们并没有将布尔值初始化为某个值。相反,我们只是告诉 Swift 编译器我们只希望在该变量中存储布尔值。之后,Swift 将不允许您在该变量中存储任何其他类型的数据。这很好,因为当我们在不小心尝试在其中存储数字或字符值时,编译器能够警告我们。
Swift 可以确定类型
Swift 也非常擅长确定您想要存储在变量中的类型,如果您没有明确告知它类型,它会这样做。以下是您如何做到这一点。
var isRunning = true
现在 Swift 编译器将确定该值的类型(在这种情况下,它将评估为布尔值),然后它还将变量初始化为该值。在这个例子中,我们声明并初始化了变量,而没有明确告诉编译器类型。
创建 Swift 变量的通用语法
无论何时创建变量,您都
- 以关键字
var
开头 - 关键字后跟变量的名称
- 要么将变量初始化为
= <value>
的值 - 或者在变量名后跟冒号 (
:
) 和类型名称 - 或者您可以明确告诉编译器类型,然后初始化值。
所以通用语法看起来像下面这样
var [variableName] <: typeName> <= initialValue>
这表示您必须使用 var
关键字。
方括号 [ ]
表示您必须提供一个唯一的变量名。
尖括号 <>
表示您可以包含类型名称或初始值,或两者都包含。但是,您必须包含其中之一(类型名称或初始值)。
但是,您不能只用关键字和变量名声明变量,如下所示
var myThing
在这种情况下,始终需要知道变量类型的编译器无法确定它可能存储在该变量中的类型,因为您没有通过使用 <typeName>
显式提供它,也没有通过提供初始化值隐式提供它。
变量名可以包含除了空格和制表符等空白字符之外的几乎任何字符,但它们不能以符号或数字开头。以下变量声明将给您一个奇怪的编译器错误,并且您的程序将无法构建。请注意,第二个以 @
符号开头,这就是 @IBOutlet
是由 Interface Builder 定义的特殊事物,它不是普通的 Swift 变量或类型的原因。
var #garbage
var @trash
让我们看一下我们声明为 @IBOutlets
的变量。忽略 @IBOutlet
宏,它们看起来像下面这样
var counterLabel : UILabel!
我们已经声明了一个名为 counterLabel
的变量,其类型为 UILabel
。感叹号不是变量类型的一部分,而是表示该变量是 Optional
。这意味着该变量可以有一个非值,在 Swift 中称为 nil(在其他语言中通常称为 null
)。Nil 是对象可以拥有的特殊值,我们稍后会详细讨论它。
UILabel
类型是 Apple 开发者为我们创建并放入名为 UIKit
的库中的内置类型,该库在我们的 ViewController.swift 文件的顶部导入。如果您从文件顶部移除该 import
语句,您的程序将无法构建,因为它将不知道 UILabel
或 UIButton
类型是如何定义的。
现在,让我们继续使用 Interface Builder 设置 @IBOutlet
。
单击左侧层级视图中的 ViewController
。我们需要按住 Command 键(如果您使用的是 Windows 键盘,就像我一样,则按 CTRL 键),然后拖动到 storyboard 上的 Keys Label
。当您仍在拖动并浮动在 Keys Label
上方时,它将显示如下:
当 Label
像这样高亮显示时,您需要松开 Command(或 CTRL)键,然后会出现另一个小菜单。
这是“Outlets”菜单。我们希望将我们在代码中创建的 IBOutlet
连接到 storyboard 上的控件,因此我们选择(点击)名为 counterLabel
的那个。它只提供这一个选择(以及主视图),因为 Interface Builder 知道可以为标签选择的有效类型,并且它知道我们的 counterLabel
是 UILabel
。
现在,让我们对“开始计时器”按钮做同样的事情。这次,storyboard 将知道我们可能希望将其连接到 counterButton
。
如果一切按预期进行,有一种方法可以检查以确保设置正确。
在层级列表中的“视图控制器”=>“视图”区域下,您现在应该看到两个新项目(计数器标签和计数器按钮)(请参见下图中的红色高亮部分)。
您还可以点击右侧的 [圆形右箭头],它会显示已设置的 Outlets。
现在我们需要设置按钮,以便用户点击它时执行某些操作。当用户点击按钮时,它可以执行一个动作。但是,我们需要添加一个动作(IBAction
),当按钮被点击时,它将运行该动作。一个 IBAction
基本上是一个特殊的函数,按钮被点击时会调用它。现在让我们回到 ViewController.swift 文件并添加那个 IBAction
。
我们真的只需要向我们的 ViewController
添加一个普通函数。然而,为了让 Interface Builder 知道我们希望它将函数连接到按钮,我们为函数设置了一个特殊的装饰器(@IBOutlet
)。该函数还必须将对被点击按钮的引用作为参数。您将在下面的函数调用中看到它被命名为 sender
。
这是将切换按钮的普通 Swift 函数。
func counterButtonClicked(_ sender: UIButton){
if isRunning{
counterButton.setTitle("Start Counter", for:UIControl.State.normal)
isRunning = false
}
else {
counterButton.setTitle("Stop Counter", for:UIControl.State.normal)
isRunning = true }
}
要将该函数转换为 IBAction
,我们在函数定义中的 func
关键字之前添加 @IBAction
装饰器,如您在到目前为止我们添加的所有代码列表中所见。如果我们不添加该装饰器,那么当我们在 storyboard 上尝试连接按钮时,Interface Builder 将不会显示该方法。
Interface Builder 也知道与 UIButton
关联的动作必须接受一个代表被点击按钮的参数作为函数参数列表的一部分,所以您可以看到我们也传递了一个参数。即使我们将函数定义为 @IBAction
但我们忘记添加参数,Interface Builder 也不会将此函数视为我们尝试将其连接到用户界面时正在寻找的函数。
现在我们已经添加了 @IBAction
函数,我们想切换回 storyboard 视图,以便将按钮点击连接到此 outlet。
连接 @IBAction 是反向操作
切换回那个视图,这次,您必须先点击按钮,然后拖动到层级视图中的 ViewController
,然后松开 Command(或 CTRL)键。当您这样做时,将显示一个您可以选择的项目列表。当然,我们想点击 counterButtonClicked
: 项目来连接它。对话框将消失,并且动作已设置。
如果您已完成所有这些操作,我们现在可以运行它进行测试。所以继续在模拟器上运行应用并点击“开始计时器”按钮。当您这样做时,它应该在两个文本设置(“开始计时器”,“停止计时器”)之间来回切换。
如果这些都运行正常,让我们添加额外的代码来启动计时器,然后我们将结束本章。
这是完整的 ViewController.swift 文件的快照,以便您将所有代码集中在一个地方,以防您出于某种原因无法使其正常工作。
代码解释
我将从第 30 行开始解释代码,因为那是处理按钮点击的地方。
这是我们作为 @IBAction
添加的函数。
当用户点击按钮时,程序会检查 isRunning
的值(第 31 行)以确定它是 true
还是 false
。
如果 isRunning
为 true
,则第一个代码块(由开头的 {
和结尾的 }
指示)将运行。当然,代码第一次运行且视图加载时,isRunning
设置为 false
,因此第一次调用 counterButtonClicked
时,代码将进入 else
块。
我们做的第一件事是将按钮的标题(显示在按钮上的文本)设置为“停止计数器”,因为我们要开始计数器。之后,我们通过将 isRunning
变量设置为 true
来正确初始化它,以指示计时器正在运行。
最后,我们启动特殊的 Timer
对象,它由预先创建的编程库提供,类似于 UIKit
库。
在这种情况下,我们发送一些值,其中包括计时器应该触发的 timeInterval
。在我们的应用中,我们将其设置为 0.25 秒,因此计时器每秒将触发约 4 次。接下来,我们将计时器的目标设置为当前 View
。这确保了该视图将在每个间隔计时器触发时收到消息。
我们接下来做的是提供一个将被调用的函数(选择器),该函数可用于 scheduledTimer
。我们将该方法命名为 updateLabel
。这意味着每次计时器触发时,都会调用 updateLabel
函数。
我们没有额外的数据发送到 selector
方法,所以我们将 userInfo
设置为 nil
(无)。
最后,我们将 repeats
布尔变量设置为 true
,以指示 scheduledTimer
应该每 0.25 秒重复一次。如果您将其设置为 false
,那么 updateLabel
将只被调用一次。
当用户第一次点击按钮时,按钮的文本将更改为“停止计时器”,并且计时器将启动。每次计时器触发时,都会调用 updateLabel
。
当调用 updateLabel
时,它将递增计数器值。
counter += 1
这是 counter = counter + 1
的简写。
接下来,我们将 counterLabel.text
设置为 counter
(一个整数)的 String
值。
一个 String
是一个或多个字符的表示,由于 counterLabel
的 text
属性期望一个 String
,我们必须将整数变量 (counter
) 的值强制转换 (convert
) 为 String
,以便标签可以使用该值显示在标签中。
当然,当用户下次点击按钮时,if
块的顶部部分将运行,并将
- 将按钮的标题文本改回“开始计数器”
- 将
isRunning
的值改回false
- 通过调用
timer.invalidate()
停止timer
最后,这是正在运行的程序。
这是一个好的开始,它让我们为在第二章中开始编写下一个应用做好了准备。
我们将开始开发一个简单的应用,它允许我们记笔记并保存以备后用。
该应用程序将帮助我们学习如何适应视图上不断增长的项目列表以及如何将数据保存到设备。
历史
- 2018年12月9日:首次发布