swiftuinavigationcontrollersegue

performSegue() works to show (push) either of two UIViewControllers, but one has nil navigationController


I have a UIViewController that is in a navigation stack. There are two different segues that can be triggered from this UIViewController using:

performSegue(withIdentifier: "SomeIdentifier", sender: self)

There are two different identifiers to show (push) either of two different subclasses of UIViewController onto the navigation stack.

In both cases, the shown (pushed) view controller appears correctly and mostly functions correctly, except that in one case the view controller has a nil navigationController. Of course this prevents several functions from being available (eg, I cannot control the standard navigation controller toolbar from this view controller).

As far as I can tell, I'm doing the same thing in both segues, and in both UIViewController subclasses (in every way that is relevant). The two segues are both configured in a Storyboad as "Show (eg, Push)" segues. They both have identifiers that are called by the performSegue() (which clearly work, as they load up their correct view controllers). They both use similar code in the prepare(for segue: UIStoryboardSegue, sender: Any?) function. Relevant code is below.

    @objc func handleButtonAction() {
//      performSegue(withIdentifier: "Profile Chart", sender: self)
        performSegue(withIdentifier: "Feature Geometry", sender: self)
    }

Comment out one of those performSegue() lines and uncomment the other to try the other segue:

    @objc func handleButtonAction() {
        performSegue(withIdentifier: "Profile Chart", sender: self)
//      performSegue(withIdentifier: "Feature Geometry", sender: self)
    }

Preparing for segue (for either of the two segues):

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let geometryController = segue.destination as? FeatureGeometryController {
            geometryController.feature = feature
        } else if let profileController = segue.destination as? ProfileChartController {
            profileController.feature = feature
        }
    }

In the ProfileChartController, everything is fine. In the FeatureGeometryController most things work fine. The feature is assigned correctly, and its data displays correctly. But its navigationController is nil (as determined below), so there is some functionality that I just can't access (include managing the standard toolbar, and calling the root view controller of the navigation controller).

In the FeatureGeometryController, the print statement in viewDidLoad() below displays navigationController: nil.

    override func viewDidLoad() {
        super.viewDidLoad()
        print("navigationController:  \(String(describing: navigationController))")

I have literally dozens of similar segues to/from dozens of custom UIViewController subclasses in this navigation stack, and this is the only one that has this issue. I cannot see what I'm doing differently with this one.

What am I missing? What am I doing wrong here?


Solution

  • Solved! This was very obscure (at least to me!):

    As can be seen in the prepare() function in the question, the following property gets set: geometryController.feature = feature.

    This property had a didSet{} block in the FeatureGeometryController (the segue's destination) which included this line: tableView.tableHeaderView = someUILabel

    It seems that having this code in this didSet{} block must be wrong I' not sure why. It seems that the view is loaded OK at this point (because this did actually work... the label did appear nicely as the header of the table view.)

    In any case, the solution was to move this line out of the didSet{} block and into the viewDidLoad() function.

    It's unclear to me why it caused this particular bad behaviour. I would be interested in an explanation, if anybody has one.