将@IBSegueAction与UINavigationController一起使用

时间:2019-10-24 16:14:03

标签: ios swift uistoryboard ibsegueaction

感谢下面的 AD Progress 研究和解答,我终于弄清了IBSegueAction的情况。

如果您在rootViewController中将IBSegueAction序列(或其他类型,大概是)连接到UINavigationContoller,但在此处找不到它,它将查看响应者链上的下一个视图控制器,直到找到具有正确签名的IBSegueAction。如果找不到,它将在init(with coder: NSCoder)上调用rootViewController。如果那只是记录在某处……


注释

注意:这是一个说明问题的最小示例-使用UINavigationController初始化模式IBSegueAction中包含的视图控制器时如何传递参数。我知道这通常不是您呈现详细视图的方式。

注释2 :我知道如何在prepare(for segue: UIStoryboardSegue)中使用segues。我想知道如何在使用UINavigationController

初始化模式IBSegueAction中包含的视图控制器时如何传递非可选参数。

注释3 :我知道如何使用IBSegueAction来使用UINavigationController中不包含的视图控制器(例如,推入导航堆栈)< / em>


原始问题

我有以下设置...

enter image description here

UITableViewController中的人员列表。轻按PersonCell会在PersonViewController内出现UINavigationController

为了避免在Person中使用可选的PersonViewController,我尝试使用IBSegueAction

我首先想到的是,PeopleViewControllerPersonNavController之间的安全,以及PersonNavControllerPersonViewController之间的安全,我需要一个…… / p>

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

事实证明,这里的问题是,即使您可以将UINavigationController与它的rootViewController之间的segue进行segue动作,它也不会被触发,因为上面的示例中,super.init(coder: coder)中的PersonNavController调用PersonViewController.init(coder: NSCoder)(未实现)。

关于如何使它工作的任何想法?

3 个答案:

答案 0 :(得分:1)

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

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

Recreate the Segue

下一步选择当前模式,并使用标识符命名您的segue

Present modally

Segue Identifier

接下来,您需要在第一个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) 只需点击修复。

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的顶部,以便您可以看到三个图标。

Select ViewController from which you pass data

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

StoryBoard

就是这样。

点击第一个View Controller中的任何单元格,您应该得到这样的结果

Sample

这是示例项目GitHub

我还找到了有关IBSegueAction here

的更多信息

答案 1 :(得分:1)

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

但是我不想创建

  1. 自定义导航类

  2. 目标ViewController中的
  3. 初始化器


故事板

enter image description here


Segue

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

答案 2 :(得分:-1)

我遇到了同样的问题,但是设法使它起作用。不用在“显示详细信息”序列上设置您的序列动作选择器,而是在“导航控制器的关系”序列上进行设置,如以下屏幕截图所示:

right clicking navigation controller's relationship segue