大多数显示多个内容的 iOS 应用程序也包含多个 视图控制器。如果您的应用程序不是基于导航控制器或标签栏控制器,而是基于单个视图的应用程序,那么加载并向用户呈现各种视图控制器是一项必须手动完成的任务。一般来说,通过故事板或纯粹以编程方式加载视图控制器并不是一项艰巨的工作,但是对于新开发人员来说,这一切有多简单或直接?
这是一个由两部分组成的教程,在这篇文章中,我将通过涵盖所有可能的情况向您展示可用于加载和呈现视图控制器的各种技术:
直接从 Interface Builder创建和执行 segue;这是一种纯粹的图形方法。 在 Interface Builder 中创建一个 segue,但在代码中执行它;一种混合方式。 以编程方式从故事板加载在 Interface Builder 视图控制器中设计的图形,然后呈现它。 完全不使用故事板加载和呈现视图控制器;100% 程序化方法。
除了上述内容,我还将演示如何在演示完成后在视图控制器之间来回发送数据。然而,这不会发生在这篇文章中,因为它是第二部分的主题。
尽管这篇文章主要面向初学者,但需要熟悉 Xcode 和一些基本的 iOS 概念。如果您发现主题之外的某些内容您不理解或想要了解更多详细信息,那么在 StackOverflow 上或通常在网络上进行快速搜索可能会很有帮助。
你会在这篇文章中读到什么......
-
- 入门
-
- 技术#1:完全图形化的方法——使用 Segue
-
- 技术#2:混合方法——在代码中执行 Segues
-
- 技术#3:对于快乐的设计师和程序员——在代码中从故事板文件加载和呈现视图控制器
-
- 技巧#4:对于代码狂人——完全程序化的方法
-
- 总结
入门
为了尝试我们将在接下来的部分中看到的概念,您必须创建一个简单的 Xcode 项目。因此,启动 Xcode,开始创建一个新项目并选择 Single View App 作为您的应用程序模板。
继续下一步,为项目命名(我将其命名为 VCTechniques),在您的计算机上找到一个位置来保存它,并完成它的创建。
准备就绪后,您必须添加三个新的视图控制器,我们将使用它们来演示我们将在本文中讨论的各种主题。添加新视图控制器的过程很简单:首先按 键盘上的Ctrl + N,然后选择 Cocoa Touch Class 作为新文件的模板。
在下一步中,确保UIViewController
是Subclass of:字段的值 ,然后键入我们新类的名称: FirstViewController。
继续下一步并完成创建新的视图控制器。然后,再重复上述过程两次,并添加以下视图控制器:
- 第二个视图控制器
- 第三个视图控制器
完成后,随意删除由 Xcode 自动创建的默认 ViewController.swift文件。在上述所有步骤结束时,您应该能够看到以下文件列表(以及其他):
最后一件事必须要做。打开 Main.storyboard文件,然后选择View Controller 场景中的 ViewController对象 (黄色图标)。然后打开 身份检查器,将FirstViewController设置 为视图控制器的类:
技术 #1:完全图形化的方法——使用 Segue
根据苹果文档:
一个SEGUE定义了你的应用程序的故事板文件中的两个视图控制器之间的过渡。转场的起点是启动转场的按钮、表格行或手势识别器。Segue 的终点是您要显示的视图控制器。
实际上,Interface Builder 中的 segue 表示为两个视图控制器之间的 连接。它在用户时执行 :
- 要么与第一个视图控制器(例如,按钮)中触发 segue 的控件交互,要么
- 执行另一个以编程方式触发 segue 的操作,例如手势识别器(下一节将详细介绍这种方法)。
segue 呈现连接到的视图控制器的方式可以是动画的,也可以是不动画的。如果它是动画的,则提供少量预定义的过渡可供选择。如果这些还不够,还可以制作和使用自定义过渡。非常重要的是,一个 segue 必须有一个唯一的标识符,以便可以将它与项目中存在的所有 segue 区分开来。
要查看 segue 是如何创建的,在 Main.storyboard文件中,从Xcode Utilities 窗格中的Object library中将一个新的视图控制器对象拖到画布上,如下所示:
添加后,更改背景颜色,使其与第一个视图控制器不同(我设置了 r: 149, g: 165, b: 166),并添加一个按钮,让我们在呈现后关闭该视图控制器它。然后,单击视图控制器对象(场景顶部的黄色图标),并转到身份检查器。在那里,将类更改为 SecondViewController。 如您所知,我们的目标是从FirstViewController触发 segue 并呈现 SecondViewController。
接下来,向第一个视图控制器添加一个按钮。我们将创建一个新的转场,我们将从该按钮触发。
现在是时候创建转场了。单击您刚刚创建的按钮,同时按住 键盘上的 Ctrl键。开始将鼠标拖向 SecondViewController 场景,直到它在它上面并且整个场景被选中,如下所示:
当您松开鼠标和 Ctrl 键时,会出现一个小弹出窗口,显示以下选项:
单击 Present Modally 选项,将创建 segue。您将通过看到一条带箭头的蓝线连接两个视图控制器场景来验证这一点。单击 segue 本身,然后打开 Utilities 窗格,直接进入 Attributes inspector。在 Identifier 字段中,为 segue 设置一个唯一值(例如,idSegueSecondVC)。从那里显示的其余选项中,您很可能想要更改其中的几个选项,例如默认过渡,或者是否将新的视图控制器显示为动画。
是时候测试了!使用模拟器运行项目并注意,当点击红色大按钮时, 会显示SecondViewController。
要关闭新的视图控制器并返回到第一个控制器,您必须执行以下操作:
打开 SecondViewController.swift 文件,并添加以下操作方法存根:
@IBAction func dismissMe() {
self.dismiss(animated: true, completion: nil)
}
复制代码
保存文件并返回 Main.storyboard文件,选择SecondViewController 场景,然后在 Utilities 窗格中打开Connections 检查器。在 Received Actions部分,您将看到您刚刚创建的方法。单击右侧的圆圈,然后向Dismiss Me按钮拖动 ;将创建一条显示连接的蓝线:
从弹出窗口中选择 Touch Up Inside事件,然后再次测试:
技术 #2:混合方法——在代码中执行 Segue
以上所有步骤都包含纯图形方式来启动 segue 并呈现另一个视图控制器。但是,这样做并不总是可行的,而且很多时候您还需要以编程方式触发 segue。为此,您必须在 Interface Builder 和代码中“破坏”您的工作。
总体思路在于在 Interface Builder 中感兴趣的视图控制器之间创建 segue,而不是从可以实际触发它的控件开始 segue,就像我们在上一节中使用的按钮。相反,segue 在代码中以编程方式执行。
为了我们的例子,让我们在第一个和第二个视图控制器之间创建一个新的转场。然而,这一次,我们将从视图控制器对象而不是像我们上次在FirstViewController场景中那样从大红色按钮启动 segue :
再次在弹出窗口中选择Present Modally选项,然后单击新的 segue 并打开 Attributes 检查器。为该标识符键入另一个标识符(例如idSegueSecondVC_2),并根据需要指定与默认标识符不同的转换。保存 Main.storyboard文件,然后打开 FirstViewController.swift。
在这里,我们将创建一个点击手势识别器并将其添加 到FirstViewController的视图中 ,我们将这样设置它,以便每当我们双击它时, 就会 显示SecondViewController。转到viewDidLoad() 方法并添加以下内容:
override func viewDidLoad() {
super.viewDidLoad()
let doubleTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.handleDoubleTap))
doubleTapGestureRecognizer.numberOfTapsRequired = 2
self.view.addGestureRecognizer(doubleTapGestureRecognizer)
}
复制代码
通过以上几行,我们创建了一个新的点击手势识别器,我们指定要进行两次点击以触发它,并将其添加到视图控制器的视图中。的handleDoubleTap() 是将我们每次双击调用视图上的方法。它还不存在,我们现在就定义它。在它的主体中,我们唯一想做的就是触发我们的新 segue,正如您将看到的,它只是一个单行操作:
@objc func handleDoubleTap() {
self.performSegue(withIdentifier: "idSegueSecondVC_2", sender: self)
}
复制代码
请注意 segue 的标识符是多么重要,因为这个标识符指定了我们想要执行的 segue。
现在通过测试应用程序,每次我们双击视图中的任意位置时,都会 向我们显示 SecondViewController。
技术#3:对于快乐的设计师和程序员——在代码中从故事板文件加载和呈现视图控制器
如果您喜欢编码,但同时保留 Interface Builder 来设计视图控制器的 UI,而无需以图形方式创建 segue,那么本节绝对适合您。我们在这里的目标是看看我们如何加载和呈现一个存在于故事板中的视图控制器,但这次我们根本不会创建 segue。
出于演示目的,我们将继续使用 SecondViewController。然而,在我们继续之前,让我们在FirstViewController场景中添加一个新按钮。因此,打开 Main.storyboard文件并在第一个下方拖动一个新按钮,如下所示,并指定标题 Load from Storyboard:
接下来,选择 SecondViewController 场景对象(黄色图标),然后打开身份检查器。我们必须设置一个故事板 ID,以便以后可以在代码中引用它。在身份检查器中,将值idSecondVC 设置为 Storyboard ID字段:
保存您的故事板文件,然后切换到 FirstViewController 文件。在其中,添加以下操作方法,稍后我们将连接到名为Load from Storyboard 的新按钮 :
@IBAction func presentSecondVC() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if let secondVC = storyboard.instantiateViewController(withIdentifier: "idSecondVC") as? SecondViewController {
self.present(secondVC, animated: true, completion: nil)
}
}
复制代码
上面的代码将从主故事板加载 SecondViewController视图控制器 ,然后将其呈现。有了更多详细信息,这些行执行以下操作:
在第一行代码中,我们访问名为Main的故事板,这是项目中存在的默认故事板 。显然,如果您有更多的故事板文件并且您想要访问不同的故事板文件,您可以提供正确的名称作为上述初始化程序中的第一个参数。 接下来,我们尝试 使用标识符idSecondVC (我们之前在 Interface Builder 的 Identity 检查器中指定的标识符)实例化视图控制器 。这个标识符是我们在故事板文件中引用正确视图控制器的方式。请注意,我们强制转换为 SecondViewController以指定我们加载的视图控制器的确切类,如果该过程成功,则视图控制器实例将保留在 对象中。secondVC 成功加载后,我们以模态方式呈现第二个视图控制器。 如果您想更改两个视图控制器之间的默认转换并在预定义的转换中选择另一个(就像您在带有 segue 的 Interface Builder 中所做的那样),然后通过添加显示新视图控制器之前显示的行来修改该方法:
@IBAction func presentSecondVC() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if let secondVC = storyboard.instantiateViewController(withIdentifier: "idSecondVC") as? SecondViewController {
// Add this line to select another transition.
secondVC.modalTransitionStyle = .crossDissolve
self.present(secondVC, animated: true, completion: nil)
}
}
复制代码
除此之外,请注意我们将SecondViewController的实例 保留 为本地属性。但是,有时您可能希望有一个类属性来保留该实例,以便您可以在加载它的方法之外引用它。在这种情况下,让我们在FirstViewController 类的开头声明以下属性 :
class FirstViewController: UIViewController {
var secondVC: SecondViewController!
...
}
复制代码
最后,让我们更新我们的方法,如下所示:
@IBAction func presentSecondVC() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if let secondVC = storyboard.instantiateViewController(withIdentifier: "idSecondVC") as? SecondViewController {
secondVC.modalTransitionStyle = .crossDissolve
self.present(secondVC, animated: true, completion: nil)
// Keep the SecondViewController instance to a class property.
self.secondVC = secondVC
}
}
复制代码
在我们能够测试应用程序之前,我们需要将presentSecondVC() 操作方法连接到新按钮。因此,打开 Main.storyboard文件,然后选择 FirstViewController 场景。在 Connections 检查器中,在Received Actions部分下找到上述 操作。单击右侧的圆圈并开始向新的红色按钮拖动;当你在它上面时松开鼠标,然后从弹出窗口中选择 Touch Up Inside事件,将建立连接。
现在通过测试应用程序,您将看到 当我们点击我们在此步骤中添加的第二个按钮时,会显示 SecondViewController。上面的几行代码完成了所需的所有“魔法”。
技术#4:对于代码狂人——完全程序化的方法
如果您喜欢在代码中创建用户界面,或者您刚刚决定不再喜欢 Interface Builder 和故事板,那么这个解决方案适合您。在这个场景中,我们将使用 ThirdViewController作为我们将展示的视图控制器,但我们不会在 Interface Builder 中进行任何设计;实际上,我们甚至不会在故事板中添加场景。一切都将在代码中发生。
让我们开始吧,让我们打开 ThirdViewController.swift文件。我们不会在这里做太多关于 UI 的事情;我们将保持简单,我们将只更改视图的背景颜色,我们将添加一个按钮,让我们关闭视图控制器。因此,在ThirdViewController 类的开头添加以下代码,为关闭按钮创建一个属性并执行一些基本配置:
class ThirdViewController: UIViewController {
lazy var dismissButton: UIButton = {
// Initialize a custom button.
let button = UIButton(type: .custom)
// Set the title and title color.
button.setTitle("Dismiss Me", for: .normal)
button.setTitleColor(.white, for: .normal)
// Set the background color.
button.backgroundColor = UIColor(red: 1.0, green: 0.17, blue: 0.33, alpha: 1.0)
// Specify the method that should be called when the button is tapped.
button.addTarget(self, action: #selector(self.dismissMe), for: .touchUpInside)
// Prevent the view from creating constraints automatically for this button.
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
...
}
复制代码
上面的注释可以帮助更容易地理解代码。如你所见,我们对上面的按钮没有做任何特别的事情,我们只是设置了它的标题、标题颜色和背景颜色。dismissMe 当按钮被点击时,我们设置为动作目标的方法如下:
@objc func dismissMe() {
self.dismiss(animated: true, completion: nil)
}
复制代码
即使我们为按钮设置了一些属性,我们仍然需要将其添加到视图控制器的根视图中并指定其大小和位置。让我们创建一个新方法来做到这一点:
func configureDismissButton() {
// Add the button to view.
self.view.addSubview(self.dismissButton)
// Align the button horizontally to the view.
NSLayoutConstraint(item: self.dismissButton, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1.0, constant: 0.0).isActive = true
// Place it close to the bottom of the view.
NSLayoutConstraint(item: self.dismissButton, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1.0, constant: -32.0).isActive = true
// Set width and height.
NSLayoutConstraint(item: self.dismissButton, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 120.0).isActive = true
NSLayoutConstraint(item: self.dismissButton, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 36.0).isActive = true
}
复制代码
大小和位置都是使用约束指定的。
这个方法必须被调用,最好的地方就是在视图出现之前。因此,实现该viewWillAppear(_:) 方法并向其添加下一个内容:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.view.backgroundColor = UIColor(red: 0.0, green: 0.69, blue: 0.92, alpha: 1.0)
self.configureDismissButton()
}
复制代码
这里发生了两个不同的任务:我们调用configureDismissButton() 将关闭按钮放置到视图的方法,并且我们还更改了视图的背景颜色。
ThirdViewController的 UI 现在已准备就绪。是时候进入本节的真正重点了,看看我们如何加载和呈现这个完全用代码设计的视图控制器。保存此文件后,转到 FirstViewController.swift,我们将在其中创建一个新的 action ( IBAction) 方法。我们只需要写两行代码,我们的目标就已经实现了。
@IBAction func presentThirdVC() {
let thirdVC = ThirdViewController()
self.present(thirdVC, animated: true, completion: nil)
}
复制代码
嗯,就这么简单!我们只是像通常对对象和类那样初始化视图控制器,然后呈现它。
在我们能够测试上面的内容之前,我们需要以某种方式触发该动作,所以让我们再向 FirstViewController场景添加一个按钮 。打开 Main.storyboard文件,并添加一个名为Third View Controller的新按钮 。将其放置在视图上后,按照与前面部分相同的步骤并将上述IBAction 方法连接到按钮的 Touch Up Inside事件(在实用程序窗格中打开连接检查器,找到该方法并开始从圆圈拖动按钮的右侧)。
现在我们可以测试了!如果您想使用默认转换以外的转换,那么您已经从上一节中了解了如何执行此操作。您只需要添加以下行并指定您喜欢的过渡:
@IBAction func presentThirdVC() {
let thirdVC = ThirdViewController()
thirdVC.modalTransitionStyle = .partialCurl
self.present(thirdVC, animated: true, completion: nil)
}
复制代码
最后,以上所有操作:
结论
在这篇文章中,我向您展示了四种不同的方式来加载和呈现视图控制器。没有更好或更坏的方法,所有方法都一样好,您将使用哪一种取决于您喜欢开发应用程序的方式。如果您是一名广泛使用 Interface Builder
和 storyboards
的程序员,那么前两种方法更适合您。相反,如果您在代码中创建所有内容,则最后一种方法可能是您最需要的。就个人而言,我通常介于技术 #3 和 #4 之间,但如有必要,我也会使用其他两种技术。
现在我们知道如何以各种方式加载和呈现视图控制器,下一步是看看我们如何在它们之间传递值。这是这个分为两部分的教程下一部分的主题,不要错过,因为有有趣的东西可以阅读;我们不会只停留在常见的方法上,还有更多要讨论的。
希望你喜欢这个……玩得开心!
这是一个链接,可以将您在本文中阅读的内容作为单个项目下载。