How can I extend system default ButtonStyle class

Viewed 286

I am looking to extend the system buttonstyle for example BorderedButtonStyle class and add my own custom configuration

I have tried

extension CrazyButtonStyle: BorderedButtonStyle{
    public func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .foregroundColor(.red)
    }
}

but im getting an error Inheritance from non-protocol type 'BorderedButtonStyle'

2 Answers

Inheritance is possible only for classes, but majority of SwiftUI types are structs (ie. values), so we need to use composition instead, combining needed features with sequence of modifiers, like

Updated: Xcode 13.4 / iOS 15.5

demo

// usage
Button("Demo") { }
    .buttonStyle(.crazy)


// calculable property extension
extension PrimitiveButtonStyle where Self == CrazyButtonStyle {
    static var crazy: CrazyButtonStyle { CrazyButtonStyle() }
}

Complete code & demo is here

Original variant:

@available(iOS 15.0, *)
extension Button {
    func crazyButton() -> some View {
        self.buttonStyle(.bordered)
            .foregroundColor(.red)
    }
}

and usage:

var body: some View {
    Button("Crazy") {
        // ... action here
    }
    .crazyButton()
}

works but I would love to call it using the .buttonStyle(.crazy) or sth like that. Would that be possible? – Nic Wanavit

Asperi's answer is much cleaner, nonetheless

struct ContentView: View
{
    var body: some View
    {
        Button("Tap Me!")
        {
            print("button pressed!")
        }
        .buttonStyle(myButtonStyle())
    }
}

struct myButtonStyle: ButtonStyle
{
    // required method to conform to ButtonStyle protocol
    func makeBody(configuration: Self.Configuration) -> some View
    {
        myButtonStyleView(configuration: configuration)
    }
    
    struct myButtonStyleView: View
    {
        let configuration: myButtonStyle.Configuration
        
        var body: some View
        {
            configuration
                .label // must be listed first
                .padding()
                .background(Color.red)
                .foregroundColor(.white)
        }
    }
}

Related