Navigation Bar background color in SwiftUI
iOS SwiftUI tutorial Estimated reading time: 6 minutesHow many times do we need to change something in standard components supplied by Apple? Well, quite often, so I guess everything should be done keeping this simple thing in mind. But let’s check NavigationBar
in SwiftUI.
“Oh crap!” - u can say after the first 10 min of testing and trying to change something - it won’t be easy to customize that :(. So today I want to tell u about my experience related to NavigationBar
customization in SwiftUI.
history
Before we dive into SwiftUI detail, let’s refresh our memory and see how it works on UIKit. To do so, we may review official sample. When we dive into details we can see that there is no direct method of changing NavigationBar
background color, instead Apple propose force us to use UIAppearence
. Just to recap - appearance is kind of a proxy that is used to modify something without direct change. And here is also limitation - we can’t do that change on the fly
because:
iOS applies appearance changes when a view enters a window, it doesn’t change the appearance of a view that’s already in a window. To change the appearance of a view that’s currently in a window, remove the view from the view hierarchy and then put it back.
This means that we change it through navigationItem
of viewController in viewDidLoad
method (for example).
Another option is to use UINavigationController
instance, like the following:
and offcause use global settings:
Did u see it? None of the above methods didn’t provide an easy way of changing backgroundColor
. Why? There is must be some really good reason for that. Maybe this is due to UIEffectsView
inside or due to UIImageView
that serves as a background or due to some other points…
This Appearance API can result in something like:
here u can see color change using appearance on
viewDidLoad
andviewDidAppear
So the problem actually is quite old and developers always tried to make some workarounds on this - from accessing subviews and reverse engineering to developing their own custom navigationBars
.
swiftUI
Ok, how about SwiftUI
. This technology should bring to us a new experience and easy-to-use API. Apple heard a lot of responses and hopefully make some appropriate changes.
SwiftUI is an innovative, exceptionally simple way to build user interfaces across all Apple platforms with the power of Swift. … With a declarative Swift syntax that’s easy to read and natural to write, SwiftUI works seamlessly with new Xcode design tools to keep your code and design perfectly in sync. from offcial
First look at the API created for SwiftUI
and we see… nothing. Yes, nothing exists for changing navigationBar
backgroundColor
, at all - not even for appearance :(
Wow, that’s a bit unexpected. You even don’t have access to NavigationController
anymore.
Starting from SwiftUI 2.0 (iOS 14) Apple add possibility to modify navigationBar
via toolbar
.
Better, but it’s still tricky and non so easy as everyone wants. The question about the dynamic change of backgroundColor
is still open. Even more - what about iOS 13?
That the problem I faced with, like many other developers.
solution
As u can imagine I have tested all appearance solutions and some other stuff also. Indeed I ended up with some, but first, let’s see what I got.
Off cause, as u maybe already think about my first attempt was to use global appearance, but even without trying I assumed that this won’t for dynamic change thus it mention in the doc
iOS applies appearance changes when a view enters a window, it doesn’t change the appearance of a view that’s already in a window. To change the appearance of a view that’s currently in a window, remove the view from the view hierarchy and then put it back.
Anyway, this is quite a good solution for those who have a constant color of navigationBar.
to make it shiny we can even create View
modifier
:
but - remember pitfall - it won’t work for cases when u need to change color dynamically, like I want, so moving forward.
Next attempt - to change backgroundColor
directly on navigationBar
. How to achieve this? Well, let’s think about navigationBar
- every viewController
has its own configuration related to used NavigationController
. How to access this property? Aha - childViewController - when it attached to viewcontroller
with navigationController
- access granted :). How to attach? UIViewControllerRepresentable
is here to rescue. So basically we need to create a viewModifier that attaches viewController and get access to navigationBar
for future manipulation. Sounds like a good approach to go. Let’s do this:
for modification
navigationBar
I used extenstionthere are few other
AppearenceType
props, but for a test - this is ok to go
Usage:
Ok, it’s time to play.
“Just add modified for our view and everything should work like a charm” - I was thinking :). Indeed - it works, when u attach it and change on-the-flay, but not for the case when u open the screen momentary and want to change color instantly. Why? The reason is quite simple - view modifier don’t attach our viewController
as child momentary and so we haven’t access to navigationBar
at the very first moment of modifier usage :( This is because coordinator
firstly creates an object, that calls modify callback and then attaches to our view - but what we need - it’s slightly another sequence.
I can think about playing within navigationItem
property in an similar way or some other alternatives (like iterate subview that is not preferable at all)… but the result will be the same because of the process of combining SwiftUI
and UIKit
is the same…
So, what is my solution then? Ugly one :( - I switched navigationBar
into transparent
mode and every view that is needed to be modified has a ZStack
with color that extends safeArea
and the actual content. Not the perfect one.
So this is one more improvement that needs to be done for SwiftUI
.
Share on: