Home > Software engineering >  Blur effect on SwiftUI NavigationBar stops working if I add content to the bottom of my view
Blur effect on SwiftUI NavigationBar stops working if I add content to the bottom of my view

Time:12-05

I have a very simple View with NavigationBar and a List in a VStack. If I scroll up on the list, the list contents will appear behind the NavigationBar with a nice blur effect by default. This behavior is correct.

The correct blur effect

I would also like to have some fixed text below my list that doesn't scroll. However, if I add the following single line of code Text("bottom") below the list, the blur effect on the NavigationBar doesn't work anymore.

The blur effect is gone

struct ContentView: View {
    
    var body: some View {
        NavigationView {
            VStack {
                List {
                    Text("Hello, world!")
                        .foregroundColor(.red)
                        .font(.system(size: 70))
                }
                .listStyle(.plain)
                .border(.red)

                Text("bottom")  // remove this and the blur works
            }
            .background(.black)
            .navigationBarTitle("test", displayMode: .inline)
        }
    }
}

Why is this happening, and how can I get content below my list while retaining the blur effect?

CodePudding user response:

This is mostly an educated guess, based on some interesting facts revealed in this article. Basically, UIKit has some internal logic for when it detects that the first view in a hierarchy is a UIScrollView, and it adapts the navigation bar behavior based on that. And either SwiftUI is still built on top of UIKit, or it has inherited that logic.

With the label in place, the list is not the first element in the UI tree, for some reason SwiftUI puts the label before the list, even if in the code is the other way around, and this causes the navigation bar logic to break.

If we capture the UI hierarchy of the app, this is what we see if the "bottom" label is part of the app:

, however, if we comment off the line with the label, we get this:

The above images confirm the theory that iOS gives different treatments when it detects a scroll view is the main view.

CodePudding user response:

The blur effect on the NavigationBar is not working when you add a fixed piece of text below the list because the blur effect is applied to the entire view hierarchy, including the fixed piece of text. When you add the fixed text, it is being included in the view hierarchy and therefore the blur effect is not being applied correctly.

One way to fix this is to use a ZStack to place the fixed text on top of the list, which will allow the blur effect to be applied correctly to the list and the NavigationBar. Here is an example of how you can do this:

struct ContentView: View {
    var body: some View {
        NavigationView {
            ZStack {
                VStack {
                    List {
                        Text("Hello, world!")
                            .foregroundColor(.red)
                            .font(.system(size: 70))
                    }
                    .listStyle(.plain)
                    .border(.red)
                }
                .background(.black)
                .navigationBarTitle("test", displayMode: .inline)

                Text("bottom")
            }
        }
    }
}

In this example, the list and the NavigationBar are placed in a VStack, and the fixed text is placed in a ZStack on top of the VStack. This allows the blur effect to be applied to the list and the NavigationBar, while the fixed text remains visible on top of them.

  • Related