In my original answer, I answered the question of how to have a button recognize both a tap and a long press. In the clarified question, it appears you want this button to continuously "fire" as long as the user holds their finger down. If that's the case, only one gesture recognizer is needed.
For example, in Interface Builder, drag a long press gesture recognizer from the object library onto the button and then set the "Min Duration" to zero:
Then you can control-drag from the long press gesture recognizer to your code in the assistant editor and add an @IBAction
to handle the long press:
weak var timer: Timer?
@IBAction func handleLongPress(_ gesture: UILongPressGestureRecognizer) {
if gesture.state == .began {
timer?.invalidate()
timer = Timer.scheduledTimer(withTimeInterval: 0.3, repeats: true) { [weak self] timer in
guard let self = self else {
timer.invalidate()
return
}
self.handleTimer(timer)
}
} else if gesture.state == .ended || gesture.state == .cancelled {
timer?.invalidate()
}
}
func handleTimer(_ timer: Timer) {
print("bang")
}
Or, if you also want to stop firing when the user drags their finger off of the button, add a check for the location of the gesture:
@IBAction func handleLongPress(_ gesture: UILongPressGestureRecognizer) {
if gesture.state == .began {
timer?.invalidate()
timer = Timer.scheduledTimer(withTimeInterval: 0.3, repeats: true) { [weak self] timer in
guard let self = self else {
timer.invalidate()
return
}
self.handleTimer(timer)
}
} else if gesture.state == .ended || gesture.state == .cancelled || (gesture.state == .changed && !gesture.view!.bounds.contains(gesture.location(in: gesture.view))) {
timer?.invalidate()
}
}
My original answer, answering the different question of how to recognize both taps and long presses on a button, is below:
Personally, I'd use tap and long press gesture recognizers, e.g.:
override func viewDidLoad() {
super.viewDidLoad()
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress(_:)))
button.addGestureRecognizer(longPress)
let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
tap.shouldRequireFailure(of: longPress)
button.addGestureRecognizer(tap)
}
@objc func handleTap(_ gesture: UITapGestureRecognizer) {
print("tap")
}
@objc func handleLongPress(_ gesture: UILongPressGestureRecognizer) {
if gesture.state == .Began {
print("long press")
}
}
If you want, with the long press gesture, you could perform your action upon .Ended
, too. It just depends upon the desired UX.
FYI, you can also add these two gesture recognizers right in Interface Builder, too, (just drag the respective gestures from the object library on to the button and then control-drag from the gesture recognizer to @IBAction
functions) but it was easier to illustrate what's going on by showing it programmatically.