13

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

如果你将一个rootViewControllersegue(或任何其他类型,大概)连接到IBSegueAction它的 中UINavigationContoller,但它没有在那里找到它,它会查看响应者链上的下一个视图控制器,直到找到IBSegueAction具有正确签名的一个。如果找不到,它会调用init(with coder: NSCoder). rootViewController如果只有那被记录在某个地方……</p>


笔记

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

注 2:我知道如何使用 segues 和prepare(for segue: UIStoryboardSegue). 我想知道如何在UINavigationController使用IBSegueAction

注 3:我知道如何使用IBSegueAction不包含在视图控制器中的视图控制器UINavigationController(例如推送导航堆栈)


原始问题

我有以下设置……</p>

在此处输入图像描述

中的人员列表UITableViewController。点击 aPersonCell会呈现 a PersonViewControllerinside aUINavigationController

为避免在 中包含可选项PersonPersonViewController我尝试使用IBSegueActions.

我的第一个想法是我需要一个用于 thePeopleViewController和 the之间的 segue 以及 the和PersonNavControllerthe 之间的 segue如下......</p> PersonNavControllerPersonViewController

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
    }
}

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

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

4

3 回答 3

11

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

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

重新创建 Segue

接下来选择present Modally并用标识符命名你的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)
    }
}

然后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
    }
}

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

选择从中传递数据的 ViewController

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

故事板

而已。

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

样本

这是示例项目GitHub

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

于 2019-10-30T13:14:16.200 回答
1

我有同样的问题,但设法让它工作。不要在 Show Detail segue 上设置 segue 操作选择器,而是在 Navigation Controller 的关系 Segue 上设置它,如下面的屏幕截图所示:

右键单击导航控制器的关系segue

于 2020-02-05T22:30:30.847 回答
0

我认为这不是最好的方法。

但我不想创建

  1. 自定义导航类

  2. 目标 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
     }
}
于 2020-01-31T06:59:33.757 回答