Reputation: 1562
I have a scenario where a button triggers a change that requires a UI to be updated but the nature of the button is such that the user will often press it repeatedly and running the updateUI() function every time the button is pressed seems excessive. Instead I would like to call the function with a delay using something like this:
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0, execute: {
self.updateUI()
})
but collapsing all those identical function calls into a single one.
Is it possible to remove duplicate function calls from the DispatchQueue or is there a different way of doing this?
Upvotes: 0
Views: 362
Reputation: 2451
Have you looked into NSObject's cancelPreviousPerformRequest?
This works with objective-c selectors. More info here
from apple documentation.
Cancels perform requests previously registered with the perform(_:with:afterDelay:) instance method.
What I would do is cancel the request then call the selector
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(self.someSelector), object: nil)
self.perform(#selector(self.someSelector), with: nil, afterDelay: 0.3) // add a delay so we can cancel it, otherwise it will immediately be called.
@objc
func someSelector() {
//do something here
}
Upvotes: 0
Reputation: 545
Have your async calls all execute a function, passing a deadline parameter, and also update a property storing the last deadline sent.
Your function will check the deadline parameter received against the last called deadline. If they don't match, it means that there was a more recent press of the button, so ignore that execution. If they match, then you can execute.
Here's a little example you can plug into a new SwiftUI single view app. Even if you don't know SwiftUI, it should be fairly clear what's going on.
Every time the button is pressed, it will reset the deadline. No matter how many times you press the button, you can see it only executes the print function (and changes the colour of the button), after 2 seconds without a press.
struct ContentView: View {
@State public var buttonSelected = false
@State private var lastDeadline = DispatchTime.now()
func delayedUpdate(when currentDeadline: DispatchTime) {
guard currentDeadline == lastDeadline else {
return
}
print("Delayed Action!")
self.buttonSelected.toggle()
// Do your self.updateUI() or whatever else here.
}
var body: some View {
Button(action: {
let deadline = DispatchTime.now() + 2.0
self.lastDeadline = deadline
DispatchQueue.main.asyncAfter(deadline: deadline, execute: {
self.delayedUpdate(when: deadline)
})
}) {
Text("Delayed")
.padding()
.foregroundColor(.white)
.background(self.buttonSelected ? Color.blue: Color.green)
.clipShape(Capsule())
}
}
}
Upvotes: 1