SwiftUI - Combine buttonStyle functionality with different functions for each button

912 Views Asked by At

I am new to SwiftUI and iOS. What I am trying to achieve is to add a buttonStyle to all the buttons inside my app, while keeping the function that each button calls when pressed. This is my code so far for the buttonStyle. It works as expected (changes color, backgroundColor and opacity when tapped):

struct circularButton: ButtonStyle {
    @State private var isPressed: Bool = false
    
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .padding()
            .background(isPressed ? Color.white : Color.black)
            .opacity(isPressed ? 0.6 : 1.0)
            .foregroundColor(isPressed ? Color.black : Color.white)
            .clipShape(Circle())
            .pressEvents {
                withAnimation(.easeIn(duration: 0.25)) {
                    isPressed = true
                }
            } onRelease: {
                withAnimation {
                    isPressed = false
                }
            }
    }
}

struct ButtonPress: ViewModifier {
    var onPress: () -> Void
    var onRelease: () -> Void
    
    func body(content: Content) -> some View{
        content
            .simultaneousGesture(
                DragGesture(minimumDistance: 0)
                    .onChanged({_ in
                        onPress()
                    })
                    .onEnded({_ in
                        onRelease()
                    }))
    }
}

extension View {
    func pressEvents(onPress: @escaping (() -> Void), onRelease: @escaping (() -> Void)) -> some View {
        modifier(ButtonPress(onPress: { onPress() }, onRelease: { onRelease() }))
    }
}

However, the function attached to each button is not being called on click.

//Execute command is not being called when clicked
Button(action: {executeCommand(0x04)}) {
    Image(systemName: "wifi.exclamationmark")
}
.buttonStyle(circularButton())

I understand it has something to do with the isPressed State, because when I comment out the .pressEvents lines, the function DOES get called. I am unsure of how to deal with this situation. Any help is appreciated.

1

There are 1 best solutions below

0
On

For a button style, configuration already keeps track of when a button is pressed via the configuration.isPressed boolean. Using that instead of reading gestures would fix the gesture conflict you're experiencing right now.

struct circularButton: ButtonStyle {
    
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .padding()
            .background(configuration.isPressed ? Color.white : Color.black)
            .opacity(configuration.isPressed ? 0.6 : 1.0)
            .foregroundColor(configuration.isPressed ? Color.black : Color.white)
            .clipShape(Circle())
            .animation(.easeIn(duration: 0.25), value: configuration.isPressed)
    }
}

If you want to learn more about custom button styles, this article on Hacking with Swift is a very good resource.