Customizing SwiftUI Navigation Bar

Pacu
4 min readJan 8, 2020

--

The (not so) good, the bad and the ugly

Let’s say that you are asked to do a Dark Background and a transparent navigation bar with white buttons and title.

SwiftUI comes packed with new features, a renewed air of simplicity and huge productivity boosts for developers. Yet, it still falls short on some aspects and to be fair, in most of the cases, UIKit had the same or similar pains.

It comes a time in a developer’s life when your next App’s design is both astonishing and nothing like the out-of-the-box components. SwiftUI helps a lot in that way but the Devil is in the details.

Your design is cristal clear (well, dark in this case)

Transparent navigation bar on black background with white tint

So you to do something like this:

struct ContentView: View {
var body: some View {
NavigationView {
ZStack {
Color.black
NavigationLink(destination: Text("detail")) {
Text("push")
}
}
.navigationBarTitle(Text("My Custom white title")
.foregroundColor(.white)
.font(.title), displayMode: .inline)
}
}
}

The API totally points you that way, but the result is this

Then the desperate googling starts

You find something like this as solution

struct ContentView: View {
init() {
UINavigationBar.appearance().tintColor = UIColor.white
UINavigationBar.appearance().titleTextAttributes = [
.font : UIFont.systemFont(ofSize: 20),
NSAttributedString.Key.foregroundColor : UIColor.white
]
}

but the result is somewhat misleading

Your background is not going behind the bar. so you add

Color.black.edgesIgnoringSafeArea([.all])

and you get this

Close enough huh?

Well there are a couple of things you need to know:

You need to perform this changes on the root of your navigation stack

The order of the factor DOES alter the product

init() {
// this is not the same as manipulating the proxy directly
let appearance = UINavigationBarAppearance()

// this overrides everything you have set up earlier.
appearance.configureWithTransparentBackground()

// this only applies to big titles
appearance.largeTitleTextAttributes = [
.font : UIFont.systemFont(ofSize: 20),
NSAttributedString.Key.foregroundColor : UIColor.white
]
// this only applies to small titles
appearance.titleTextAttributes = [
.font : UIFont.systemFont(ofSize: 20),
NSAttributedString.Key.foregroundColor : UIColor.white
]

//In the following two lines you make sure that you apply the style for good
UINavigationBar.appearance().scrollEdgeAppearance = appearance
UINavigationBar.appearance().standardAppearance = appearance

// This property is not present on the UINavigationBarAppearance
// object for some reason and you have to leave it til the end
UINavigationBar.appearance().tintColor = .white

}

EUREKA!!!!

Ok. Now we need to see what happens when you push this. So lets create another content view (ContentView2) and remove the title to ContenView

tl;dr: this is the final snippet

//
// ContentView.swift
// test
//
// Created by Francisco Gindre on 1/3/20.
// Copyright © 2020 Francisco Gindre. All rights reserved.
//
import SwiftUIstruct ContentView: View {
init() {
// this is not the same as manipulating the proxy directly
let appearance = UINavigationBarAppearance()

// this overrides everything you have set up earlier.
appearance.configureWithTransparentBackground()

// this only applies to big titles
appearance.largeTitleTextAttributes = [
.font : UIFont.systemFont(ofSize: 20),
NSAttributedString.Key.foregroundColor : UIColor.white
]
// this only applies to small titles
appearance.titleTextAttributes = [
.font : UIFont.systemFont(ofSize: 20),
NSAttributedString.Key.foregroundColor : UIColor.white
]

//In the following two lines you make sure that you apply the style for good
UINavigationBar.appearance().scrollEdgeAppearance = appearance
UINavigationBar.appearance().standardAppearance = appearance

// This property is not present on the UINavigationBarAppearance
// object for some reason and you have to leave it til the end
UINavigationBar.appearance().tintColor = .white

}
var body: some View {
NavigationView {
ZStack {
Color.black
.edgesIgnoringSafeArea([.all])
NavigationLink(destination: ContentView2()) {
Text("push")
}
}
.navigationBarTitle("", displayMode: .inline)
.navigationBarBackButtonHidden(true)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct ContentView2: View {

var body: some View {

ZStack {
Color.black
.edgesIgnoringSafeArea([.all])
NavigationLink(destination: ContentView()) {
Text("push")
}
}
.navigationBarTitle("My Custom White title", displayMode: .inline)

}
}

There we go!

Note: It’s January 2020. This is subject to change at any moment. I’m using Xcode 11.2.1 (11B53), because 11.3 renders your app useless when you navigate back and forth.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Pacu
Pacu

Written by Pacu

Software Developer. Rookie surfer

Responses (2)

Write a response