How do I customize the NavigationView in SwiftUI?

Mark Moeykens
Oct 16


Learn in SwiftUI:

  • How the NavigationView changed
  • How background colors affect the NavigationView
  • How to set a custom background color for the NavigationView
  • How scrolling a List affects the NavigationView
  • How to customize the NavigationView when scrolling
  • How to set a custom font for the NavigationView
  • Completely replace the NavigationView with a custom view using safeAreaInset
  • Bonus: Using ZStack for a NavigationView background

How the NavigationView changed

Starting in iOS 15, the background materials for bars (navigation bar, tab bar, etc.) were removed "giving more visual clarity to your content" as stated in this WWDC 2021 video titled "What's new in UIKit".

How background colors affect the NavigationView

When you set a background color, you will notice it will go behind the NavigationView for large and inline nav bars.
 

How to set a custom background color for the NavigationView

In SwiftUI, the rule I have observed since iOS 15 is:
 
If a shape style (such as a color or material) touches a safe area edge, then that style will bleed into the safe area.

I believe this has to do with changes in iOS 15 for UIKit that were announced during WWDC 21.

Solid Colors

Notice:
  1. That Rectangle with the background HAS to touch the safe area edge.
  2. The shape style (Color) is in a background modifier because the background accepts a "ShapeStyle" type.
  3. The background touching the safe area edge should be the full width of the NavigationView. The Rectangle shape expands horizontally to match the width of the device/NavigationView.

Materials
You can use a Divider instead of a Rectangle. Let's combine it with a material this time:

This makes it look more how NavigationViews used to look.

Gradients
Gradients are also a ShapeStyle type. 
A vertical gradient doesn't work too well using this method because only the top color will bleed into the safe area.
Instead, use a linear gradient with some angle besides a vertical one.

Notice:
  1. The Rectangle has a height and so it will add some padding to the bottom of the NavigationView.
  2. Because the Rectangle has height, I filled it with a clear color so you can see its background gradient.

How scrolling a List affects the NavigationView

When you scroll up a List, the NavigationView will automatically turn into the inline style with a material background:

But as soon as you try to customize the NavigationView, you lose this feature.

For example:

When you scroll up, the NavigationView does not collapse inline. And the rows do not scroll behind the NavigationView.

For this to work, the List has to touch the NavigationView.

So how can we customize the NavigationView AND keep this behavior while scrolling?

How to customize the NavigationView when scrolling

You will need to use the UINavigationBarAppearance from UIKit to customize the NavigationView and keep the behavior when scrolling.

Example:
With a UINavigationBarAppearance, you can customize all different aspects of the NavigationView.

This code:
  1. Sets a material (blur effect)
  2. Sets a color
  3. Assigns it to the scrollEdgeAppearance - Which you want to use when you have a scrollable view (like a List or ScrollView) touching the NavigationView.
Note: You can set these appearance properties in onAppear, the views init, or even in your App delegate when your app starts. It's up to you.

Here is the result:

Do you see the problem?
When the NavigationView switched to inline (second screenshot), it lost the customizations!

The UINavigationBarAppearance allows you to customize different states or appearances of the NavigationView.

So you need to apply the appearance you want to the standardAppearance of the NavigationView:
Now it looks correct when scrolling up:

How to set a custom font for the NavigationView

You will need the UINavigationBarAppearance again.

Notice you can set different fonts for large and inline titles:

Completely replace the NavigationView with a custom view using safeAreaInset

If you don't want to go into UIKit land and use UINavigationBarAppearance then you can create your own navigation view.

Using safeAreaInset, you can easily add a view to a scrolling view (List, ScrollView) and have the list scroll behind your custom navigation view.

Example:

Don't forget to hide the NavigationView (.navigationBarHidden(true)) when you add your own.

Summary

You learned you can customize the NavigationView in SwiftUI with the use of ShapeStyles that are set as backgrounds to views touching the safe area edge of the NavigationView.

You also learned how to use UINavigationBarAppearance for customizations.

Finally, you learned you can hide the NavigationView and create your own with the use of the safeAreaInset modifier on a scrolling view.

SwiftUI Views Quick Start Book

Do you need a SwiftUI visual reference guide?

You can download the SwiftUI Views Quick Start Book for FREE to get you started.

Bonus: Using ZStack for a NavigationView background

Someone suggested using a ZStack and a separate shape style to create a background.

This is totally doable.

Here's how I did it:


Note:
  • You need to ignore the top safe area edge.
  • The frame height is actually ZERO in this example. Greater than zero will make it grow downward from the NavigationView. Pretty interesting.

2 Replies

Chris Parker
6d
These are awesome Mark.  
Mark Moeykens
6d
Thanks, Chris!