5

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?

Falco
  • 63
  • 5
  • You are adding black as the background. How did you get blur effect the first time. Could you check your code – user1046037 Dec 05 '22 at 00:27
  • Go ahead and try it yourself. Paste the code above into XCode and remove the `Text("bottom")` line. You will see that indeed the background is black, but when you scroll the list up, as the red text disappears behind the navigation bar, there is a blurred effect in the navigation bar which is common in many Apple apps. If you add the text line back in, the navigation bar becomes completely transparent when you scroll the list up. – Falco Dec 05 '22 at 03:36
  • is your question why this is happening or is your question how to achieve a bottom overlay? – user1046037 Dec 05 '22 at 17:42

2 Answers2

2

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.

Cristik
  • 30,989
  • 25
  • 91
  • 127
1

I had the same issue and found two possible solutions for this. Either use a section footer or use an overlay. Each will give you a different placement of the text: The footer places the text right below the section, the overlay at the very bottom of the screen hovering above the list.

Section footer

struct ContentView: View {
    var body: some View {
        NavigationView {
            List {
                Section {
                    Text("Hello, world!")
                        .foregroundColor(.red)
                        .font(.system(size: 70))
                } footer: {
                    Text("Bottom Text")
                }
            }
            .listStyle(.plain)
            .navigationBarTitle("Test", displayMode: .inline)
        }
    }
}

Overlay

struct ContentView: View {
    var body: some View {
        NavigationView {
            List {
                Text("Hello, world!")
                    .foregroundColor(.red)
                    .font(.system(size: 70))
            }
            .listStyle(.plain)
            .navigationBarTitle("Test", displayMode: .inline)
            .overlay(alignment: .bottom) {
                Text("Bottom Text")
            }
        }
    }
}
viedev
  • 869
  • 1
  • 6
  • 16