I have a UIButton
within an app that will allow the user to send a request to an API.
How can I prevent the user from pressing this button more than X times per second?
I have a UIButton
within an app that will allow the user to send a request to an API.
How can I prevent the user from pressing this button more than X times per second?
I assume you want to just ignore taps on the button if they are too frequent, but you don't need to set the button to appear disabled while taps are ignored. (If you want to change the button appearance, you'll need to use an NSTimer
or other delayed action to re-enable the button, which is a significant complication.)
If you change the requirement to “at least 1/X seconds between taps”, it's a little simpler. For example, instead of allowing at most 4 taps per second, we'll ignore a tap if it comes less than 1/4 seconds after the prior tap.
To implement this requirement, store the time of the last tap. When a tap arrives, see if 1/X seconds have elapsed since the last tap. If not, ignore the tap.
private let minimumTapInterval = 1 / CFTimeInterval(4)
private var lastTapTime = CFAbsoluteTime(0)
@IBAction func buttonWasTapped(sender: AnyObject?) {
let now = CFAbsoluteTimeGetCurrent()
guard now >= lastTapTime + minimumTapInterval else { return }
lastTapTime = now
sendAPIRequest()
}
If you really want to implement a “no more than X taps per second” requirement, you can store the times of the accepted taps. When a new tap comes in, throw away any stored times older than one second. If there are still at least X stored times, ignore the new tap.
private let maxTapsPerSecond = 4
private var tapTimes = [CFAbsoluteTime]()
@IBAction func buttonWasTapped(sender: AnyObject?) {
let now = CFAbsoluteTimeGetCurrent()
let oneSecondAgo = now - 1
tapTimes = tapTimes.filter { $0 >= oneSecondAgo }
// All elements of tapTimes are now within the last second.
guard tapTimes.count < maxTapsPerSecond else { return }
tapTimes.append(now)
sendAPIRequest
}
You can use the isEnabled
property of your UIButton
.
For the target of your button, add the following at the end of your @objc
selector function:
@objc func handleMyButtonPress() {
self.someOtherFunction()
myButton.isEnabled = false
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
myButton.isEnabled = true
}
}
The .now() + 1
deadline is basically saying, after 1 second the button will become enabled again and your users can interact with it.
For a 5 minute delay, you could use .now() + (60 * 5)
I use this myself and it works reliably in almost any situation.
Hopefully, this answer can help you or others with the same problem.