将 @IBSegueAction 与 UINavigationController 结合使用

Ash*_*lls 12 ios uistoryboard swift ibsegueaction

感谢AD Progress出色的研究和下面的回答,我终于弄清楚了IBSegueActions是怎么回事。

如果您将rootViewControllersegue(或任何其他类型,大概)连接到IBSegueActionin 中UINavigationContoller,但在那里找不到它,它将查看响应者链上的下一个视图控制器,直到找到IBSegueAction具有正确签名的 。如果它没有找到一个,它会叫init(with coder: NSCoder)rootViewController。如果只是在某处记录了这一点……


笔记

注意:这是一个说明问题的最小示例 - 如何在UINavigationController使用IBSegueAction. 我知道这不是您通常呈现详细视图的方式。

注 2:我知道如何将 segues 与prepare(for segue: UIStoryboardSegue). 我想知道如何初始化一个视图控制器时,如何通过非可选参数包含在一个模式UINavigationController使用IBSegueAction

注 3:我知道如何将 anIBSegueAction用于未包含在 a 中的视图控制器UINavigationController(例如推送导航堆栈)


原始问题

我有以下设置…

在此处输入图片说明

中的人员列表UITableViewController。轻敲 aPersonCell呈现 aPersonViewController内 aUINavigationController

为了避免在 中有一个可选PersonPersonViewController,我试图使用IBSegueActions。

我首先想到的是,我需要一个用于之间的SEGUEPeopleViewControllerPersonNavController,和之间的SEGUEPersonNavControllerPersonViewController如下...

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)
    }
}
Run Code Online (Sandbox Code Playgroud)
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)
    }
}
Run Code Online (Sandbox Code Playgroud)
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
    }
}
Run Code Online (Sandbox Code Playgroud)

事实证明,这里的问题是,即使您可以将 segue 操作连接到 aUINavigationController和 its之间的 segue rootViewController,它也不会像上面的示例中的super.init(coder: coder)inPersonNavController调用 那样被触发PersonViewController.init(coder: NSCoder)(未实现) .

关于如何让这个工作的任何想法?

AD *_*ess 10

为了达到你想要的结果,我重新创建了一个简单的项目,我将在我的 GitHub 上链接它。

首先,您需要摆脱为导航控制器创建的 segue 并重新创建它。

重新创建 Segue

接下来选择present Modally并用标识符命名你的segue

模态呈现

转场标识符

接下来你需要在你的第一个 ViewController 中定义 IBSegueAction

视图控制器.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)
    }
}
Run Code Online (Sandbox Code Playgroud)

然后init?(coder: NSCoder, personDetails: String)在PersonViewController中添加的实现如下。Xcode 会大喊你需要实现required init?(coder: NSCoder) Just tap fix。

PersonViewController.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
    }
}
Run Code Online (Sandbox Code Playgroud)

下一步是在情节提要中选择 ViewController 的顶部,以便您可以看到三个图标。

选择从中传递数据的 ViewController

之后,您必须选择从第二个 UINavigationController 到目标 ViewController 的 Segue 在这里 PersonViewController 并从 segue 拖回您想要从中发送信息的 ViewController 的第一个黄色图标,如下面的屏幕截图所示。Xcode 将自动识别您在代码中创建的 IBSegueAction 名称。

故事板

就是这样。

一旦你点击第一个视图控制器中的任何单元格,你应该得到这样的结果

样本

这是示例项目GitHub

我还在这里找到了有关 IBSegueAction 的更多信息