4

I am able to create my custom style of button with a ButtonStyle but I would like to add haptic feedback when the button is touched down and let go of. I know there is a configuration.isPressed variable available in ButtonStyle, but how do I give haptic feedback when it actually changes.

I know how to give haptic feedback but need help knowing when the user touches the button and let's go.

I have tried this extension as a button:

struct TouchGestureViewModifier: ViewModifier {
    let touchBegan: () -> Void
    let touchEnd: (Bool) -> Void
    let abort: (Bool) -> Void

    @State private var hasBegun = false
    @State private var hasEnded = false
    @State private var hasAborted = false

    private func isTooFar(_ translation: CGSize) -> Bool {
        let distance = sqrt(pow(translation.width, 2) + pow(translation.height, 2))
        return distance >= 20.0
    }

    func body(content: Content) -> some View {
        content.gesture(DragGesture(minimumDistance: 0)
                .onChanged { event in
                    guard !self.hasEnded else { return }

                    if self.hasBegun == false {
                        self.hasBegun = true
                        self.touchBegan()
                    } else if self.isTooFar(event.translation) {
                        self.hasAborted = true
                        self.abort(true)
                    }
                }
                .onEnded { event in
                    print("ended")
                    if !self.hasEnded {
                        if self.isTooFar(event.translation) {
                            self.hasAborted = true
                            self.abort(true)
                        } else {
                            self.hasEnded = true
                            self.touchEnd(true)
                        }
                    }
                    self.hasBegun = false
                    self.hasEnded = false
                })
    }
}

// add above so we can use it
extension View {
    func onTouchGesture(touchBegan: @escaping () -> Void = {},
                        touchEnd: @escaping (Bool) -> Void = { _ in },
                        abort: @escaping (Bool) -> Void = { _ in }) -> some View {
        modifier(TouchGestureViewModifier(touchBegan: touchBegan, touchEnd: touchEnd, abort: abort))
    }
}

Then I do this on a view:

.onTouchGesture(

                // what to do when the touch began
                touchBegan: {

                    // tell the view that the button is being pressed
                    self.isPressed = true

                    // play click in sound
                    playSound(sound: "clickin-1", type: "wav")

                    // check if haptic feedback is possible
                    if self.engine != nil {
                        // if it is - give some haptic feedback
                        let pattern = try? CHHapticPattern(events: [self.event], parameters: [])
                        let player = try? self.engine?.makePlayer(with: pattern!)
                        try? self.engine!.start()
                        try? player?.start(atTime: 0)
                    }
                },

                // what to do when the touch ends. sometimes this doesnt work if you hold it too long :(
                touchEnd: { _ in

                    // tell the view that the user lifted their finger
                    self.isPressed = false


                    playSound(sound: "clickout-1", type: "wav")

                    // check if haptic feedback is possible
                    if self.engine != nil {
                        // if it is - give some haptic feedback
                        let pattern = try? CHHapticPattern(events: [self.event], parameters: [])
                        let player = try? self.engine?.makePlayer(with: pattern!)
                        try? self.engine!.start()
                        try? player?.start(atTime: 0)
                    }
                },

                // if the user drags their finger away, abort the action
                abort: { _ in
                    self.isPressed = false
                }
            )

But it gets stuck halfway through sometimes which is not very useable for users. How do I do it on the more reliable Button with ButtonStyle?

Privitec
  • 69
  • 6
  • 1
    Does this answer your question? [How to create haptic feedback for a Button in SwiftUI?](https://stackoverflow.com/questions/56748539/how-to-create-haptic-feedback-for-a-button-in-swiftui) – gotnull Feb 28 '20 at 01:14
  • 1
    No I've already tried all things recommended in that question but I have the issue where if held for a certain amount of time, it gets stuck in a pressed state – Privitec Feb 28 '20 at 01:15

0 Answers0