I have a button in SwiftUI and I would like to be able to have a different action for \"tap button\" (normal click/tap) and \"long press\".
Is that possible in Swift
Thought I'd post back on this, in case anyone else is struggling. Strange that Apple's default behaviour works on most controls but not buttons. In my case I wanted to keep button effects while supporting long press.
An approach that works without too much complexity is to ignore the default button action and create a simultaneous gesture that handles both normal and long clicks.
In your view you can apply a custom long press modifier like this:
var body: some View {
// Apply the modifier
Button(action: self.onReloadDefaultAction) {
Text("Reload")
}
.modifier(LongPressModifier(
isDisabled: self.sessionButtonsDisabled,
completionHandler: self.onReloadPressed))
}
// Ignore the default click
private func onReloadDefaultAction() {
}
// Handle the simultaneous gesture
private func onReloadPressed(isLongPress: Bool) {
// Do the work here
}
My long press modifier implementation looked like this and uses the drag gesture that I found from another post. Not very intuitive but it works reliably, though of course I would prefer not to have to code this plumbing myself.
struct LongPressModifier: ViewModifier {
// Mutable state
@State private var startTime: Date?
// Properties
private let isDisabled: Bool
private let longPressSeconds: Double
private let completionHandler: (Bool) -> Void
// Initialise long press behaviour to 2 seconds
init(isDisabled: Bool, completionHandler: @escaping (Bool) -> Void) {
self.isDisabled = isDisabled
self.longPressSeconds = 2.0
self.completionHandler = completionHandler
}
// Capture the start and end times
func body(content: Content) -> some View {
content.simultaneousGesture(DragGesture(minimumDistance: 0)
.onChanged { _ in
if self.isDisabled {
return
}
// Record the start time at the time we are clicked
if self.startTime == nil {
self.startTime = Date()
}
}
.onEnded { _ in
if self.isDisabled {
return
}
// Measure the time elapsed and reset
let endTime = Date()
let interval = self.startTime!.distance(to: endTime)
self.startTime = nil
// Return a boolean indicating whether a normal or long press
let isLongPress = !interval.isLess(than: self.longPressSeconds)
self.completionHandler(isLongPress)
})
}
}