简体   繁体   中英

How can I add haptics on initial press of a button in Swift, not after the button has been released?

I'm familiar with UIImpactFeedbackGenerator, and that you have to use it in a button's action rather than with.onTapGesture. What I can't figure out is how to get the haptics to trigger on the initial button press instead of on button release.

I initially tried this code and it only works on button release:

struct HapticButton: View {
    var body: some View {
        
        Button {
            
            let impactLight = UIImpactFeedbackGenerator(style: .light)
            impactLight.impactOccurred()
            
        } label: {
            
            Text("Haptic Button")
            
        }
        
    }
}

Then I tried using the simultaneousGesture modifier with DragGesture, and it achieves the haptic effect as soon as the button is pressed — however , it also repeats the haptic every time you drag your finger around the screen after button press. That would be great for a color change that you want to maintain, but not so good for a haptic event. Here's what that code looks like:

struct HapticButton: View {
    var body: some View {
        
        Button {
            
            let impactLight = UIImpactFeedbackGenerator(style: .light)
            impactLight.impactOccurred()
            
        } label: {
            
            Text("Haptic Button")
            
        }
        .simultaneousGesture(
            DragGesture(minimumDistance: 0)
                .onChanged({ _ in
                    
                    let impactLight = UIImpactFeedbackGenerator(style: .light)
                    impactLight.impactOccurred()
                    
                })
        )
    }
}

Any tips on how to only apply the haptic effect one time on initial press? For an example of the behavior I'm seeking, check the iOS keyboard. The haptic occurs as soon as you touch they key, not after you release it, and not repeatedly if you drag.

Extending your proposed solution, we could create a custom view modifier. We can track whether DragGesture started and trigger a haptic effect only for the initial press.

struct HapticOnTouch: ViewModifier {
    @State var isDragging: Bool = false

    func body(content: Content) -> some View {
        content
            .simultaneousGesture(
                DragGesture(minimumDistance: 0)
                    .onChanged { _ in
                        if !isDragging {
                            let impactLight = UIImpactFeedbackGenerator(style: .light)
                            impactLight.impactOccurred()
                        }

                        isDragging = true
                    }
                    .onEnded { _ in
                        isDragging = false
                    }
            )
    }
}

extension View {
    func hapticOnTouch() -> some View {
        modifier(HapticOnTouch())
    }
}

Then you can apply this effect to any view or button like this:

Button {
    // perform action
} label: {
    Text("Haptic Button")
}
.hapticOnTouch()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM