iOS Quick Tip: Filtering a UITableView with a search bar

Step 1: Add a UISearchBar to your UITableView and create an outlet for it.

Step 2: Add properties for the array of all the table data, and the array of filtered table data.

@property (strong, nonatomic) NSMutableArray* allTableData;
@property (strong, nonatomic) NSMutableArray* filteredTableData;

Step 3: Assign your search bar’s delegate to your controller class.

-(void)viewDidLoad
{
    // ...Do initialization stuff here...
 
    searchBar.delegate = (id)self;
}

Step 4: Implement the searchBar:textDidChange: method from the UISearchBarDelegate protocol. This will let you filter your list as you type. If you want to filter when the search button is clicked, use the searchBarSearchButtonClicked: method instead.

In this example, we are searching through a list of foods with names and descriptions to see if the match the search text. If they do, we add them to our NSMutableArray containing our filtered foods. We also set a flag that indicates whether or not we are currently filtering the list.

-(void)searchBar:(UISearchBar*)searchBar textDidChange:(NSString*)text
{
    if(text.length == 0)
    {
        isFiltered = FALSE;
    }
    else
    {
        isFiltered = true;
        filteredTableData = [[NSMutableArray alloc] init];
 
        for (Food* food in allTableData)
        {
            NSRange nameRange = [food.name rangeOfString:text options:NSCaseInsensitiveSearch];
            NSRange descriptionRange = [food.description rangeOfString:text options:NSCaseInsensitiveSearch];
            if(nameRange.location != NSNotFound || descriptionRange.location != NSNotFound)
            {
                [filteredTableData addObject:food];
            }
        }
    }
 
    [self.tableView reloadData];
}

Step 5: Modify our other UITableViewController methods to make them aware of the isFiltering flag, and to use the correct list depending on whether or not we are filtering.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    int rowCount;
    if(self.isFiltered)
        rowCount = filteredTableData.count;
    else
        rowCount = allTableData.count;
 
    return rowCount;
}
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil)
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
 
    Food* food;
    if(isFiltered)
        food = [filteredTableData objectAtIndex:indexPath.row];
    else
        food = [allTableData objectAtIndex:indexPath.row];
 
    // ... Set up the cell here...;
 
    return cell;
}

Voila! You now have a UITableView that can be filtered using a UISearchBar.

7 thoughts on “iOS Quick Tip: Filtering a UITableView with a search bar

  1. Thanks for the tutorial. I tried recreating this and had one problem. I’m using a “Custom” cell through iOS5 storyboarding. The problem I see is that when getting back items from my filtered array the custom UITableViewCell comes back as null. In iOS5 I understand you should only have to get a cell via dequieReusableCell and not explicity init a cell. However when the search in the search bar successfully matches the cell comes back as null. I’m not sure how I can manually init my cell with the custom style. Any ideas?
    Here’s a snippet from my cellForRowAtIndexPath method.

    ItemCell *cell = (ItemCell *)[tableView
    dequeueReusableCellWithIdentifier:CellIdentifier];
    self.searchBar.tableView.
    if (cell == nil){
    NSLog(@”Why is this happening when I get a successfull match from the search bar?”);
    }

    This causes the following error :
    Assertion failure in -[UISearchResultsTableView _createPreparedCellForGlobalRow:withIndexPath

    *** Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:’

  2. Pingback: ios5 UISearchBar to filter tableview causes Assertion error with a null cell

  3. Hey imran, thanks for the comment.
    Using custom cells while filtering is working for me. as long as I make sure to set the cell’s identifier in my storyboard, and pass that identifier to dequeueReusableCellWithIdentifier:

    static NSString* CellIdentifier = @”MyCustomCellIdentifier”;
    UITableViewCell* cell = [tableView
    dequeueReusableCellWithIdentifier:CellIdentifier];

    And if you have a custom ItemCell class, you should just have to set that as the Custom Class in your cell’s Identity Inspector in your storyboard.

    If you’re already doing that, then I’m afraid I have no idea why it wouldn’t be working!

  4. Hey Marty, thanks for the quick reply. I figured out my problem. I had setup my controller hierarchy incorrectly ( I wanted a navigation controller inside a tab controller ). Anyways, by fixing that problem everything works. Thanks for the help!

  5. Hello, I was trying to make the searchBar…But as you didn’t make the whole script I got confused.
    Can you release the entire project or just say which are the types of the variables, text, food…
    I coudn’t understand the Step 4…that’s why I got confused…
    I don’t know what kind of variables I’ll need for that or what I should attribute for that variables…
    Can you give me a hand? Thanks.

    • Hey, thanks for the comment. I am currently on the road but when I get back I can put up the entire project.

      The Food class is just a simple class with a name and a description property. In step 4, the code searches through all the Food objects to find the ones whose name and/or description match the search text.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">