onmyway133 / blog

🍁 What you don't know is what you haven't learned
https://onmyway133.com/
MIT License
669 stars 33 forks source link

How to disable with ButtonStyle in SwiftUI #853

Open onmyway133 opened 2 years ago

onmyway133 commented 2 years ago

With ButtonStyle, the disabled modifier does not seem to work, we need to use allowsHitTesting.

import SwiftUI

struct ActionButtonStyle: ButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        HStack {
            Text("Button")
        }
        .padding()
        .disabled(true) // does not work
        .allowsHitTesting(false)
    }
}

We need to call disabled outside, after buttonStyle. In case we have onTapGesture on the entire view, touching on that disabled button will also trigger our whole view action, which is not what we want.

VStack {
    Button(action: onSave) {
        Text("Save")
    }
    .buttonStyle(ActionButtonStyle())
    .disabled(true)
}
.onTapGesture {
    window.endEditing(true)
}

Another way is to overlay with a dummy view with a dummy tap gesture to obscure the action

extension View {
    func disableInteraction(_ shouldDisable: Bool) -> some View {
        disabled(shouldDisable)
            .overlay(
                Group {
                    if shouldDisable {
                        DummyView()
                            .onTapGesture {}
                    }
                }
            )
    }
}

private struct DummyView: UIViewRepresentable {
    func makeUIView(context: Context) -> UIView {
        let view = UIView()
        return view
    }

    func updateUIView(_ uiView: UIView, context: Context) {}
}

So that we have

Button(action: onSave) {
    Text("Save")
}
.buttonStyle(ActionButtonStyle())
.disableInteraction(true)