iOS 从零开始使用 Swift:使用 UIKit 的第一步

iOS 从零开始使用 Swift系列:
探索 iOS SDK
探索 Foundation 框架
使用 UIKit 的第一步
自动布局基础
表格视图基础
导航控制器和视图控制器层次结构
iOS 上的数据持久性和沙盒
构建购物清单应用程序 1
构建购物清单应用程序 2
下一步该去哪里

UIKit 是您在开发 iOS 应用程序时最常使用的框架。它定义了 iOS 应用程序的核心组件,从标签和按钮到表格视图和导航控制器。在本文中,我们不仅将开始探索 UIKit 框架,还将探索 iOS 项目的内部结构和 iOS 应用程序的基本构建块。

什么是 UIKit 框架?

Foundation 框架为 iOS 和 OS X 开发定义了类、协议和函数,而 UIKit 框架专门针对 iOS 开发。它相当于用于 OS X 开发的 Application Kit 或 AppKit 框架。

与 Foundation 一样,UIKit 定义了类、协议、函数、数据类型和常量。它还  通过使用 Objective-C 类别为各种 Foundation 类(例如NSObject、  NSString) 添加了附加功能。NSValue

Objective-C 类别是向现有类添加额外方法而无需子类化的便捷方式。它们类似于 Swift 扩展。 如果您想了解有关 Objective-C 类别的更多信息,请阅读 Aaron Crabtree 的 本教程。

不像我们为 Foundation 框架所做的那样探索 UIKit 的关键类,我们将创建并浏览一个新的 iOS 项目,探索我们遇到的类。通过采用这种方法,很快就会清楚在什么上下文中使用一个类,每个类如何适应 iOS 应用程序的更广泛的方案,以及它扮演什么角色。

一个新的开始

启动 Xcode 并通过从 File 菜单中选择New > Project… 创建一个新项目 。在左侧的 iOS 部分中,选择 应用程序 类别。从项目模板列表中,选择 Single View Application 模板。

Single View Application模板包含 iOS 应用程序的  基本构建块,这使它成为我们开始旅程的好地方。

将项目命名为 FirstSteps 并输入组织名称和标识符。将Language 设置为Swift并将 Devices设置 为 iPhone。不选中底部的复选框。

告诉 Xcode 您要将新项目保存在哪里,并通过选中标记为 Create Git repository on My Mac的复选框,确保将项目置于版本控制之下。重新访问 本文 以获取有关版本控制及其优势的更多信息。

文件和文件夹

自从上次我们从头开始创建 iOS 项目以来,我们已经学到了很多新东西,所以探索我们新项目的各种文件和文件夹以查看它们是否响起是个好主意。

在 Project Navigator中,您应该在项目的根目录中看到两个文件夹:

  • 产品
  • 带有项目名称的文件夹,  在此示例中为FirstSteps

让我们来看看每个文件夹的内容。

产品

Products文件夹当前  包含一项。它带有项目的名称,扩展名为 .appProducts文件夹包含项目在编译源代码后创建的一个或多个应用程序。

您是否注意到 FirstSteps .app 以红色突出显示?每当 Xcode 无法找到文件时,它会以红色突出显示该文件。不过不用担心。由于项目尚未编译,Xcode 尚未创建产品。

项目文件夹

您的大部分时间都花在项目文件夹中,该文件夹目前包含六个文件。前两个文件 AppDelegate.swift和 ViewController .swift是源文件。这些文件包含应用程序的源代码。

Main.storyboard包含应用程序的用户界面。我们已经在本系列的前面部分使用过故事板。请注意,还有第二个故事板 LaunchScreen.storyboard。启动应用程序时,操作系统会使用此情节提要。操作系统不是显示一个空视图,而是使用这个故事板动态创建一个启动图像,当应用程序加载时向用户显示。

Info.plist,通常称为项目的“info-dot-plist”文件,是一个包含各种配置设置的属性列表。大多数这些设置也可以通过在 Project Navigator中选择项目、在Targets列表中选择目标  并打开 General、  Capabilities和 Info 选项卡来修改。

Assets.xcassets 是一种特殊类型的文件夹,用于存储项目的资产,例如图像。

应用程序组件

现在我们知道了项目中不同的文件和文件夹的用途,是时候探索 iOS 应用程序的不同组件了。随着我们继续我们的旅程,我们将遇到几个属于 UIKit 的类。属于 UIKit 框架的每个类都以类前缀 UI开头。请记住,Foundation 类以 NS为前缀。

The Model-View-Controller Pattern
模型-视图-控制器模式

在我们开始探索 UIKit 框架之前,我想先谈谈模型-视图-控制器 (MVC) 模式。MVC 模式是面向对象编程中最普遍的模式之一。它促进了代码的可重用性,并与职责或关注点分离的概念密切相关。MVC 模式的主要目标之一是将应用程序的业务逻辑与表示层分离。

Cocoa Touch 包含 MVC 模式,这意味着了解它是什么以及它是如何工作的很重要。换句话说,通过了解 MVC 模式,将更容易掌握 iOS 应用程序的不同组件如何协同工作。

在 MVC 模式中,模型层控制应用程序的业务逻辑。例如,与数据库交互是模型层的职责。视图层将模型层管理的数据呈现给用户。视图层管理用户界面和用户输入。

控制器是模型层和视图层之间的粘合剂。虽然模型层和视图层不知道彼此的存在,但控制器知道两者。

因为控制器知道模型和视图,所以它通常是应用程序中可重用性最低的部分。对象对其环境和与之交互的对象了解得越少,它就越容易重用。

UIApplication

尽管 UIApplication 类是每个 iOS 应用程序的关键组件,但您不会经常与它交互,而且您很少(如果有的话)觉得需要子类化 UIApplication.

当应用程序启动时,会创建一个此类的单例。你还记得什么是 单例对象 吗?这意味着 UIApplication 在应用程序的生命周期中只创建一个类的对象实例。

该 UIApplication 实例是用户交互的入口点,它将事件分派给适当的目标对象。当我们看一下视图控制器时,它的确切含义将在几分钟内变得清晰。

在 iOS 应用程序中, UIApplication 实例有一个与之关联的委托对象。每当您使用提供的模板之一创建 iOS 项目时,Xcode 都会为您创建一个应用程序委托类。查看Project Navigator中项目文件夹中源文件的名称。第一个文件名为 AppDelegate.swift

此类的实例是 UIApplication 单例的委托。在仔细研究 AppDelegate 类之前,我们需要了解什么是委托模式。

Ole Begemann 写 了一篇 关于典型 iOS 应用程序启动顺序的优秀文章。在他的文章中,Ole 谈到了涉及的各种组件以及它们在应用程序启动序列中的作用。如果您想更好地了解 UIApplication 类的作用,我强烈建议您阅读这篇文章。

委托模式

委托模式在 Cocoa 和 Cocoa Touch 中广泛使用。在本系列的后续文章中,我们将探讨表视图的来龙去脉,您会发现表视图严重依赖委托(和数据源)模式。

与 MVC 模式一样,委托在面向对象编程中很常见。然而,Cocoa Touch 中的委托模式略有不同,因为它使用委托协议来定义委托对象的行为。

让我们继续看一下表格视图。如果你用过 iPhone 或 iPad,那么 UITableView 你应该对这门课很熟悉。它向用户展示了一个有序的数据列表,并且很好地完成了这项工作。

当一行被点击时,表格视图如何知道该做什么?这种行为是否包含在 UITableView 课程中?当然不是,因为这种行为因应用程序而异。如果我们要在 UITableView 类中包含这种行为,它就不是很可重用。

表视图 将此责任外包 给委托对象。换句话说,它将 这个任务委托 给另一个对象,一个 委托 对象。花点时间检查类的 类引用 UITableView 。dataSource 它有两个名为和 的属性 delegate。当 delegate 用户点击一行时,表格视图会通知它。响应该触摸事件是委托对象的责任。

表视图的数据源类似。主要区别在于表格视图  数据源对象询问某些内容,即它需要显示的数据。

委托和数据源对象,例如表视图 delegate 和 dataSource 对象,几乎总是自定义类。在大多数情况下,由开发人员来创建和实现这些类,因为它们的实现是特定于每个应用程序的。

申请代表

现在我们知道什么是委托,是时候探索 AppDelegate 为我们创建的 Xcode 类了。在应用程序启动时,应用程序创建 AppDelegate 该类的一个实例。然后将此AppDelegate 实例设置为UIApplication操作系统为您的应用程序创建的实例的委托。您永远不会显式实例化应用程序委托对象。

打开AppDelegate.swift 以检查AppDelegate该类的实现。忽略顶部的注释,第一行导入 UIKit 框架,以便我们可以使用它的类和协议。

importUIKit

在下一行,我们看到了一些我们还没有涉及到的东西,一个属性。Swift 中的属性以@符号开头,可以看作是编译器的指令。该 @UIApplicationMain属性告诉编译器AppDelegate应该用作应用程序委托的类。这就是你现在需要知道的关于这个属性的全部内容。

@UIApplicationMain

下一行应该看起来很熟悉。这是 AppDelegate 类声明的开始。它指定了类的名称和类的父类 UIResponder

它还告诉编译器AppDelegate符合UIApplicationDelegate协议。这并不奇怪,因为我们已经知道它AppDelegate 充当应用程序的委托。

class AppDelegate: UIResponder, UIApplicationDelegate {<font></font>
    ...<font></font>
}

下一行是属性的属性声明window 。请注意,该属性是类型的变量UIWindow?。该类UIWindow 是 UIViewiOS 上视图的基类 的子类。

varwindow: UIWindow?

AppDelegate 类 接口中最有趣的部分 是UIApplicationDelegate 协议。查看协议的  参考以 UIApplicationDelegate 获取协议定义的方法的完整列表。

该协议定义了数十种方法,我鼓励您探索其中的一些方法以了解协议的功能。在这一点上,我们最感兴趣的方法是 application(_:didFinishLaunchingWithOptions:).

当 UIApplication 对象完成启动应用程序的准备后,它会通知其委托 AppDelegate 对象,即应用程序即将启动。

为什么它会通知应用程序委托这个事件?实例将此事件通知其委托, UIApplication 以便它有机会准备应用程序启动。的实现 application(_:didFinishLaunchingWithOptions:) 很短,如下所示。

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {<font></font>
    // Override point for customization after application launch.<font></font>
    return true<font></font>
}

它提供对 UIApplication 实例的引用和选项字典。您可以暂时忽略选项字典。为了确保应用程序是由操作系统启动的,我们返回true.

Storyboard

Xcode 项目包含另一个有趣的文件 Main.storyboard。故事板定义了应用程序的用户界面是什么样的。默认情况下,故事板被命名为 Main.storyboard。选择情节提要将其打开。

故事板当前在中央工作区中包含一个视图。在 Project Navigator的右侧, 您可以看到项目列表,即您在视图中看到的对象。顶部项目 View Controller Scene包含一个子项目 View Controller

View Controller 对象也有 许多子项,但有一个我们特别感兴趣, 名为 View的对象。记住关于 MVC 模式的讨论。您现在可以看到 MVC 模式的实际应用。目前缺少模型,但我们确实有一个视图,即 View 对象和一个控制器,即 View Controller 对象。

当应用程序启动时,情节提要用于创建应用程序的用户界面。视图控制器会自动实例化,视图控制器的视图也是如此。故事板中的 View 对象由视图控制器管理。

等一下。在情节提要的哪里可以找到视图控制器的类?如何更改其行为以创建独特的应用程序?选择左侧的 View Controller 对象并打开右侧的 Identity Inspector  。

身份检查员 会告诉您您需要知道的 一切。在顶部的 自定义类部分,您可以看到视图控制器类的名称 ViewController。您是否注意到我们尚未讨论的文件名称相同?稍后我们将探索这个文件。

视图控制器是为我们实例化的,因为它是故事板的初始视图控制器。这在情节提要中由指向View Controller Scene的箭头指示。

UIViewController

如果你打开 ViewController.swift,你会注意到这个 ViewController 类是 .swift 的子类 UIViewController。就像 AppDelegate, UIViewController 类是 的子类 UIResponder。视图控制器或其子类属于 MVC 模式的 控制器 类别。顾名思义,它们控制一个视图,一个 UIView 类的实例,属于MVC模式的 视图 范畴。

正如我们稍后将看到的,视图控制器管理视图及其子视图。为此,视图控制器需要了解视图。换句话说,它需要有对视图的引用。

故事板中的视图控制器具有对视图的引用。您可以通过在故事板中选择视图控制器并打开  右侧的Connections Inspector来验证这一点。

在 Connections Inspector中,您应该看到名为 Outlets的部分。术语出口是您可以在情节提要中设置的属性的一个花哨的词。将鼠标悬停在名为 view的插座上 ,观察工作区中的视图是如何突出显示的。那就是视图控制器和视图之间的连接。

UIView

即使您的应用程序只能有一个 的实例 UIWindow,它也可以有多个视图。该类 UIView 是 UIKit 框架的重要组成部分,因为许多类直接或间接继承自它。

 通过选择它重新访问 Main.storyboard 并查看 Inspector 底部 Object  Library

浏览对象库并将标签 和 按钮拖动  到工作区中的视图。只要它们在视图控制器的视图中,它们在视图中的位置并不重要。

 请注意,左侧的“对象”部分中添加了两个新对象 。标签 ( UILabel) 和按钮 ( UIButton) 都继承自 UIView. 您是否注意到 Label 和 Button对象与View对象 相比略有缩进  ?这表明 Label 和 Button 对象是 View 对象的子视图。一个视图可以有一个或多个由它管理的子视图。

正如我前面提到的, UIView 类是 UIKit 的重要组成部分。视图管理屏幕上的矩形区域或框架。它管理区域的内容、子视图以及与视图内容的任何交互。该类 UIView 是 的子类 UIResponder。在本系列课程中,您将了解更多有关视图的信息。

outlet

让我们看一个例子来说明故事板、它包含的视图和视图控制器之间的关系。这三个组件很重要,我想确保您了解它们是如何协同工作的。

刚才,您向视图控制器的视图添加了一个标签和一个按钮。视图控制器如何知道这些对象?目前,它们没有出现在 Connections Inspector中,但我们可以通过告诉视图控制器它们来改变它。打开ViewController.swift 并为标签和按钮添加一个属性。

import UIKit<font></font>
<font></font>
class ViewController: UIViewController {<font></font>
<font></font>
    @IBOutlet var myLabel: UILabel!<font></font>
    @IBOutlet var myButton: UIButton!<font></font>
<font></font>
    ...<font></font>
<font></font>
}

通过将 @IBOutlet 属性添加到属性声明中,属性会出现在情节提要的Connections Inspector中,这就是我们想要的。

回到故事板, 在View Controller Scene中选择View Controller 对象 ,然后打开  右侧的Connections Inspector 。新属性现在列在 Outlets列表中。但是,视图控制器尚未在新属性和情节提要中的对象之间建立联系。

这很容易补救。从插座左侧的空圆圈拖动到 myLabel 工作区中的标签。这将创建连接。视图控制器现在知道标签了。对按钮重复此步骤。

即使我们可以更改故事板中标签的文本,让我们在视图控制器中执行此操作以说明视图控制器可以访问故事板中的标签和按钮。

打开ViewController.swift 并查找viewDidLoad() 方法。修改 viewDidLoad() 方法以反映下面的实现。为清楚起见,已省略注释。

override func viewDidLoad() {<font></font>
    super.viewDidLoad()<font></font>
    <font></font>
    myLabel.text = "This is an instance of a UILabel."<font></font>
}

我们可以通过访问视图控制器的属性向标签属性发送消息 myLabel 。text我们可以通过使用字符串文字设置标签的属性来更改标签的 文本。

myLabel.text = "This is an instance of a UILabel."

viewDidLoad()当视图控制器加载其视图时,该方法会自动调用。该override关键字表示我们正在覆盖由继承树中更高级别的类定义的方法。类,UIViewController类的父类 ViewController ,实现了这个方法。

另请注意,我们调用viewDidLoad()on super。是什么super?就像self指的是当前实例,super指的是父类,UIViewController. 换句话说,我们调用viewDidLoad()超类的方法。这在覆盖子类中的方法时通常很重要,因为超类可能在其自己的实现中执行重要任务,我们不想破坏任何东西。

 通过单击左上角的Run按钮在模拟器中运行您的应用程序 。请注意,标签的文本已更新。不用担心标签和按钮的位置。这是我们将在下一个教程中解决的问题。

行动

我们在本文中探索了很多新事物。我想通过谈论行动来结束这一部分。就像插座一样,动作只不过是您可以在情节提要中访问的方法。

让我们看看这是如何工作的。打开ViewController.swift 并在方法下方添加以下viewDidLoad()方法。

@IBAction func changeColor(sender: UIButton) {<font></font>
    print(sender.classForCoder)<font></font>
    print(sender.superclass)<font></font>
    <font></font>
    let r = CGFloat(arc4random() % 255)<font></font>
    let g = CGFloat(arc4random() % 255)<font></font>
    let b = CGFloat(arc4random() % 255)<font></font>
    <font></font>
    let color = UIColor(red: (r/255.0), green: (g/255.0), blue: (b/255.0), alpha: 1.0)<font></font>
    <font></font>
    view.backgroundColor = color<font></font>
}

不要被 @IBAction 属性所迷惑。该属性表明该方法是一个动作,因此可以在情节提要中访问。如果我们仔细看看这个changeColor(_:)动作,我们可以看到它需要一个类型的参数 UIButton

正如参数名称所暗示的,方法或操作的参数是将消息发送到视图控制器的对象。稍后我将更详细地解释这一点。

重新访问情节提要, 在View Controller Scene中选择View Controller 对象 ,然后打开 Connections InspectorConnections Inspector,  Received Actions中出现了一个新部分 ,我们刚刚添加的操作在此部分中列出。

从操作左侧的空圆圈拖动到工作区中的按钮。应该会出现一个带有选项列表的弹出菜单。该列表包含按钮可以响应的所有可能事件。我们感兴趣的是 Touch Up Inside。当用户触摸按钮并在按钮范围内抬起手指时触发此事件。

再次运行您的应用程序并点击按钮。每次点击按钮时,视图控制器的视图都应该改变颜色。我们在操作中添加了两个打印语句changeColor(_:)。让我们看看点击按钮时的输出是什么样子的。

UIButton<font></font>Optional(UIControl)

第一行显示发送者的类,一个UIButton. 这证明它是在视图控制器上触发此方法的按钮。changeColor(_:)它正在向视图控制器发送消息。

第二行显示发送者的超类。请记住,并非每个类都有超类。这就是我们得到一个可选的原因。输出告诉我们的父类 UIButtonUIControl

该方法本身非常简单。我们生成0 到 255之间的三个随机整数 ,将这些值传递给以 init(red:green:blue:alpha:) 生成随机颜色,并使用随机生成的颜色更新视图控制器视图的背景颜色。请注意,它 view 引用了视图控制器管理的视图。

结论

在本文中,我们探索了 UIKit 框架的几个类,并仔细研究了 iOS 应用程序的各种组件。我们将在本系列的其余部分中更详细地探索和使用 UIKit 框架。如果你不完全理解各种概念和模式,那么随着系列的进展,我相信你会的。

ios-from-scratch-with-swift-first-steps-with-uikit–cms-25461