I am working with a SwiftUI based app that relies on a NavigationView
to transition from screens.
I have a requirement to set the background color on the navigation bar and have found code that makes this work most of the time.
When the app is launched in portrait mode, everything works properly across rotations.
However, when the app is launched in landscape mode, the bar is the default gray and only updates after the first rotation.
Below, I have the minimal amount of code to recreate my problem:
import SwiftUI
struct ContentView: View {
var body: some View {
return NavigationView {
List {
NavigationLink(destination: Text("A")) {
Text("See A")
}
}
.background(NavigationConfigurator { navigationConfigurator in
navigationConfigurator.navigationBar.barTintColor = .orange
})
.navigationBarTitle(Text(verbatim: "Home"), displayMode: .inline)
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
struct NavigationConfigurator: UIViewControllerRepresentable {
var configure: (UINavigationController) -> Void = { _ in }
func makeUIViewController(context: UIViewControllerRepresentableContext<NavigationConfigurator>) -> UIViewController {
UIViewController()
}
func updateUIViewController(_ uiViewController: UIViewController,
context: UIViewControllerRepresentableContext<NavigationConfigurator>) {
if let navigationController = uiViewController.navigationController {
self.configure(navigationController)
print("Successfully obtained navigation controller")
} else {
print("Failed to obtain navigation controller")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
How the app appears when launched in portrait mode:
...and rotated to landscape...
Finally, how it looks when launched in landscape mode.
I have also logged out the NavigationConfigurator
and have found that when it launches in portrait mode, there are two calls made. The first one fails to find the navigation controller, but the second one does.
When I launch in landscape mode, only a single call is made which fails to find it. Upon rotation, it then finds it and successfully updates the color.
I did attempt to force redraws via @State
management, but that was unsuccessful.
Short of locking the app to portrait mode, I have run out of ideas.
If it is only about bar tint color and not needed navigation controller for more then it is simpler to use appearance, as
struct ContentView: View {
init() {
UINavigationBar.appearance().barTintColor = UIColor.orange
}
// .. your other code here
This solution works fine with any initial orientation. Tested with Xcode 11.4.
Alternate:
If you still want access via configurator, the following solution (tested & worked) by my experience is more reliable
struct NavigationConfigurator: UIViewControllerRepresentable {
var configure: (UINavigationController) -> Void = { _ in }
func makeUIViewController(context: UIViewControllerRepresentableContext<NavigationConfigurator>) -> UIViewController {
let controller = UIViewController()
DispatchQueue.main.async {
if let navigationController = controller.navigationController {
self.configure(navigationController)
print("Successfully obtained navigation controller")
} else {
print("Failed to obtain navigation controller")
}
}
return controller
}
func updateUIViewController(_ uiViewController: UIViewController,
context: UIViewControllerRepresentableContext<NavigationConfigurator>) {
}
}