SwiftUI Visual Blur Effect with clear background

262 views Asked by At

Is there any way to have the

.background(.ultraThinMaterial)

in SwiftUI without it having a background color? It seems that it has some light white dim as a background because if it would just blur the background alone, it shouldn't show any color, only if content is scrolled behind.

iOS Simulator: iOS simulator

Desired outcome:

Desired outcome

1

There are 1 answers

3
Benzy Neez On

Here are two possible workarounds.

1. Show the Material conditionally

In your question you said:

it shouldn't show any color, only if content is scrolled behind

So as suggested in a comment, you could make the material background conditional on content actually being scrolled up behind it. If you apply changes withAnimation then it works quite well:

@State private var withMaterialBackground = false

var body: some View {
    ZStack(alignment: .top) {
        ScrollView {
            content
                .padding(.top, 100)
                .background {
                    GeometryReader { proxy in
                        Color.clear
                            .onChange(of: proxy.frame(in: .named("ScrollView")).minY) { oldVal, newVal in
                                if withMaterialBackground != (newVal < 0) {
                                    withAnimation {
                                        withMaterialBackground.toggle()
                                    }
                                }
                            }
                    }
                }
        }
        .coordinateSpace(name: "ScrollView")

        header
            .background {
                if withMaterialBackground {
                    Rectangle()
                        .fill(.ultraThinMaterial)
                        .ignoresSafeArea()
                }
            }
    }
}

2. Use a darker background to compensate for the material effect

The lighter shade of background that is caused by the material effect can be almost eliminated, or at least significantly reduced, if you make the background for the content behind the material even darker. In other words, you can compensate for the lighter shading by setting a darker background, just in that region.

Making the background darker is for the case of when dark mode is in operation. In light mode it works the other way and the content behind the material needs to be made lighter.

Testing on a simulator, I found that these combinations work quite well:

  • In dark mode, a main background of Color(white: 0.15) with Color.black behind the material.

  • In light mode, a main background of Color(white: 0.91) with Color.white behind the material.

Like this:

@Environment(\.colorScheme) private var colorScheme: ColorScheme
let headerHeight: CGFloat = 175

var body: some View {
    ZStack(alignment: .top) {
        Color.clear

        content
            .background(alignment: .top) {
                Color(white: colorScheme == .dark ? 0.0 : 1.0)
                    .ignoresSafeArea()
                    .frame(height: headerHeight, alignment: .top)
            }

        header
            .frame(height: headerHeight, alignment: .top)
            .background(.ultraThinMaterial)
    }
    .background(Color(white: colorScheme == .dark ? 0.15 : 0.91))
}

MaterialScreens