Navigation Bar Items After Push from Swiftui to UIkit

Navigation Bar Items not shown when UIKit SwiftUI UIKit

SwiftUI view used its own navigation controller and it ignores the UIKit navigation controller so the possible solution is to set your navigation bar to parent controller and set your navigation bar element by coding.

like this

class MyUIKitViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// ADD NAV BUTTON HERE - must be in DispatchQueue
DispatchQueue.main.async {
self.parent?.navigationItem.title = "Your Title"
self.parent?.navigationItem.rightBarButtonItems?.append(UIBarButtonItem(systemItem: .camera))
}
}
}

Another solution is to use self.navigationController?.navigationBar.topItem?. This is the exact same as above. It gives a topItem.

class MyUIKitViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// ADD NAV BUTTON HERE - must be in DispatchQueue
DispatchQueue.main.async {
self.navigationController?.navigationBar.topItem?.title = "Your Title"
self.navigationController?.navigationBar.topItem?.rightBarButtonItems?.append(UIBarButtonItem(systemItem: .camera))
}
}
}

I used DispatchQueue for when the view is initiated then the parent is not set at this time. (You can use also use viewWillAppear instead of DispatchQueue.main.async)

Edit

If you don't want to add elements programmatically and if you need to use the same element from the storyboard then you can use this approch.

class MyUIKitViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// ADD NAV BUTTON HERE
navigationItem.rightBarButtonItems?.append(UIBarButtonItem(systemItem: .camera))

DispatchQueue.main.async { //<--Here
self.navigationController?.navigationBar.topItem?.title = self.navigationItem.title
self.navigationController?.navigationBar.topItem?.rightBarButtonItems = self.navigationItem.rightBarButtonItems
}
}
}

UIViewControllerRepresentable: Navigation Title and Bar Button Items Ignored in NavigationView

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.

How to achieve the auto-shrink SwiftUI Navigation Title with UIKit?

As well as providing a configuration for large titles, you need to instruct your navigationController to use large titles

navigationController.navigationBar.prefersLargeTitles = true

The default configuration for a view controller should be to automatically use large titles if that is the preferred choice. However you can set it explicitly per view controller:

navigationItem.largeTitleDisplayMode = .always // or .automatic or .never

If the nav controller has prefersLargeTitle = false then large titles will never be displayed whatever the largeTitleDisplayMode setting.

Set navigation bar item style in SwiftUI

iOS 14+

It is worth noting that using ToolbarItem(placement:) within a toolbar modifier will automatically apply emboldened text to buttons in the .confirmationAction placement position.

For example:

struct MyView: View {
var body: some View {
NavigationView {
Form {
// other elements
}
.navigationTitle("Edit Publication")
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel") { }
}
ToolbarItem(placement: .confirmationAction) {
Button("Save") { }
}
}
}
}

As you can see from the illustration below, the Save button appears in bold.

Toolbar with contextually emboldened buttons

If you want a button in the same place as the Save button below but not to be emphasised, you'd use the .primaryAction modifier.

Using the placement types that describe toolbar items' context – rather than using the deprecated navigationBarItems modifier, or the .navigationBarTrailing and .navigationBarLeading placement values – is the best way to make your SwiftUI views adapt to any changes in future versions of iOS.

They're also applicable across multiple platforms that don't necessarily have navigation bars, and other platforms may choose to render them differently. For example, using .confirmationAction on macOS creates a button with the app accentColor as a background.

SwiftUI UIHostingController navigation title animation broken

Found the solution for this issue. UIHostingController is in fact just a normal UIViewController (with some add-ons for SwiftUI). Which means everything which is available for a UIViewController is as well available for UIHostingController. So the solution for this is to set everything related to the navigation bar on UIHostingController and not in the wrapped SwiftUI View.

let vc = UIHostingController(rootView: Content())
vc.title = "My custom title"

Also all navigations buttons work much better if directly set on UIHostingController. A good alternative is also to directly derive from UIHostingController and implement custom needed behavior there.



Related Topics



Leave a reply



Submit