Tobonaut
Tobonaut

Reputation: 2485

How to implement a reusable button with style, text and icon using SwiftUI?

I know how a custom ButtonStyle works. But I do want to have a fully reusable custom button.

Aka a button with Text and and Icon that has some styles applied.

I know how to achieve this using a ButtonStyle with a text property, but I think this is completely a misuse of button styles.

But I do not want to copy a Button with a large HStack as content all over my app.

What do you recommend in such use cases? It seems that no one has subclassed a Button in SwiftUI on the internet.

Upvotes: 12

Views: 11302

Answers (5)

Johannes
Johannes

Reputation: 1440

You can of course fully customize the button with ButtonStyle. The makeBody just has to return a view, and configuration.label is just a label. No magic involved. Just wrap the label anyway you see fit. You can e.g. do like this to add an icon to the button:

struct CustomButtonStyle: ButtonStyle {
    func makeBody(configuration: Self.Configuration) -> some View {
        return
            HStack {
                Image("imageName")
                configuration.label
            }
            .padding()
            .background(Color.green)
            .foregroundColor(Color.white)
            .opacity(configuration.isPressed ? 0.7 : 1)
            .scaleEffect(configuration.isPressed ? 0.8 : 1)
            .animation(.easeInOut(duration: 0.2))
    }
}

Upvotes: 1

manubrio
manubrio

Reputation: 469

struct DarkButton: View {

   var buttonText: String = ""

   var body: some View {
       ZStack {
           Text(buttonText)
           .customDarkButtonStyle()
       }
   }
}

// MARK: - View Modifier to style our button -
/// If you need more customization add @State var

struct DarkButtonStyle: ViewModifier {

   /// Background button color
   var backgroundColor = Color.red

   /// Foreground button color
   var foregroundColor = Color.white

   /// Button width
   var width = 148

   /// Button height
   var height = 18

   /// Button corner radius
   var cornerRadius = 8

   func body(content: Content) -> some View {

       content
           .foregroundColor(foregroundColor)
           .font(/* Your font here */)
           .frame(width: width, height: height)
           .background(backgroundColor)
           .cornerRadius(cornerRadius)
   }
}

// MARK: - Cleaner way to call our custom ViewModifier -

extension View {
    func customDarkButtonStyle() -> some View {
        ModifiedContent(
            content: self,
            modifier: DarkButtonStyle()
        )
    }
}

Upvotes: 4

aheze
aheze

Reputation: 30496

It seems that no one has subclassed a Button in SwiftUI on the internet.

True. But that's because it's not possible - Button is a struct, so it can't be subclassed. Instead, what you can do is make a custom View.

struct CustomButton: View {
    var text: String
    var icon: Image
    var clicked: (() -> Void) /// use closure for callback
    
    var body: some View {
        Button(action: clicked) { /// call the closure here
            HStack {
                Text(text) /// your text
                
                icon /// your icon image
            }
            .foregroundColor(Color.green)
            .padding()
            .background(Color(.secondarySystemBackground))
            .cornerRadius(16)
        }
    }
}

struct ContentView: View {
    var body: some View {
        CustomButton(
            text: "Custom Button",
            icon: Image(systemName: "plus")
        ) {
            print("Clicked!")
        }
    }
}

Result:

"Custom Button +" button

Upvotes: 36

minchaej
minchaej

Reputation: 1844

You can actually create Styles in Swift UI, for example ButtonStyle

Here is the sample code, you could copy-paste and try it

import SwiftUI

struct CustomButtonStyle: ButtonStyle {
    func makeBody(configuration: Self.Configuration) -> some View {
        return configuration.label
            .padding()
            .background(Color.green)
            .foregroundColor(Color.white)
            .opacity(configuration.isPressed ? 0.7 : 1)
            .scaleEffect(configuration.isPressed ? 0.8 : 1)
            .animation(.easeInOut(duration: 0.2))
    }
}


//  USAGE:
Button(action: { }) {
  Text("This is a custom button")
}
.buttonStyle(CustomButtonStyle())

Upvotes: 4

Taeeun Kim
Taeeun Kim

Reputation: 1256

You can use a ViewModifier.

The code below is a ViewModifier for a custom back button.

struct OverlayBackButton: ViewModifier {
    @Environment(\.presentationMode) var presentation
    
    func body(content: Content) -> some View {
        content
            .overlay(
                Button(action: {
                    presentation.wrappedValue.dismiss()
                }) {
                Image(systemName: "chevron.backward") // set image here
                    .aspectRatio(contentMode: .fit)
                    .foregroundColor(.red)
                Text("Back")
                    .foregroundColor(.red)
                }
                .padding()
                ,alignment: .topLeading
            )
    }
}

extension View {
    func overlayBackButton() -> some View {
        self.modifier(OverlayBackButton())
    }
}

Then you can use the button like this

ZStack{

}
.overlayBackButton()

Upvotes: 0

Related Questions