Recommand · October 14, 2021 0

In SwiftUI, how to fill Path with text color on top of a material?

I’m drawing icons on a toolbar with a material background. The Text and symbol Images are white, but if I draw my own Path, it’s gray.

enter image description here

struct ContentView: View {
    var body: some View {
        HStack {
            Text("Hi")
            Image(systemName: "square.and.arrow.up.fill")
            Path { p in
                p.addRect(CGRect(origin: .zero, size: .init(width: 20, height: 30)))
            }.fill()
            .frame(width: 20, height: 30)
        }
        .padding()
        .background(.regularMaterial)
    }
}

I get the same result with .fill(), .fill(.foreground), or .fill(.primary).

Why is it gray? How do I get it to match the white text color?

There is no issue with code, your usage or expecting is not correct! Text and Image in that code has default Color.primary with zero code! So this is you, that messing with .fill() you can delete that one!

struct ContentView: View {
    var body: some View {
        HStack {
            Text("Hi")
            Image(systemName: "square.and.arrow.up.fill")
            Path { p in
                p.addRect(CGRect(origin: .zero, size: .init(width: 20, height: 30)))
            }
            .fill(Color.primary) // You can delete this line of code as well! No issue!
            .frame(width: 20, height: 30)
        }
        .padding()
        .background(Color.secondary.cornerRadius(5))

    }
}

enter image description here

enter image description here

I find it weird that .white or .black work, but .primary doesn’t.

Upon discovering the Material documentation, I found this interesting snippet:

When you add a material, foreground elements exhibit vibrancy, a context-specific blend of the foreground and background colors that improves contrast. However using foregroundStyle(_:) to set a custom foreground style — excluding the hierarchical styles, like secondary — disables vibrancy.

Seems like you have to force a different color (see previous edit which I used the environment color scheme), since hierarchical styles such as .primary won’t work by design.

Luckily there is a way around this – you can use colorMultiply to fix this problem. If you set the rectangle to be .white, then the color multiply will make it the .primary color.

Example:

struct ContentView: View {
    var body: some View {
        HStack {
            Text("Hi")

            Image(systemName: "square.and.arrow.up.fill")

            Path { p in
                p.addRect(CGRect(origin: .zero, size: .init(width: 20, height: 30)))
            }
            .foregroundColor(.white)
            .colorMultiply(.primary)
            .frame(width: 20, height: 30)
        }
        .padding()
        .background(.regularMaterial)
    }
}