'SwiftUI + Dynamic action closure for Button

I am exploring SwiftUI and I was able to create a subclass of Button.

The subclass contains image & title properties, which makes it a reusable component.

But I am not able to define dynamic action behavior for Button class.

Please refer code below.

Subclass of Round Button:

struct RoundButton: View {
    var image: String
    var title: String
    var body: some View {
        VStack {
            Button(action: {
                print("Button pressed")
            }){
                Image(image)                
                   .renderingMode(Image.TemplateRenderingMode?.init(Image.TemplateRenderingMode.template))
            }
            .frame(width: 40, height: 40)
            .background(Color.blue)
            .cornerRadius(20)
            .accentColor(.white)

            Text(title)
                .font(.footnote)
        }
    }
}

Example usage:

HStack(alignment: .center) {
    Spacer(minLength: 50)
    RoundButton(image: "chat", title: "message")
    Spacer()
    RoundButton(image: "call", title: "call")
    Spacer()
    RoundButton(image: "video", title: "video")
    Spacer()
    RoundButton(image: "mail", title: "mail")
    Spacer(minLength: 50)
}

You will see that action block print a message here.

I would like to know how we can pass a function for a button's action event?



Solution 1:[1]

Button update ...

struct RoundButton: View {
    var image: String
    var title: String
    var action: () -> Void

    var body: some View {
        VStack {
            Button(action: action){
                Image(image)
                   .renderingMode(Image.TemplateRenderingMode?.init(Image.TemplateRenderingMode.template))
            }
    ...

Usage update ...

RoundButton(image: "chat", title: "message") {
    print("Button pressed")
}

backup

Solution 2:[2]

Here is another way that you could just create your custom button without given an action to it! and if you need to give an action you could do it as well, like example in ContentView:

PS: I noticed that you just given the tap or clickable posibility just to Image which makes the tap or click harder! instead you can put all in a VStack and it makes more easier to tap or click for user and also much better animation of being tapped. And also you do not need use cornerRadius for making a Circle, you could use clipShape.

struct RoundButton: View {
    
    var image: String
    var title: String
    var action: (() -> Void)?
    
    init(image: String, title: String, action: (() -> Void)? = nil) {
        self.image = image
        self.title = title
        self.action = action
    }
    
    var body: some View {
        VStack {
            Button(action: {
                action?()
            }){
                VStack {
                    Image(image)
                        .renderingMode(Image.TemplateRenderingMode?.init(Image.TemplateRenderingMode.template))
                        .frame(width: 40, height: 40)
                        .background(Color.blue)
                        .foregroundColor(.white)
                        .clipShape(Circle())

                    Text(title)
                        .font(.footnote)
                        .foregroundColor(.black) 
                }
            }
        }
    }
}

Use case:

struct ContentView: View {
    
    var body: some View {
        
        RoundButton(image: "person", title: "person")

        RoundButton(image: "person", title: "person", action: { print("Button pressed") })

        RoundButton(image: "person", title: "person") {
            print("Button pressed") 
        }

    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2