JoshDG
JoshDG

Reputation: 3931

iOS: Search suggestions as you type

I have a viewcontroller with a searchView and a tableView and I want the tableView to display results from a websearch based on the text of the searchView (changing as you add more letters to the search).

As I have it now, each time a letter is added it searches properly but the app stalls as it searches so you can't add in a new letter until the last results have returned.

Is there a smarter way to do this so that when a new letter is added the last search is essentially aborted?

- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
    if(searchText.length>3)
    {
        [self getWebDataWithQuery:searchBar.text]
        [tblResults reloadData];
    }
}

Upvotes: 2

Views: 2143

Answers (4)

danypata
danypata

Reputation: 10175

Another way to do it (I did it like this in one of my projects) I create an NSOperations for the search. Every time a character from the search string was changed I checked if the last search query is not equal with the current, if it wasn't then I cancel all the executing operations and after that I create a new operation and start it.

Off course all the requests/data processing is performed in background thread and only when the downloading/parsing/processing is completed the UI is notified.

Upvotes: 0

LombaX
LombaX

Reputation: 17364

You can do something similar:

  • on view controller load you load the data from web/database/core data
  • put the data inside an array (the data can be objects or dictionaries), in my example is "arrayOfActivities"
  • create a secondary array, let's call it "filteredArray" and copy all the content inside it

Then, at every digit, update the filtered array using a predicate. This is good if you have a small dataset (you can even split your data in two arrays and permit the search only on a small subset, like the newest, for example)

- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText;
{
    if (![searchText isEqualToString:@""]) // here you check that the search is not null, if you want add another check to avoid searches when the characters are less than 3
    {
        // create a predicate. In my case, attivita is a property of the object stored inside the array
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(attivita CONTAINS[cd] %@)", searchText];
        self.filteredArray = [[self.arrayOfActivities filteredArrayUsingPredicate:predicate] mutableCopy];
    } else {
        self.filteredArray = [self.arrayOfActivities mutableCopy];  // I used a mutable copy in this example code
    }

    // reload section with fade animation
    [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
}

Upvotes: 0

HalR
HalR

Reputation: 11073

You could use a call like this in your search results

   dispatch_async(dispatch_get_main_queue(), ^{
       [self getWebDataWithQuery:searchBar.text]
       [tblResults reloadData]
    });

Upvotes: 1

bbarnhart
bbarnhart

Reputation: 6710

Your call to [self getWebDataWithQuery:searchBar.text] is calling [NSData datawithContentsOfURL:]. That is a synchronous call. You need to use an asynchronous mechanism to collect your data from the web service. Either use a third party network framework such as AFNetworking or NSULRConnection.

This will allow the user to continue typing and will not block the UI.

Upvotes: 1

Related Questions