Changing background colour of Button changes the container background

To change the background of a TextField, I simply apply:

TextField("0.0", value: $p, format: .number)                                     
  .textFieldStyle(PlainTextFieldStyle())
  .background(.red)

That does only work with plain style, but it works.

Trying something similar on a button changes the container view background.

The only solution I've found is to overlap with a Rectangle.

How is it ?

  • A SwiftUI bug ?
  • A current limit of Swift ?
  • A rationale for it ?
  • There a better solution I've not found ?
Answered by DTS Engineer in 810278022

The .background(…) modifier adds a background layer behind a component, which explains why it works fine for a plain textFieldStyle because it has no decorations.

A roundedBorder textFieldStyle applies the system-defined style and rounded border to the textField. To create a textField without decorations, use the PlainTextFieldStyle.

Accepted Answer

The .background(…) modifier adds a background layer behind a component, which explains why it works fine for a plain textFieldStyle because it has no decorations.

A roundedBorder textFieldStyle applies the system-defined style and rounded border to the textField. To create a textField without decorations, use the PlainTextFieldStyle.

Thanks for the explanation.

But so, what is the best solution for a Button ? Having to overlay in a Rectangle is really a convoluted workaround.

Hi @Claude31. The best solution will depend on the look you're ultimately trying to achieve for the button. I hope this will be helpful to you and others in the community!

Built-in button styles

I'd start by trying one of SwiftUI's own button styles, such as the .bordered or .borderedProminent styles. (Note: In macOS, the .bordered style does not adapt its color, but .borderedProminent does. This gives you a look consistent with the expected appearance of buttons on that platform.)

You can customize the button's color with the tint view modifier. By default, the button will be tinted with the application's accent color (if you haven't specified one in your Asset Catalog, it will use either the system accent color or, in macOS, the accent color chosen by the user in System Settings).

Button("My Button", action: performAction)
    .buttonStyle(.borderedProminent)
    .tint(.red)

You can further customize the shape of the button's border with the buttonBorderShape view modifier. Some of the available options are .capsule, .circle, a .roundedRectangle with default corner radius defined by the system, or a .roundedRectangle(radius:) to specify a custom corner radius.

Destructive buttons

I'll take this time to also note, based simply on the example color being .red, that if this button represents a destructive action, such as deleting something, than you may want to consider simply specifying a button role.

Button("Delete", role: .destructive, action: delete)

This will make the button have a destructive appearance in the contexts and platforms where destructive buttons are intended to have a unique appearance. For example, in alerts, a destructive button will have red text.

(It's expected that destructive button's don't have a different appearance in all contexts. For example, in macOS, they show red text only in alerts and within a sheet's .toolbar modifier.)

In macOS, destructive buttons also don't immediately perform their action if they are clicked while the window inactive (or in the background), to prevent the button from being accidentally clicked while bringing a window to the front.

Custom button style

If you need to further customize the appearance of the button, you can create your own custom ButtonStyle, which you apply with the buttonStyle view modifier and reuse throughout your app. This is the recommended, as you can make the style adapt to pressed state and environment values such as appearsActive and isEnabled. You can set the buttonStyle on a container to apply it to all Buttons within that container.

Below is an example of a custom button style that applies a background using the tint color and adapts to the disabled modifier (via the isEnabled environment value) and current pressed state.

struct MyCustomButtonStyle: ButtonStyle {
    @Environment(\.isEnabled) private var isEnabled

    func makeBody(configuration: ButtonStyle.Configuration) {
        configuration.label
            .background(isEnabled: AnyShapeStyle(.tint) ? AnyShapeStyle(.gray))
            .opacity(configuration.isPressed ? 0.9 : 1.0)
    }
}

Button("Answer Call", action: answerCall)
    .tint(.green)
Changing background colour of Button changes the container background
 
 
Q