Search code examples
swiftui

SwiftUI Toggle with Material and ClipShape loses toggle background


I have a Toggle view inside a 'half rounded' view with Material background. To achieve the 'half rounded', I use .clipShape with .rect and selected radius.
It seems that using .clipShape with .rect (with specific radius params) is causing the Toggle to lose the background color.
No background toggle image

Code:

ZStack {
    Color.blue
    Toggle("On", isOn: .constant(false))
    .padding()
        .frame(width: 150, height: 100)
        .background(.thinMaterial)
        .clipShape(
            .rect(
                topLeadingRadius: 20,
                bottomLeadingRadius: 0,
                bottomTrailingRadius: 0,
                topTrailingRadius: 20
            )
        )
}

If I use .clipShape with RoundedRectangle(cornerRadius:), I get the background again, but I need bottom to not be rounded.


Edit

I later needed to also extent the view, allowing the bottom part to extend, where the Material alone should go under the safe area.
Background on bottom area

In short, I needed to embed the 'material' background inside another background modifier, where I apply the 'material' on a 'clear color' view.
Here's the code:

ZStack {
    Color.blue
        .ignoresSafeArea()
    VStack {
        Spacer()
        Toggle("On", isOn: .constant(false))
            .padding()
            .frame(height: 100)
            .background( // 'Regular background container
                Color.clear // Apply the Material background with 'ignoresSafeArea'
                    .background(.thinMaterial,
                                in: .rect(
                        topLeadingRadius: 20,
                        bottomLeadingRadius: 0,
                        bottomTrailingRadius: 0,
                        topTrailingRadius: 20
                    ))
                    .ignoresSafeArea()
            )
    }
}

Solution

  • This seems to be some weird interaction between how SwiftUI clips things and how UIKit renders the toggle background. The background of the toggle is visible if you instead pass the shape you want to the in: parameter of background.

    .background(.thinMaterial, in: .rect(
        topLeadingRadius: 20,
        bottomLeadingRadius: 0,
        bottomTrailingRadius: 0,
        topTrailingRadius: 20
    ))