Search code examples
iosswiftswiftui

SwiftUI adjust background externally, similar to the foregroundStyle


Given the following code:

import SwiftUI

struct HomeScreenCounterView: View {
    private let value: Int
    private let title: String
    private let emptyDescription: String


    init(value: Int, title: String, emptyDescription: String) {
        self.value = value
        self.title = title
        self.emptyDescription = emptyDescription
    }

    var body: some View {
        VStack {
            Text("Messages")
                .font(.fontTextLBold)
            Text("No new messages")
                .font(.fontTextSRegular)
        }
        .padding()
        .clipShape(RoundedRectangle(cornerRadius: 16))
    }
}

#Preview {
    HomeScreenCounterView(value: 0,
                          title: "Messages",
                          emptyDescription: "No new messages")
    .foregroundStyle(Color.red)
    .background(Color.blue)
}

I get the following result:

enter image description here

Notice, how the text color in both labels has been changed to red.

While the background is correctly displayed as blue, it is obviously not clipped, as the clipping operator has been applied earlier, not later.

What would be the best/the most elegant way to have a background color modifier, similarly to how the other SwiftUI components work?

One of the solutions I am thinking of, and it's obviously a working solution, is to add an initializer parameter:

init(value: Int, title: String, emptyDescription: String, backgroundColor: Color) {

And then using that value to set the background color just after padding:

        .padding()
        .background(backgroundColor) 
        .clipShape(RoundedRectangle(cornerRadius: 16))

But is there a better way?

This is how I'd like the end result to look like: enter image description here


Solution

  • You could consider using a semantic shape style for filling the background inside the view. The obvious choice would be .background, but you could also use one of the other semantic or hierarachical styles such as .secondary. The actual color can then be set from a parent view:

    // HomeScreenCounterView
    
    VStack {
        // ...
    }
    .padding()
    .background(.background, in: .rect(cornerRadius: 16)) // or, say, .secondary
    
    // Parent
    
    HomeScreenCounterView(
        // ...
    )
    .foregroundStyle(.red)
    // .foregroundStyle(.red, .blue) // for setting .blue as secondary
    .backgroundStyle(.blue)
    

    Screenshot