6

Hoping somebody can shed some light on this.

I'm working on an app for a company and they have a search field that is a custom UITextField. Presently it refines the search with each letter entered.

Problem: They have a little popup indicating that it's searching and with each keypress, you see it flash on and off the screen. They don't want this, obviously.

What I was thinking of doing although not sure how, is instead of refining the search on every keypress, searching when they're finished typing. BUT, I also don't want to have to add a "Search" button. I just want them to finish typing, one second, delay or something, search happens.

Is there a way to detect when the user has finished typing? Has anybody done this?

Thanks in advance.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
r0ddy
  • 159
  • 2
  • 10
  • Possible duplicate of [How to detect a pause in input for UISearchBar/UITextField?](http://stackoverflow.com/questions/7061377/how-to-detect-a-pause-in-input-for-uisearchbar-uitextfield) – Cristik Sep 07 '16 at 03:06

3 Answers3

9

You could probably do this with a simple NSTimer. In shouldChangeCharactersInRange: check if the timer is valid and if it is, invalidate it, then restart the timer. When the time is finally aloud to fire, it means that the user hasn't typed anything in the interval you specify for the time.

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    if (someTimer.isValid) {
        [someTimer invalidate];
    }
    someTimer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeToSearchForStuff:) userInfo:nil repeats:NO];

    return YES;
}

As @Jai Govindani pointed out in order to implement this method, you'll need to assign your controller as the delegate of of your UITextField and make sure your controller conforms to the UITextFieldDelegate protocol.

Mick MacCallum
  • 129,200
  • 40
  • 280
  • 281
  • don't forget to set yourself as the delegate of the UITextField first, of course – Jai Govindani Aug 16 '13 at 15:13
  • This makes sense and it's a great start but I'm getting a crash. Probably due to an initialization issue? I type a first letter in the field, nothing happens (even after delay). Type a second letter in the field, crash! EXC_BAD_ACCESS error. I feel like I'm close here. – r0ddy Aug 16 '13 at 15:49
  • @r0ddy It's hard to say what the problem is, but once you get everything lined up correctly, this approach should work as you want it to. – Mick MacCallum Aug 16 '13 at 15:53
  • I apologize, still learning. Trying to initialize someTimer in the viewController like so: someTimer = [[NSTimer alloc] init]; I presume that's incorrect? – r0ddy Aug 16 '13 at 15:57
  • @r0ddy Yes, the initialization for NSTimer is included in the sample code I've provided. Other than this, all you need to do is configure the UITextFieldDelegate, and create the global reference to the timer, and this should work. – Mick MacCallum Aug 16 '13 at 15:58
  • Got it! Wasn't retaining the timer properly. Once I got that settled it worked like a charm. Thank you muchly. – r0ddy Aug 16 '13 at 18:33
5

With the method timerWithTimeInterval:target:selector:userInfo:repeats: you must add the timer to the runloop. I needed to edit the answer as follows:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range  replacementString:(NSString *)string{
    if (someTimer.isValid) {
        [someTimer invalidate];
    }
    someTimer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeToSearchForStuff:) userInfo:nil repeats:NO];
    [[NSRunLoop mainRunLoop] addTimer:someTimer forMode:NSDefaultRunLoopMode];
    return YES;
}
Josh Gafni
  • 2,831
  • 2
  • 19
  • 32
0

You can try the following way

[self performSelector:<your selector> withObject:<object if you want to pass anything> afterDelay:<time delay>];

in UITextField delegate method

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
Exploring
  • 925
  • 6
  • 18