UITableViewController
subclass that I have wrapped in UIViewControllerRepresentable
.navigationItem.title
and navigationItem.leftBarButtonItems
in my view controller.UIViewControllerRepresentable
instance as the destination of a SwiftUI NavigationLink
within a SwiftUI NavigationView
.What is happening here?
Here's a UIKit solution that doesn't require making internal changes to your UIViewController and should only be called once, when the wrapper is added as parent.
struct MyViewControllerRepresentable: UIViewControllerRepresentable {
class Coordinator {
var parentObserver: NSKeyValueObservation?
}
func makeUIViewController(context: Self.Context) -> MyViewController {
let viewController = MyViewController()
context.coordinator.parentObserver = viewController.observe(\.parent, changeHandler: { vc, _ in
vc.parent?.title = vc.title
vc.parent?.navigationItem.rightBarButtonItems = vc.navigationItem.rightBarButtonItems
})
return viewController
}
func updateUIViewController(_ uiViewController: MyViewController, context: Self.Context) {}
func makeCoordinator() -> Self.Coordinator { Coordinator() }
}
Solved!
Problem and Expectation SwiftUI uses a UINavigationController under the hood. So, if I push a UIViewController onto a SwiftUI NavigationView using UIViewControllerRepresentable, then I would expect the navigation item and toolbar items of that view controller to be used by said navigation controller. As I mention above, they are ignored.
Root Cause It turns out the title and items are ignored because my view controller’s parent is not the UINavigationController as expected. Rather, it’s parent is an intermediary wrapper view controller used by SwiftUI under the hood, which is in turn pushed onto the navigation controller. It’s ignoring the title and items because the navigation controller is asking the wrapper for its items (which has none), rather than my view controller.
UIKit Solution So, if you want to set the title or bar button items or toolbarItems from your UIKit view controller, then you need to set them on it’s parent as such:
self.parent?.navigationItem.title = "My Title"
Furthermore, you cannot do this from viewDidLoad, because the view controller does not appear to have been wrapped by the SwiftUI parent by that time. You have to do it in viewWillAppear.
SwiftUI Solution You can also set the title and bar buttons from SwiftUI. On your UIViewControllerRepresentable instance, just add .navigationBarTitle and leading/trailing items as you normally would. Then you can have the buttons talk to your view controller from within your UIViewControllerRepresentable implementation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With