Использование @IBSegueAction с UINavigationController - PullRequest
3 голосов
/ 24 октября 2019

Благодаря AD Прогрессу превосходного исследования и ответу ниже, я наконец понял, что происходит с IBSegueAction с.

Если вы подключите rootViewController segue (или любой другой вид, предположительно) к IBSegueAction в его UINavigationContoller, но он не найдет его там, он будет смотреть на следующий видКонтроллер цепочки респондента, пока он не найдет IBSegueAction с правильной подписью. Если он не найдет его, он позвонит init(with coder: NSCoder) на rootViewController. Если бы только это было где-то задокументировано ...


Примечания

Примечание : Это минимальный пример для иллюстрации проблемы -как передать параметры при инициализации контроллера представления, содержащегося в модальном UINavigationController, используя IBSegueAction. Я знаю, что это не то, как вы обычно представляете подробный вид.

Примечание 2 : я знаю, как использовать segues с prepare(for segue: UIStoryboardSegue). Что я хочу знать, как это, как передать необязательные параметры при инициализации контроллера представления, содержащегося в модальном UINavigationController с использованием IBSegueAction

Примечание 3 : Я знаю, как использовать IBSegueAction для контроллера представления, не содержащегося в UINavigationController (например, нажатие на стек навигации)


Оригинальный вопрос

У меня есть следующие настройки…

enter image description here

Список людей в UITableViewController. Нажав на PersonCell, вы увидите PersonViewController внутри UINavigationController

Чтобы избежать опционального Person в PersonViewController, я пытаюсь использовать 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
    }
}

Проблема здесь, оказывается, в том, что даже если вы можете подключить действие перехода к переходу между UINavigationController и его rootViewController, оно не получаетсрабатывает, как в примере выше, super.init(coder: coder) in PersonNavController вызывает PersonViewController.init(coder: NSCoder) (что не реализовано).

Есть какие-нибудь мысли о том, как я могу заставить это работать?

1 Ответ

1 голос
/ 30 октября 2019

Чтобы достичь желаемого результата, я воссоздал простой проект, который я буду размещать на своем GitHub.

Для начала вам нужно избавиться от созданного вами перехода к вашему навигационному контроллеру и пересоздать его.

Recreate the Segue

Далее выберите подарок Модально и назовите свой переход с идентификатором

Present modally

Segue Identifier

Далее необходимо определить действие IBSegueAction в вашем первом ViewController

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) Просто нажмите исправить.

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

После этого, вы должны выбрать Segue, который выходит из второго UINavigationController в ваш целевой ViewController здесь PersonViewController и перетащить назадот перехода к первому желтому значку ViewController, из которого вы хотите отправить информацию, как на скриншоте ниже. Xcode автоматически распознает имя IBSegueAction, которое вы создали в коде.

StoryBoard

Вот и все.

Вы должны получить такой результаткак только вы нажмете любую из ячеек в первом контроллере вида

Sample

Вот пример проекта GitHub

Я также нашел больше информации о IBSegueAction здесь

...