Xcode11中 Storyboard 页面跳转的一些修改

Posted 乐Coding

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Xcode11中 Storyboard 页面跳转的一些修改相关的知识,希望对你有一定的参考价值。

Xcode11发布后,我们一直在惊叹SwiftUI的强大,却忽略了storyboard的一些改进。据我所知,AppleWWDC期间也没有提到过segue自定义初始化(initializers)的修改。我们可以从Xcode和ios 13发行说明中看到一些修改提示。

SegueAction

假设我们使用storyboards时通过Segue进行页面跳转

Xcode11中 Storyboard 页面跳转的一些修改
001

左侧的BookController有一个Button,点击该按钮时,它会跳转到右侧的PreviewController,以显示该Book的详情。使用storyboard,您可以通过从按钮拖拽到目标控制器创建segue实现页面跳转。

Xcode11中 Storyboard 页面跳转的一些修改
002

要完成segue的配置,必须在属性检查器( attributes inspector)中添加一个唯一标识符(Identifier):

Xcode11中 Storyboard 页面跳转的一些修改
003

页面跳转时,我们需要将Model数据(本例中的Book)从源传递到目标ViewController。在Xcode 10和iOS 12中,我们使用源视图控制器中的 prepare(for:sender) 来实现:

// BookController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let previewController = segue.destination as? PreviewController else {
fatalError("Missing PreviewController")
}
previewController.book = book
}

注意,我们没有创建目标视图控制器。UIKit通过调用init(coder:) 方法来初始化新的ViewController。我们可以配置数据并将其传递给controller,但只能在UIKit创建controller之后。

我的PreviewController有一个Book属性,但它是可选的:

// PreviewController
import UIKit

final class PreviewController: UIViewController {
@IBOutlet private var textView: UITextView!

var book: Book?

override func viewDidLoad() {
super.viewDidLoad()
title = book?.title
textView.text = book?.preview
}
}

book属性是optional的,因为在初始化PreviewController期间无法设置它。我想在创建PreviewController时初始化book,并把book成员变量改成let不可修改的,怎么办呢?

segue是UIKit在调用segue方法期间,创建目标视图控制器的一种方法。

从Xcode 11开始,我们还有另一种将数据传递到目标视图控制器的方法。我们可以通过使用@IBSequeAction在源视图控制器中标记一个方法来创建segue action

@IBSegueAction
private func showPreview(coder: NSCoder, sender: Any?, segueIdentifier: String?)
-> PreviewController? {
return PreviewController(coder: coder, book: book)
}

SegueAction方法具有三个参数。必需的NSCoder参数以及可选的sender和segueIdentifier。如果不需要,我们可以省略可选参数:

@IBSegueAction
private func showPreview(coder: NSCoder)
-> PreviewController? {
return PreviewController(coder: coder, book: book)
}

如果该方法返回nil,则UIKit将调用 init(coder:) 方法来创建视图控制器。SegueAction不会阻止segue的发生。无论哪种方式,都会在segue对象中传递新创建的视图控制器给prepare(for:sender)方法。由于这里我们不再需要它,因此从视图控制器中删除了prepare(for:sender)方法。

注意,Swift 5.1允许我们省略具有单个表达式的方法的return语句,我们进一步简化SegueAction方法:

@IBSegueAction
private func showPreview(coder: NSCoder)
-> PreviewController? {
PreviewController(coder: coder, book: book)
}

要将storyboard中的segue连接到ViewController中的SegueAction方法,请从segue对象向view controller拖动,然后选择SegueAction方法:

Xcode11中 Storyboard 页面跳转的一些修改
004
Xcode11中 Storyboard 页面跳转的一些修改
0041

如果正确连接了SegueAction,在segue的属性检查器(attributes inspector)中会看到该方法的Selector变成了你定义的方法:

Xcode11中 Storyboard 页面跳转的一些修改
005

现在,PreviewController可以创建一个自定义initializer方法,该初始化方法在创建视图控制器时 用我们SegueAction传递的Book做参数:

// PreviewController
import UIKit
final class PreviewController: UIViewController {
@IBOutlet private var textView: UITextView!

let book: Book

init?(coder: NSCoder, book: Book) {
self.book = book
super.init(coder: coder)
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func viewDidLoad() {
super.viewDidLoad()
title = book.title
textView.text = book.preview
}
}

自定义初始化方法必须调用super.init(coder:) 并传递从SegueAction中接收到的coder参数。另外,Book属性不再是可选的,因此我们可以将其从var更改为let

自定时实例化方法

页面跳转时我们可以使用storyboards而不必使用segue。例如,我可以将按钮连接到视图控制器中的IBAction方法,而不是创建segue:

@IBAction private func showPreview(_ sender: UIButton) {
guard let previewController = storyboard?.instantiateViewController(
withIdentifier: "PreviewController") as? PreviewController else {
fatalError("Unable to create PreviewController")
}
previewController.book = book
show(previewController, sender: self)
}

showPreview方法从storyboard中实例化视图控制器,进行配置然后显示。这种方式存在与segue相同的一些问题。我们调用storyboard的instantiateViewController(withIdentifier:)方法,然后返回一个ViewController。任何Model数据(例如我们的Book)都必须是目标视图控制器的可选属性。

苹果在iOS 13发行说明中对这个问题进行了完善:

You can now invoke a custom initializer from a creation block that’s passed through instantiateInitialViewController(creator:) or instantiateViewController(identifier:creator:).

例如,使用我们的自定义初始化方法传递Model数据:

@IBAction private func showPreview(_ sender: UIButton) {
guard let previewController = storyboard?.instantiateViewController(
identifier: "PreviewController",
creator: { coder in
PreviewController(coder: coder, book: self.book)
}) else {
fatalError("Unable to create PreviewController")
}
show(previewController, sender: self)
}

storyboard.instantiateViewController多了一个creator参数,creator block提供了我们需要传递给自定义view controller初始化方法的coder,现在就把book可选参数改成let了。

迟到总比没有好

未来可能是SwiftUI一统天下,但我仍然很高兴storyboard有所完善。第一个改动使用Xcode11开发低版本iOS也可以使用,第二个改动就需要iOS13及以上版本支持了。所以也许这就是迟到总比没有好

作者: kharrison
翻译整理: 乐Coding
原文地址: https://useyourloaf.com/blog/better-storyboards-with-xcode-11/



精彩推荐




以上是关于Xcode11中 Storyboard 页面跳转的一些修改的主要内容,如果未能解决你的问题,请参考以下文章

实现跳转的几种法式

代码视图与StoryBoard.Xib文件视图的跳转

代码设定的按钮与storyboard中的xib页面间的跳转

Xcode使用介绍之二:创建UI界面+连线

xcode跳转问题. 忘大神解答.

Xcode 从 viewcontroller.swift 跳转到 storyboard