将@IBSegueAction 与 UINavigationController 一起使用
Using @IBSegueAction with UINavigationController
感谢 AD Progress 出色的研究和下面的回答,我终于弄清楚了 IBSegueAction
s 是怎么回事。
如果您将 rootViewController
segue(或任何其他类型,大概)连接到其 UINavigationContoller
中的 IBSegueAction
,但它在那里找不到它,它将查看响应者链上的下一个视图控制器,直到找到具有正确签名的 IBSegueAction
。如果找不到,它将在 rootViewController
上调用 init(with coder: NSCoder)
。如果在某处记录了这一点……
备注
注意:这是一个说明问题的最小示例 - 如何在初始化包含在模态 UINavigationController
中的视图控制器时传递参数使用 IBSegueAction
。我知道这不是您通常呈现详细视图的方式。
注释 2:我知道如何在 prepare(for segue: UIStoryboardSegue)
中使用 segues。我想知道如何在使用 IBSegueAction
初始化模态 UINavigationController
中包含的视图控制器时如何传递非可选参数
注释 3:我知道如何将 IBSegueAction
用于未包含在 UINavigationController
中的视图控制器(例如推动导航堆栈)
原题
我有以下设置…
UITableViewController
中的人员列表。点击 PersonCell
会在 UINavigationController
中显示 PersonViewController
为了避免在 PersonViewController
中出现可选的 Person
,我正在尝试使用 IBSegueAction
s。
我的第一个想法是我需要一个用于 PeopleViewController
和 PersonNavController
之间的转接,以及 PersonNavController
和 PersonViewController
之间的转接,如下所示…
class PeopleViewController: UITableViewController {
// Table view delegate methods here
@IBSegueAction func showPerson(_ coder: NSCoder, sender: Any?) -> PersonNavController? {
guard let cell = sender as? PersonCell else { return nil }
return PersonNavController(coder: coder, person: cell.person)
}
}
class PersonNavController: UINavigationController {
private let person: Person
required init?(coder: NSCoder, person: Person) {
self.person = person
super.init(coder: coder)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@IBSegueAction func root(_ coder: NSCoder) -> PersonViewController? {
return PersonViewController(coder: coder, person: person)
}
}
class PersonViewController: UIViewController {
@IBOutlet private weak var name: UILabel!
private let person: Person
required init?(coder: NSCoder, person: Person) {
self.person = person
super.init(coder: coder)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
name.text = person.firstName
}
}
事实证明,这里的问题是,即使您可以将一个 segue 操作连接到 UINavigationController
和它的 rootViewController
之间的 segue,它也不会被触发,因为,在上面的示例中,PersonNavController
中的 super.init(coder: coder)
调用 PersonViewController.init(coder: NSCoder)
(未实现)。
关于如何让它工作有什么想法吗?
为了达到您想要的结果,我重新创建了一个简单的项目,我将 link 在我的 GitHub 上 link。
首先,您需要删除为导航控制器创建的转场并重新创建它。
下一个 select 以模态方式呈现并使用标识符命名您的 segue
接下来您需要在第一个 ViewController
中定义 IBSegueAction
ViewController.swift
import UIKit
class ViewController: UITableViewController {
let persons = ["Robert", "Peter", "Dave"]
var selectedPerson = 0
override func viewDidLoad() {
super.viewDidLoad()
}
@IBSegueAction
private func showPerson(coder: NSCoder, sender: Any?, segueIdentifier: String?)
-> PersonViewController? {
return PersonViewController(coder: coder, personDetails: persons[selectedPerson])
}
//MARK:- TableView Methods
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return persons.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "personCell", for: indexPath)
cell.textLabel?.text = persons[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedPerson = indexPath.row
performSegue(withIdentifier: "ShowPerson", sender: self)
}
}
然后在PersonViewController中添加init?(coder: NSCoder, personDetails: String)
的实现如下。
Xcode 会大喊你需要执行 required init?(coder: NSCoder)
只需点击修复。
人ViewController.swift
import UIKit
class PersonViewController: UIViewController {
//Your data passed in below as non optional constant
let personDetails: String
//The received data gets initialized below
init?(coder: NSCoder, personDetails: String) {
self.personDetails = personDetails
super.init(coder: coder)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@IBOutlet weak var personLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
personLabel.text = personDetails
}
}
下一步是 select 故事板中 ViewController 的顶部,以便您可以看到三个图标。
之后,你必须 select 从第二个 UINavigationController 出来的 Segue 到你的目的地 ViewController 这里 PersonViewController 然后从 segue 拖回第一个黄色ViewController 你想从下面的屏幕截图中发送信息的图标。 Xcode 将自动识别您在代码中创建的 IBSegueAction 的名称。
就是这样。
点击第一个视图控制器中的任何单元格后,您应该会得到这样的结果
这是示例项目GitHub
我还找到了更多关于 IBSegueAction 的信息here
我认为这不是最好的方法。
但是我不想创建
自定义导航Class
目标中的初始化程序ViewController
故事板
继续
// MARK: - Segue
@available(iOS 13.0, *)
extension DrawingViewController {
@IBSegueAction private func prepareImagePickerSegue(coder: NSCoder, sender: Any?, segueIdentifier: String?) -> UIViewController? {
guard let navigationController = UINavigationController(coder: coder),
let viewController = UIStoryboard(name: "ImagePicker", bundle: nil).instantiateViewController(withIdentifier: "ImagePickerViewController") as? ImagePickerViewController else { return nil }
// Prepare the target viewController
viewController.delegate = self
// Set navigation
navigationController.setViewControllers([viewController], animated: false)
return navigationController
}
}
我遇到了同样的问题,但成功了。不要在 Show Detail segue 上设置 segue 动作选择器,而是在 Navigation Controller 的 Relationship Segue 上设置它,如以下屏幕截图所示:
感谢 AD Progress 出色的研究和下面的回答,我终于弄清楚了 IBSegueAction
s 是怎么回事。
如果您将 rootViewController
segue(或任何其他类型,大概)连接到其 UINavigationContoller
中的 IBSegueAction
,但它在那里找不到它,它将查看响应者链上的下一个视图控制器,直到找到具有正确签名的 IBSegueAction
。如果找不到,它将在 rootViewController
上调用 init(with coder: NSCoder)
。如果在某处记录了这一点……
备注
注意:这是一个说明问题的最小示例 - 如何在初始化包含在模态 UINavigationController
中的视图控制器时传递参数使用 IBSegueAction
。我知道这不是您通常呈现详细视图的方式。
注释 2:我知道如何在 prepare(for segue: UIStoryboardSegue)
中使用 segues。我想知道如何在使用 IBSegueAction
UINavigationController
中包含的视图控制器时如何传递非可选参数
注释 3:我知道如何将 IBSegueAction
用于未包含在 UINavigationController
中的视图控制器(例如推动导航堆栈)
原题
我有以下设置…
UITableViewController
中的人员列表。点击 PersonCell
会在 UINavigationController
PersonViewController
为了避免在 PersonViewController
中出现可选的 Person
,我正在尝试使用 IBSegueAction
s。
我的第一个想法是我需要一个用于 PeopleViewController
和 PersonNavController
之间的转接,以及 PersonNavController
和 PersonViewController
之间的转接,如下所示…
class PeopleViewController: UITableViewController {
// Table view delegate methods here
@IBSegueAction func showPerson(_ coder: NSCoder, sender: Any?) -> PersonNavController? {
guard let cell = sender as? PersonCell else { return nil }
return PersonNavController(coder: coder, person: cell.person)
}
}
class PersonNavController: UINavigationController {
private let person: Person
required init?(coder: NSCoder, person: Person) {
self.person = person
super.init(coder: coder)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@IBSegueAction func root(_ coder: NSCoder) -> PersonViewController? {
return PersonViewController(coder: coder, person: person)
}
}
class PersonViewController: UIViewController {
@IBOutlet private weak var name: UILabel!
private let person: Person
required init?(coder: NSCoder, person: Person) {
self.person = person
super.init(coder: coder)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
name.text = person.firstName
}
}
事实证明,这里的问题是,即使您可以将一个 segue 操作连接到 UINavigationController
和它的 rootViewController
之间的 segue,它也不会被触发,因为,在上面的示例中,PersonNavController
中的 super.init(coder: coder)
调用 PersonViewController.init(coder: NSCoder)
(未实现)。
关于如何让它工作有什么想法吗?
为了达到您想要的结果,我重新创建了一个简单的项目,我将 link 在我的 GitHub 上 link。
首先,您需要删除为导航控制器创建的转场并重新创建它。
下一个 select 以模态方式呈现并使用标识符命名您的 segue
接下来您需要在第一个 ViewController
中定义 IBSegueActionViewController.swift
import UIKit
class ViewController: UITableViewController {
let persons = ["Robert", "Peter", "Dave"]
var selectedPerson = 0
override func viewDidLoad() {
super.viewDidLoad()
}
@IBSegueAction
private func showPerson(coder: NSCoder, sender: Any?, segueIdentifier: String?)
-> PersonViewController? {
return PersonViewController(coder: coder, personDetails: persons[selectedPerson])
}
//MARK:- TableView Methods
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return persons.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "personCell", for: indexPath)
cell.textLabel?.text = persons[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedPerson = indexPath.row
performSegue(withIdentifier: "ShowPerson", sender: self)
}
}
然后在PersonViewController中添加init?(coder: NSCoder, personDetails: String)
的实现如下。
Xcode 会大喊你需要执行 required init?(coder: NSCoder)
只需点击修复。
人ViewController.swift
import UIKit
class PersonViewController: UIViewController {
//Your data passed in below as non optional constant
let personDetails: String
//The received data gets initialized below
init?(coder: NSCoder, personDetails: String) {
self.personDetails = personDetails
super.init(coder: coder)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@IBOutlet weak var personLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
personLabel.text = personDetails
}
}
下一步是 select 故事板中 ViewController 的顶部,以便您可以看到三个图标。
之后,你必须 select 从第二个 UINavigationController 出来的 Segue 到你的目的地 ViewController 这里 PersonViewController 然后从 segue 拖回第一个黄色ViewController 你想从下面的屏幕截图中发送信息的图标。 Xcode 将自动识别您在代码中创建的 IBSegueAction 的名称。
就是这样。
点击第一个视图控制器中的任何单元格后,您应该会得到这样的结果
这是示例项目GitHub
我还找到了更多关于 IBSegueAction 的信息here
我认为这不是最好的方法。
但是我不想创建
自定义导航Class
目标中的初始化程序ViewController
故事板
继续
// MARK: - Segue
@available(iOS 13.0, *)
extension DrawingViewController {
@IBSegueAction private func prepareImagePickerSegue(coder: NSCoder, sender: Any?, segueIdentifier: String?) -> UIViewController? {
guard let navigationController = UINavigationController(coder: coder),
let viewController = UIStoryboard(name: "ImagePicker", bundle: nil).instantiateViewController(withIdentifier: "ImagePickerViewController") as? ImagePickerViewController else { return nil }
// Prepare the target viewController
viewController.delegate = self
// Set navigation
navigationController.setViewControllers([viewController], animated: false)
return navigationController
}
}
我遇到了同样的问题,但成功了。不要在 Show Detail segue 上设置 segue 动作选择器,而是在 Navigation Controller 的 Relationship Segue 上设置它,如以下屏幕截图所示: