The term for the solution you are trying to find is called "Debouncing". The idea is that you coalesce frequent calls to the same method and only execute the method once the calls have stopped for a period of time. Debouncing is a great way to improve the user experience when you are taking in a lot of user input quickly and must do a relatively heavy workload on that input. Only executing the work when the user has completed their input saves the cpu from doing too much work and slowing the app down. Some examples might be moving a view when the user scrolls the page, updating a table view when the user enters a search term or making network calls after a series of button taps.
An example showing how one may implemented it with a UISlider
is shown below in a playground. You can copy and paste the example into an empty playground to give it a try.
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
// Timer to record the length of time between debounced calls
var timer: Timer? = nil
override func loadView() {
super.loadView()
let view = UIView()
view.backgroundColor = .white
// Set up the slider
let slider = UISlider(frame: CGRect(x: 100, y: 100, width: 200, height: 50))
slider.minimumValue = 0
slider.maximumValue = 100
slider.isContinuous = true
slider.addTarget(self, action: #selector(sliderValueDidChange(_:)), for: .valueChanged)
self.view.addSubview(slider)
}
@objc func sliderValueDidChange(_ sender: UISlider) {
// Coalesce the calls until the slider valude has not changed for 0.2 seconds
debounce(seconds: 0.2) {
print("slider value: \(sender.value)")
}
}
// Debounce function taking a time interval to wait before firing after user input has stopped
// and a function to execute when debounce has stopped being called for a period of time.
func debounce(seconds: TimeInterval, function: @escaping () -> Swift.Void ) {
timer?.invalidate()
timer = Timer.scheduledTimer(withTimeInterval: seconds, repeats: false, block: { _ in
function()
})
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
PlaygroundPage.current.needsIndefiniteExecution = true
The meat of the example is this function:
func debounce(seconds: TimeInterval, function: @escaping () -> Swift.Void ) {
timer?.invalidate()
timer = Timer.scheduledTimer(withTimeInterval: seconds, repeats: false, block: { _ in
function()
})
}
Here every time debounce
is called it invalidates a timer and then schedules a new timer to call the function
passed in. This ensures that the function
is not called until enough time has elapsed to not have the timer invalidated.
isContinuous
istrue
, there is no need to update anything when dragging stops because you would have already been told about the most recent update just before the dragging stopped. – LevanUISlider
is aUIControl
. I've never tried but you might be able to add a target/action for one or more of the "touch-up" events. – Levan