I have a ButtonStyle which is like that:
import Foundation
import SwiftUI
struct FillButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.padding(Edge.Set.horizontal, 8)
.padding(Edge.Set.vertical, 2)
.foregroundColor(.white)
.background(Colors.MY_COLOR)
}
}
I'm using it like this: Button(action: myAction) {}.buttonStyle(FillButtonStyle())
.
But when I change the size of the global button with like .frame(maxWidth: .infinity)
I have to set the background color again but this time for the full background. not only for the label's one.
How can I change the background of the full button and not only the label ?
I checked and the Configuration
objects doesn't seems to have something else than "label" to config.
This is not possible with this design.
As much as the frame
modifier looks like it is setting the button's max size, it is not. The documentation says:
Positions this view within an invisible frame having the specified size constraints.
frame
simply puts the button inside another (invisible) view. It's more intuitive to think of SomeView().frame(maxWidth: .infinity)
as:
// not real code, but this syntax makes it clear that the frame is a totally different view
Frame(maxWidth: .infinity) {
SomeView()
}
The size of the button itself (let alone its label) is not changed by frame
. The background color you apply after .frame
is applied to the invisible frame, making it visible. This is also why the button is only tappable in its label area, not the whole area of the frame.
I would suggest passing in the max width (and any other parameters of frame
that you want) to the button style.
struct FillButtonStyle: ButtonStyle {
let maxWidth: CGFloat? // you can pass 'nil' to this
func makeBody(configuration: Configuration) -> some View {
configuration.label
.padding(Edge.Set.horizontal, 8)
.padding(Edge.Set.vertical, 2)
.foregroundColor(.white)
.frame(maxWidth: maxWidth)
.background(Colors.MY_COLOR)
}
}
For completeness, there is a way for the subview (the button's label) to affect (pass information to) its parent (the invisible frame) - a preference key. For example, you can do
struct ContentView: View {
var body: some View {
Button("Foo") {
print("Click")
}
.buttonStyle(FillButtonStyle())
// you can wrap these two modifiers into one single ViewModifier
.frame(maxWidth: .infinity)
.backgroundPreferenceValue(BackgroundColorKey.self) {
if let color = $0 {
Rectangle().fill(color)
}
}
}
}
struct BackgroundColorKey: PreferenceKey {
static let defaultValue: AnyShapeStyle? = nil
static func reduce(value: inout Value, nextValue: () -> Value) {
if let next = nextValue() {
value = next
}
}
}
struct FillButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
let color = Colors.MY_COLOR
configuration.label
.padding(Edge.Set.horizontal, 8)
.padding(Edge.Set.vertical, 2)
.foregroundColor(.white)
.background(color)
.preference(key: BackgroundColorKey.self, value: AnyShapeStyle(color))
}
}
With this, you can avoid having to write Colors.MY_COLOR
outside of the ButtonStyle
, but the button can only be tapped in the area of the label.