5

I am trying to implement a detail view which is linked from a navigation view. In this detail view, there is a default nav bar on top with a back button. But the bar only show some color when I scroll up. I have no idea why.

No Scroll: Transparent Nav Bar

Scroll: Have Background Color

Initially, the nav bar doesn't have background either when it's scrolling or not. So I created an init() method for setting up the style.

 init(fruit: Fruit) {
    self.fruit = fruit
    if #available(iOS 15.0, *) {
        let navigationBarAppearance = UINavigationBarAppearance()
        navigationBarAppearance.configureWithDefaultBackground()
        
        UINavigationBar.appearance().standardAppearance = navigationBarAppearance
        UINavigationBar.appearance().compactAppearance = navigationBarAppearance
        UINavigationBar.appearance().scrollEdgeAppearance = navigationBarAppearance
    }
  }

The Body View is a Navigation View on the outside and scroll view inside. ** For anyone wonder why I set the navbar to hidden in the VStack, is because if I don't hide it, there would be some huge space above the image. (I have no idea why) Huge Space, no idea what caused it

** Updated Code ** I updated my code which use the Opaque background. But it seems like none of those config are visible.

init(fruit: Fruit) {
    self.fruit = fruit
    let navBarAppearance = UINavigationBarAppearance()
    navBarAppearance.configureWithOpaqueBackground()
    UINavigationBar.appearance().scrollEdgeAppearance = navBarAppearance
    UINavigationBar.appearance().standardAppearance = navBarAppearance
}

var body: some View {
    NavigationView {
        ScrollView(.vertical, showsIndicators: false) {
            VStack(alignment: .center, spacing: 20) {
                // HEADER
                FruitHeaderView(fruit: fruit)
                
                VStack(alignment: .leading, spacing: 20) {
                    // TITLE
                    Text(fruit.title)
                        .font(.largeTitle)
                        .fontWeight(.heavy)
                        .foregroundColor(fruit.gradientColors[1])
                    
                    
                } //: VSTACK
                .padding(.horizontal, 20)
                .frame(maxWidth: 640, alignment: .center)
            } //: VSTACK
            .navigationBarHidden(true)
        } //: SCROLL
        .edgesIgnoringSafeArea(.top)
    } //: NAVIGATION
    .navigationBarTitle(fruit.title, displayMode: .inline)
    .navigationViewStyle(StackNavigationViewStyle())
}

Config the nav bar during init, but still don't see the background until I scroll

*** Solution *** It turns out I have to put the configurations code of the nav bar in the parent view. During init(). Can anyone explain why this on the parent view? Or if I want different style in parent and child what should I do?

    init() {
        let navBarAppearance = UINavigationBarAppearance()
        navBarAppearance.configureWithOpaqueBackground()
        UINavigationBar.appearance().scrollEdgeAppearance = navBarAppearance
         UINavigationBar.appearance().standardAppearance = navBarAppearance
    }

var body: some View {
    NavigationView {
        List {
            ForEach(fruits.shuffled()) { item in
                NavigationLink {
                    FruitDetailView(fruit: item)
                } label: {
                    FruitRowView(fruit: item)
                        .padding(.vertical, 4)
                }
            }
        }
        .listStyle(.plain)
        .navigationTitle("Fruits")
    } //: NAVIGATION

}

Parent View

JonathanLiu
  • 132
  • 1
  • 7
  • Check this great tutorial https://www.bigmountainstudio.com/community/public/posts/80041-how-do-i-customize-the-navigationview-in-swiftui – Quang Hà Apr 14 '22 at 04:16
  • use this ```navigationBarAppearance.configureWithTransparentBackground()``` instead .configureWithDefaultBackground() – Raja Kishan Apr 14 '22 at 04:23
  • Does this answer your question https://stackoverflow.com/a/65523676/12299030? – Asperi Apr 14 '22 at 05:46
  • @Asperi No, I don't want a transaprent nav bar. I would like the nav bar to have a non transparent background same as the one when I am scrolling. – JonathanLiu Apr 14 '22 at 06:06

5 Answers5

3

This worked for me -

func changeNavBar(navigationBar: UINavigationBar, to color: UIColor) {
            let appearance = UINavigationBarAppearance()
            appearance.configureWithOpaqueBackground()
            appearance.backgroundColor = color
            navigationBar.standardAppearance = appearance;
            navigationBar.scrollEdgeAppearance = navigationBar.standardAppearance
}
Prashant Gaikwad
  • 3,493
  • 1
  • 24
  • 26
  • This one line worked for me: navigationBar.scrollEdgeAppearance = navigationBar.standardAppearance In my instance I was using this code in a custom NavigationController Class... you will need a reference to you navigationController to access .navigationBar on it. – Krivvenz Jun 23 '23 at 15:37
2

Use my extension:

extension UIViewController {
func configureNavigationBar(largeTitleColor: UIColor, backgoundColor: UIColor, tintColor: UIColor, title: String, preferredLargeTitle: Bool) {
if #available(iOS 13.0, *) {
let navBarAppearance = UINavigationBarAppearance()
navBarAppearance.configureWithOpaqueBackground()
navBarAppearance.largeTitleTextAttributes = [.foregroundColor: largeTitleColor]
navBarAppearance.titleTextAttributes = [.foregroundColor: largeTitleColor]
navBarAppearance.backgroundColor = backgoundColor
navigationController?.navigationBar.standardAppearance = navBarAppearance
navigationController?.navigationBar.compactAppearance = navBarAppearance
navigationController?.navigationBar.scrollEdgeAppearance = navBarAppearance

navigationController?.navigationBar.prefersLargeTitles = preferredLargeTitle
navigationController?.navigationBar.isTranslucent = false
navigationController?.navigationBar.tintColor = tintColor
navigationItem.title = title

} else {
// Fallback on earlier versions
navigationController?.navigationBar.barTintColor = backgoundColor
navigationController?.navigationBar.tintColor = tintColor
navigationController?.navigationBar.isTranslucent = false
navigationItem.title = title
  }
 }
}

call it in viewDidAppear or viewDidLoad , in your case set background to clear... How to use:

configureNavigationBar(largeTitleColor: .yourColor, backgoundColor: .yourColor, tintColor: .yourColor, title: "YourTitle", preferredLargeTitle: false)
Fabio
  • 5,432
  • 4
  • 22
  • 24
1
        navigationController?.navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
        navigationController?.navigationBar.shadowImage = UIImage()

use this code in viewWillAppear

Sagar Ali
  • 11
  • 1
1

You need opaque appearance, because default one means system-selected-by-design which is changed from version to version.

demo

    let navBarAppearance = UINavigationBarAppearance()
    navBarAppearance.configureWithOpaqueBackground()
    UINavigationBar.appearance().scrollEdgeAppearance = navBarAppearance

Tested with Xcode 13.3 / iOS 15.4 (in ContentView.init)

Note: onAppear is too late to inject above code, the appearance settings are applied on objects created after it, and onAppear is called after NavigationView created. So use it either in init, or anywhere else, but before view created.

Asperi
  • 228,894
  • 20
  • 464
  • 690
  • This seems to be UIKit not SwiftUI – Ptit Xav Apr 14 '22 at 06:23
  • @PtitXav, it can be used in `init`, see example here https://stackoverflow.com/a/65523676/12299030. – Asperi Apr 14 '22 at 06:30
  • @Asperi I updated the code you mentioned and I put it during onAppear(). But it still doesn't work. You can refer to my updated code and image above. – JonathanLiu Apr 14 '22 at 06:32
  • @JonathanLiu, onAppear is too late, appearance settings are applied on objects created *after* it, and onAppear is called after NavigationView created. So use it either in init, or anywhere else, but before view created. – Asperi Apr 14 '22 at 06:34
  • @Asperi, modify it to init(). But it's still like this. I updated the image and code above. – JonathanLiu Apr 14 '22 at 06:44
  • But you hide navigation bar with `.navigationBarHidden(true)` :) , so what do you expect to see? - Just remove that modifier. – Asperi Apr 14 '22 at 06:53
  • @Asperi, I fixed the issue. I place the code you mentioned in the parent navigation view not in the detail view. But I have no idea why. And I don't know if I want different style in the child view what should I do. – JonathanLiu Apr 14 '22 at 06:53
  • @Asperi if I don't set the navigation bar to hidden in the vstack. There will be a huge space under the back button and title, and above the image. – JonathanLiu Apr 14 '22 at 06:55
  • You must remove second NavigationView (in child), only one in root is needed for navigation stack. – Asperi Apr 14 '22 at 07:01
  • @Asperi, I used set bar title as inline fixed the empty space problem. – JonathanLiu Apr 14 '22 at 07:11
  • @Asperi : i did not know we could use some UIKit on views like you example. – Ptit Xav Apr 14 '22 at 07:56
0

In Xcode 14.3 the solutions above did not work but the following approach proved to be successful and resolved the issue for me.

struct MyApp: App { /../ }
extension UINavigationController {
    override open func viewWillLayoutSubviews() {
        let transparentAppearance = UINavigationBarAppearance()
        transparentAppearance.configureWithTransparentBackground()
        transparentAppearance.backgroundColor = .clear
        transparentAppearance.shadowColor = .clear
        navigationBar.standardAppearance = transparentAppearance
        navigationBar.compactAppearance = transparentAppearance
        navigationBar.scrollEdgeAppearance = transparentAppearance
    }
}
LonniE
  • 99
  • 2
  • 9