ekazaev/route-composer

Help finding a contained navcontroller within UISplitView

benconstantine39 opened this issue ยท 5 comments

I can't figure out how to push a detail view onto a split view's current detail view. The split view's detail view hierarchy is a navigation controller with a view, but I can't get the router to find the navigation controller (highlighted in the attached screenshot):

var detailOne: DestinationStep<DetailOneController, DetailContext> {
    return StepAssembly(
      finder: ClassFinder<DetailOneController, DetailContext>(),
      factory: StoryboardFactory(storyboardName: "People", viewControllerID: "DetailOneController"))
      .using(UISplitViewController.pushToDetails())
      .from(peopleSplitView.expectingContainer())
      .assemble()
  }
  
  var detailTwo: DestinationStep<DetailTwoController, DetailContext> {
    return StepAssembly(
      finder: ClassFinder<DetailTwoController, DetailContext>(),
      factory: DetailTwoControllerFactory(dependencies: personDependencies))
      // .using(UINavigationController.push())
      // .from(detailOne.expectingContainer())
      .using(UISplitViewController.pushToDetails())
      .from(peopleSplitView.expectingContainer())
      .assemble()
  }

In the detailTwo assembly, if I use the commented out lines UINavigationController.push from detailOne.expectingContainer, the router can't find the split view's second contained navigationController and says "Type Mismatch Error: Container of UINavigationController type cannot be found to perform PushAction()", and if I instead use UISplitViewController.pushToDetails from peopleSplitView.expectingContainer it replaces the entire detail hierarchy (nav controller and DetailOneController) with a DetailTwoController and then the user can't navigate back to detailOne.

This is only causing me trouble in landscape orientation with a regular horizontal size class (in portrait it pushes on detailTwo just fine).

heirarchy

I got it to work finally by creating a custom container action similar to UISplitViewController.PushToDetailsAction and plucking out the nav controller I wanted to push on to within perform(embedding: in:). Not sure it's the best way, but works for now ๐Ÿ‘๐Ÿป

@benconstantine39 This is how it should be done at the end. I remember i had to do the same manually myself couple of years ago. UISplitViewConnroller breaks a lot of rules of UIKit, so it need some custom approaches depending on the layout and the custom behaviour. It could've been done with the custom action or using the custom finder that will find second UINavigationController in the hierarchy. It is impossible to insert UINavigationControllerin to another UINavigationController using UIKit, only UISplitViewConnroller.
You may need more custom actions in future, especially if you are changing UISplitViewConnroller default behaviour using its delegate.
Also, there will be new version released soon, that will allow to change default behaviour of the ContainerViewController. It is impossible to do currently. It may also help you with some issues.
Let me know if i can help you with something like this

It would be nice if you can contribute your action in to the library

@benconstantine39 Also, i am not aware if you know about this option. You can combine default finders results to be able to make a complicated search. For example a finder that will find second UINavigationController may look like this:

ClassFinder<UINavigationController, Any?>(options: .contained, startingPoint: .custom(ClassFinder<UINavigationController, Any?>().findViewController())

or this:

let secondNavigationController = ClassFinder<UINavigationController, Any?>(options: .contained, startingPoint: .custom(ClassFinder<UINavigationController, Any?>().findViewController()).findViewController()

Of course, in case of UISplitViewController you will need to search in the 2 different ways depending if it is collapsed or not.

But i decided to remind about this option just in case. It stays hidden for some people.

Sure, I'd be happy to contribute the action I wrote. Thanks for your quick feedback!