Detect when user stopped / paused typing in Swift

Tuan Anh Vu picture Tuan Anh Vu · Apr 21, 2015 · Viewed 8.3k times · Source

I've dug around stackoverflow and found the solution which I converted to Swift, it doesn't seem to work and the selector is still being performed.

 func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
    self.filter.searchTerm = self.searchBar.text

    NSObject.cancelPreviousPerformRequestsWithTarget(self, selector: "getHints", object: nil)
    NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "getHints", userInfo: nil, repeats: false)
}

Is there a better way to do this in swift? Thanks!

Answer

nacho4d picture nacho4d · Apr 21, 2015

UPDATE 2016/09/01:

We can use NSTimers or (since swift 2.0) NSObject's performSelector and friends.

Aproach 1 : performSelector

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange,
    replacementString string: String) -> Bool {
    NSObject.cancelPreviousPerformRequests(
        withTarget: self,
        selector: #selector(ViewController.getHintsFromTextField),
        object: textField)
    self.perform(
        #selector(ViewController.getHintsFromTextField),
        with: textField,
        afterDelay: 0.5)
    return true
}

func getHintsFromTextField(textField: UITextField) {
    print("Hints for textField: \(textField)")
}

Approach 2: NSTimer

var timer: NSTimer? = nil

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange,
    replacementString string: String) -> Bool {
    timer?.invalidate()
    timer = Timer.scheduledTimer(
        timeInterval: 0.5,
        target: self,
        selector: #selector(ViewController.getHints),
        userInfo: ["textField": textField],
        repeats: false)
    return true
}

func getHints(timer: Timer) {
    var userInfo = timer.userInfo as! [String: UITextField]
    print("Hints for textField: \(userInfo["textField"])")
}

Note I am passing the textField to delayed functions. It is not always required but it could make your life easier when textField is not easy to access or when dealing with various text fields.

How NSTimer approach is different from performSelector?

When you call performSelector the target is retained (in swift the target is always self) but when you use NSTimer the target is NOT retained. This means, if you use NSTimers, you have to make sure target (in this case self) is alive by the time the timer fires. Otherwise the a crash will occur.

(BTW: performSelector uses NSTimer internally 😀 )

If you are interested in GCD timers this gist a good place start: maicki/TimerWithGCD.md