Add Search Into a Table View

In the mobile app world, people want their information fast, and they want it now!

iOS users expect their data to be available on-demand and presented quickly. Furthermore, they expect this to all happen in an easy to use and intuitive manner. It’s a tall order, to be sure!

Many UIKit based applications utilize the UITableView as a way to navigate through data, since scrolling is natural and fast. But what about the cases where there are large, even huge, amounts of data to sift through? With large datasets, scrolling through massive lists becomes slow and frustrating – so it’s vitally important to allow users to search for specific items.

Lucky for us, UIKit includes UISearchBar which seamlessly integrates with your table views and allows for quick, responsive filtering of information.

In this tutorial, you’ll learn how to add search capability into your UITableView based apps, including dynamic filtering and adding an optional Scope Bar. In the end, you’ll know how to make your apps much more user friendly and satisfy your users urgent demands!

Don’t Fear the Bar!

The ability to search a large table view is something that today’s users expect; when they find it isn’t present, they won’t be happy campers!

Don’t let this happen to your users.
Always give them a search option.

The problem is the UISearchBar can be confusing when you first look at it, because it isn’t very well documented and figuring out how to get all the pieces to fit together can be quite tricky.

However, once you work with the Search Bar a bit, you’ll find that it’s not nearly as frightening as you might expect – especially after you go through this tutorial!

When you look at the UISearchBar, you’ll discover it’s kind of like a lazy layabout. It doesn’t do any of the work of searching at all! The class simply provides a standard interface that users have come to expect from their iOS apps. It’s more like a middle-class manager in that respect; it’s great at delegating tasks to others. (Like my old boss!)

The UISearchBar class communicates with a delegate protocol to let the rest of your app know what the user is doing. All of the actual functions for string matching and other operations will be written by you. Although this may seem a tad scary at first (and more than a little unfair!), writing custom search functions gives you tight control over how results are returned specifically in your app. Your users will appreciate searches that are intelligent — and fast.

In this tutorial you’ll build a searchable Candy app which is based on a table view. (Mmmm, candy!)

But first — a quick road map of where this tutorial will take you:

  • Candy Class: You’ll build a custom object that will allow your search function to search through the sample data (and also satisfy those munchies).
  • Table View: Here you’ll quickly go through the steps of setting up a table view. If you already know how to do this, you can quickly breeze through this section.
  • Search Bar: You’ll implement a search bar class onto your view controller so you can introduce some search functionality.
  • Filtered Array: Here you’ll learn how to manipulate a filtered array to handle your new search function.
  • Sending Data: This will serve as a reinforcement to the changes that are necessary to things like navigation when dealing with a search bar
  • Scope Bar: Here you’ll experience a powerful feature of the UISearchBar class: a scope bar for further zeroing in on search results.
  • Hiding the UISearchBar: This last section will show you how to perform a popular feature – hiding the search bar until the user needs it!

Ready for some sugar-coated search results? Read on!

I Want Candy

I'm getting hungry just working on this tutorial!

I’m getting hungry just working on this tutorial!

In XCode, go to “File \ New \ Project…” and choose iOS\Application\Single View Application. Name the project “CandySearch” and make sure that “Use Storyboards” and “Use Automatic Reference Counting” are checked. Finally make sure that iPhone is selected for “Device” and click Next. Save the project at a location of your choice.

Start by clearing out some of the default files so you can truly start from scratch. In the Project Navigator, select ViewController.h and ViewController.m, right-click, select Delete, and then click “Move to Trash”. Then open up the MainStoryboard.storyboard, select the only view controller and delete it.

Now that you have a fresh project to work with, start by creating the storyboard. From the Object Browser (the lower half of the right sidebar) drag out a Navigation Controller to add iOS’s built-in navigation logic to our project. This will create two views on the storyboard – one that represents the navigation controller and one that will be the UITableView that will be the initial screen of the application.

One final view controller will serve as the detail view controller that is displayed when the user navigates or searches the table. Drag a View Controller object onto the storyboard and link the two views by control-dragging from the Table View to the new view controller and selecting “Push” as the modal segue from the popup. As of now, this is how your project should look:

Setting Up the Candy Class

Next you will create a model class to keep track of the information about each piece of candy you’re displaying, such as its category and name.

To do this, create a new file with the iOS\Cocoa Touch\Objective-C class template. Name the class Candy, and make it a subclass of NSObject.

Open up Candy.h and replace its contents with the following:

    #import <Foundation/Foundation.h>
    @interface Candy : NSObject {
        NSString *category;
        NSString *name;
    @property (nonatomic, copy) NSString *category;
    @property (nonatomic, copy) NSString *name;
    + (id)candyOfCategory:(NSString*)category name:(NSString*)name;

This object has two properties that can be referenced the category of the candy, and the name of the candy. When the user searches for a candy in your app, you’ll be referencing the name property against the user’s search string. You’ll see how the category string will become important near the end of this tutorial when we implement the Scope Bar.

To finish making this object, replace the contents of Candy.m with the following code:

    #import "Candy.h"
    @implementation Candy
    @synthesize category;
    @synthesize name;
    + (id)candyOfCategory:(NSString *)category name:(NSString *)name
        Candy *newCandy = [[self alloc] init];
        newCandy.category = category; = name;
        return newCandy;

The method above gives your custom object a way to create a new instance of the Candy object which contains the name and category passed to the method. You’ll populate your table from the data stored in these Candy objects, which will allow you to easily filter the data using your search methods.

Now we are ready to set up the UITableView that our UISearchBar will filter!

Please set the UITableViewController, dear…

Next you will set up a UITableView that will work with the UISearchBar. Create a new file with the iOS\Cocoa Touch\Objective-C class template. Name the class CandyTableViewController, and make it a subclass of UITableViewController.

We will start by adding an array for the sample data. Open CandyTableViewController.h and add the following code below the @interface line:

    @property (strong,nonatomic) NSArray *candyArray;

Now for some quick edits to CandyTableViewController.m in order to finish the sample table. First, clean out some of the default code in the project that won’t be required. Remove all the code beginning with -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath to the end of the file (except the @end).

Also remove the numberOfSectionsInTableView: method, as it won’t be necessary in this app.

At the top of the file, import Candy.h so the table view controller understands this object.

    #import "Candy.h"

Now synthesize the candyArray below the @implementation line.

    @synthesize candyArray;

Now that you’ve connected the view controller with the Candy object, you can use the candyOfCategory:name: method to add some data. In this tutorial, you only need to create a limited number of values to illustrate how the search bar works; in a production app, you might have thousands of these searchable objects. But whether an app has thousands of objects to search or just a few, the methods used will remain the same. Scalability at it’s finest!

In viewDidLoad, clear out the templated comments and add the following code to provide some sample data:

    // Sample Data for candyArray
    candyArray = [NSArray arrayWithObjects:
                  [Candy candyOfCategory:@"chocolate" name:@"chocolate bar"],
                  [Candy candyOfCategory:@"chocolate" name:@"chocolate chip"],
                  [Candy candyOfCategory:@"chocolate" name:@"dark chocolate"],
                  [Candy candyOfCategory:@"hard" name:@"lollipop"],
                  [Candy candyOfCategory:@"hard" name:@"candy cane"],
                  [Candy candyOfCategory:@"hard" name:@"jaw breaker"],
                  [Candy candyOfCategory:@"other" name:@"caramel"],
                  [Candy candyOfCategory:@"other" name:@"sour chew"],
                  [Candy candyOfCategory:@"other" name:@"peanut butter cup"],
                  [Candy candyOfCategory:@"other" name:@"gummi bear"], nil];
    // Reload the table
    [self.tableView reloadData];

Replace the contents of tableView:numberOfRowsInSection: with the following line that returns the size of the candyArray:

return [candyArray count];

Add the following code above the final @end:

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if ( cell == nil ) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    // Create a new Candy Object
    Candy *candy = nil;
    candy = [candyArray objectAtIndex:indexPath.row];
    // Configure the cell
    cell.textLabel.text =;
    [cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
    return cell;

First, you tell the table view controller what to display for each row. You then access the candyArray object, reference the indexPath to decide which Candy object to pull, and then use that Candy object to populate the UITableViewCell.

You’ll need to set up the storyboard to recognize this code. Switch to MainStoryboard.storyboard, select the Root View Controller and change the Class field in the Identity Inspector (third tab on the top half of the right sidebar) to CandyTableViewController.

Then select the table view and wire the dataSource and delegate to the View Controller by switching to the Connections Inspector (it’s the sixth tab on the top half of the right sidebar) and dragging from each outlet to the Candy Table View Controller on the left sidebar.

Double click the title to change it from the rather unattractive “Root View Controller” title to “CandySearch”.

Save your changes and build and run. You now have a working table view! So much candy…so little time! We need…a UISearchBar!

Setting Up the UISearchBar

Now you’re ready to set up the UISearchBar! Start by opening up the storyboard and dragging a Search Bar and Search Display Controller object to the table view controller. Be careful — this is different from the Search Bar object, which is also available. Position the Search Bar between the Navigation bar and the Table View.

Note: Not sure what’s meant by a search display controller? According to Apple’s own documentation, a search display controller “manages display of a search bar and a table view that displays the results of a search of data managed by another view controller.

You initialize a search display controller with both a search bar and a view controller responsible for managing the original content to be searched. When the user begins a search action, the search display controller is responsible for superimposing the search interface over the original view controller’s view, and showing the search results. The results are displayed in a table view that’s created by the search display controller.”

So basically, the search display controller added above handles the task of showing the filtered data from a search in a separate view controller that you don’t have to worry about :]

UISearchBar Options in the Attributes inspector

While in the storyboard, take a moment to review the properties available for the Search Bar object. You may not use all of these, but knowing what’s available in the Attributes inspector is always valuable when working with a new UIKit component.

  • Text: This will change the actual string value that is present in the search bar. As your app has no need to have a default value in the search bar, you won’t need this.
  • Placeholder: This does exactly what you might expect – it allows you to put the light gray text in the Search bar that tells the user what the search bar can be used for. In the case of the Candy app, use something such as “Search for Candy”.
  • Prompt: This text will appear directly above the search bar. This is good for apps that have complicated search mechanisms, where the user might need instructions. (But in this app, the Placeholder text should be pretty clear!)
  • Style & Tint: These options allow you to customize the appearance of your search bar. The options are almost identical to those of the UINavigationBar and it is normally advisable to have your UISearchBar match your UINavigationBar for harmonious design.
  • Show Search Results Button: Provides a button on the right side of the search bar for performing functions such as displaying recent searches or showing the last search results. Interaction with this button is managed through the Search Bar Delegate methods.
  • Show Bookmarks Button: Shows the standard blue oval bookmarks icon in the right hand side of the search bar. Users expect this to bring up a list of their saved bookmarks. Like the search results button, this too is managed through the Search Bar Delegate methods.
  • Show Cancel Button: This button allows users to close the separate view controller that is generated by the search bar. Leave this unchecked, as the search bar will automatically show and hide the Cancel button when the user is in Search mode.
  • Shows Scope Bar & Scope Titles: The scope bar allows users to refine their search by limiting the results to a certain category, or scope. In a music application, for example, this bar may show choices such as Artists, Albums or Genres. For now, leave this box unchecked; you will implement your own scope bar later in this tutorial.
  • Capitalize, Correction & Keyboard: These are options that have been borrowed from the normal UITextField options and allow you to change your search bar’s behavior. For example, if the user will be searching on proper nouns like businesses or last names, you may want to consider turning off correction as it will be annoying to users. In this tutorial, the candy names are all common nouns, so keep autocorrect enabled.

Note: Knowing the available options allows you to reduce development time and be more efficient. So as a general note for your future iOS development, always take the time to get acquainted with the resources available :]

Setting Up the UISearchBarDelegate

After setting up the storyboard, you’ll need to do some coding work to get the search bar working. Add theUISearchBarDelegate and UISearchDisplayDelegate classes to the CandyTableViewController. To do this, switch to CandyTableViewController.h and replace:

@interface CandyTableViewController : UITableViewController


@interface CandyTableViewController : UITableViewController <UISearchBarDelegate, UISearchDisplayDelegate>

In addition, add an IBOutlet for the search bar and a new “filteredCandyArray” which will hold our search results.

@property (strong,nonatomic) NSMutableArray *filteredCandyArray;
@property IBOutlet UISearchBar *candySearchBar;

Don’t forget to synthesize the new properties in CandyTableViewController.m:

@synthesize filteredCandyArray;
@synthesize candySearchBar;

Of course, now that you have an outlet, you need to wire the search bar on your storyboard to the outlet. Switch to the storyboard, select the Candy Table View controller, switch to the Connections Inspector, and drag a connection from the candySearchBar outlet to the Search Bar on the main screen.

Compile and run now, and you’ll get the search bar on the app — but it won’t filter anything yet! In order to do this, you’ll have to set up the filteredCandyArray.

Setting Up the Filtered Array

First you will have to initialize the NSMutableArray. You can initialize the array with the size of the candyArray, as it isn’t possible to have more filtered values than the number of values that we started with. Add the following line of code to viewDidLoad in CandyTableViewController.m where we set the candyArray, but before the [self.tableView reloadData]:

    // Initialize the filteredCandyArray with a capacity equal to the candyArray's capacity
    self.filteredCandyArray = [NSMutableArray arrayWithCapacity:[candyArray count]];

Add the following code to the end of the file:

#pragma mark Content Filtering
-(void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
    // Update the filtered array based on the search text and scope.
    // Remove all objects from the filtered search array
    [self.filteredCandyArray removeAllObjects];
    // Filter the array using NSPredicate
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@" contains[c] %@",searchText];
    filteredCandyArray = [NSMutableArray arrayWithArray:[candyArray filteredArrayUsingPredicate:predicate]];

The above method will filter candyArray based on searchText (which is the search string entered by the user), and will put the results in filteredCandyArray. This filter method clears out any previous filtered search results by calling the “removeAllObjects” method. After the array has been cleared, a NSPredicate is used to filter the candy array based on the search string.

NSPredicate can filter an array using a simple condition string. Notice the format of the NSPredicate:

@" contains[c] %@",searchText

Look a little confusing? Fear not; it’s actually fairly simple. tells the NSPredicate to look at the “name” property of the Candy objects in the candyArray. “contains[c]” tells the predicate to search the “name” property for the provides text string, which is the search text in this case, in a case-insensitive manner.

Next, add the following code to the end of the file:

#pragma mark - UISearchDisplayController Delegate Methods
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
    // Tells the table data source to reload when text changes
    [self filterContentForSearchText:searchString scope:
     [[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
    // Return YES to cause the search result table view to be reloaded.
    return YES;
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
    // Tells the table data source to reload when scope bar selection changes
    [self filterContentForSearchText:self.searchDisplayController.searchBar.text scope:
     [[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption]];
    // Return YES to cause the search result table view to be reloaded.
    return YES;

This code sets up the UISearchDisplayController Delegate methods that will call the content filtering function when the the user enters a search query.

The first method runs the text filtering function whenever the user changes the search string in the search bar. The second method will handle the changes in the Scope Bar input. You haven’t yet added the Scope Bar in this tutorial, but you might as well add this UISearchBarDelegate method now since you’re going to need it later.

Compile and run the app now; you’ll notice that using the Search Bar still does not bring up any filtered results. What gives? This is simply because you haven’t yet written the code to let cellRowForIndexPath: know when to use the normal data vs. the filtered data. Replace:

	candy = [candyArray objectAtIndex:[indexPath row]];


    // Check to see whether the normal table or search results table is being displayed and set the Candy object from the appropriate array
    if (tableView == self.searchDisplayController.searchResultsTableView) {
        candy = [filteredCandyArray objectAtIndex:indexPath.row];
    } else {
        candy = [candyArray objectAtIndex:indexPath.row];

This code tests to see if the currently displayed tableView is the search table or the normal table. If it is indeed the search table, the data is taken from the filteredCandyArray. Otherwise, the data comes from the full list of items. Recall that the search display controller automatically handles showing and hiding the results table, so all your code has to do is provide the correct data (filtered or non-filtered) depending on which table view is currently displaying.

You’ll have to do something similar with numbersOfRowsInSection: since the number of rows for the filtered vs. non-filtered array are very likely going to be different! Replace the current contents of the method with:

// Check to see whether the normal table or search results table is being displayed and return the count from the appropriate array
if (tableView == self.searchDisplayController.searchResultsTableView) {
    return [filteredCandyArray count];
} else {
    return [candyArray count];

Compile and run the app. You’ve got a functioning Search Bar that filters the rows of the main table! Huzzah! Play with the app for a bit to see how the user can search for various candies.

Note: You’ve probably noticed that that the if/else logic found in the numberOfRowsInSection: method is reused quite a few times. This is important when working with the Search Bar Display Controller and omitting this if/else check may result in bugs that will be difficult to track down. Just remember that the filtered results do not appear in the same table view as the main table. They are actually completely separate table views, but Apple has designed them in such a way that the experience is seamless for the end user — at the expense of being confusing to the novice developer!

Sending Data to a Detail View

When sending information to a detail view controller, you need to ensure that the view controller knows which table view the user is working with: the full table list, or the search results. The code for this will be similar to the code that we wrote in numbersOfRowsInSection: and cellForRowAtIndexPath: methods. Add the following code to the end of your CandyTableViewController.m file:

#pragma mark - TableView Delegate
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // Perform segue to candy detail
    [self performSegueWithIdentifier:@"candyDetail" sender:tableView];
#pragma mark - Segue
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([[segue identifier] isEqualToString:@"candyDetail"]) {
        UIViewController *candyDetailViewController = [segue destinationViewController];
        // In order to manipulate the destination view controller, another check on which table (search or normal) is displayed is needed
        if(sender == self.searchDisplayController.searchResultsTableView) {
            NSIndexPath *indexPath = [self.searchDisplayController.searchResultsTableView indexPathForSelectedRow];
            NSString *destinationTitle = [[filteredCandyArray objectAtIndex:[indexPath row]] name];
            [candyDetailViewController setTitle:destinationTitle];
        else {
            NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
            NSString *destinationTitle = [[candyArray objectAtIndex:[indexPath row]] name];
            [candyDetailViewController setTitle:destinationTitle];

Open up the storyboard and make sure that the segue from the Candy Table View Controller to the Detail View has the identifier “candyDetail”. Compile and run the code at this point and see how the app now navigates to the Detail View from either the main table or the search table with ease.

Creating an Optional Scope Bar to Filter Results

If you wish to give your users another way to further filter their results, a Scope Bar can be used in conjunction with your search bar in order to filter out items by their category. The categories you will filter on are the ones you assigned to the Candy object when the candyArray was created: chocolate, hard, and other.

First, set up the scope bar on the storyboard. Go to the CandySearch View Controller and select the search bar. In the attributes inspector, check “Shows Scope Bar” in the Options section. Then modify the Scope Titles to be: “All”, “Chocolate”, “Hard” ,and “Other”. (You can use the + button to add more scope items and if you double-click an item, you can edit the item title). This is what your screen should look like:

Next, modify filterContentForSearchText: in CandyTableViewController.m to take the new scope into account. Replace the current method implementation with the following:

-(void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
	// Update the filtered array based on the search text and scope.
    // Remove all objects from the filtered search array
	[self.filteredCandyArray removeAllObjects];
	// Filter the array using NSPredicate
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@" contains[c] %@",searchText];
    NSArray *tempArray = [candyArray filteredArrayUsingPredicate:predicate];
    if (![scope isEqualToString:@"All"]) {
        // Further filter the array with the scope
        NSPredicate *scopePredicate = [NSPredicate predicateWithFormat:@"SELF.category contains[c] %@",scope];
        tempArray = [tempArray filteredArrayUsingPredicate:scopePredicate];
    filteredCandyArray = [NSMutableArray arrayWithArray:tempArray];

The new code introduces an additional NSPredicate — scopePredicate — that will further filter the array based on the scope. In order to support the “All” scope, you have to make sure that you don’t do any extra filtering if the scope is equal to “All”.

Compile and run the app. You’ll notice that the scope bar shows up — hey, that was easy! However, the app shows the scope bar before the user has even tapped on the Search Bar! It looks like we’re getting ahead of ourselves here. Add this code to the top of viewDidLoad (right after the call to super):

	// Don't show the scope bar or cancel button until editing begins
    [candySearchBar setShowsScopeBar:NO];
    [candySearchBar sizeToFit];

Compile and run now. Here’s how your scope bar should look:

Hiding the UISearchBar Like the Music App

It would be really neat if your app could initially hide the Search Bar, just like the way the Music app does. This initially gives your table view a little more real estate. Simply add the following code immediately below the code you just added to viewDidLoad:

    // Hide the search bar until user scrolls up
    CGRect newBounds = self.tableView.bounds;
    newBounds.origin.y = newBounds.origin.y + candySearchBar.bounds.size.height;
    self.tableView.bounds = newBounds;

Since your app hides the Search Bar, it wouldn’t be a bad idea to add a button to indicate to the user that a search function exists. The decision to include an explicit search button depends on the context of the app you’re developing, of course, but in this tutorial we’ll make it obvious that this app supports searching.

Begin by making an IBAction:

Add the follwing code to the bottom of CandyTableViewController.m:

-(IBAction)goToSearch:(id)sender {
    // If you're worried that your users might not catch on to the fact that a search bar is available if they scroll to reveal it, a search icon will help them
    // If you don't hide your search bar in your app, don’t include this, as it would be redundant
    [candySearchBar becomeFirstResponder];

Add the method definition to CandyTableViewController.h:


Now go to the storyboard and add a Bar Button Item to the navigation bar. In the Attributes Inspector, use Apple’s built-in search icon by changing the identifier to “search”. Then in the connections inspector, connect to goToSearch: by dragging from the “selector” item under “Sent Action” to the view controller.

Bingo! You now have a functional “Search” button on your navigation bar, which takes the user to the search interface. Compile and run to test…

and you’re done! Now give yourself a nice piece of candy :]

Where To Go From Here?

Here is a sample project with all of the code from the above tutorial.

Congratulations – you now have a working app that allows you to search directly from the main table view!

If you were going through this tutorial in order to learn how to implement search in your app, you’re in luck. The code that you’ve just worked through is completely extensible; all you have to do is add more objects as necessary. In a production app, the objects will not usually be defined directly in the code; they would be likely be loaded from some data source such as a plist file.

I hope to see you adding search capabilities to your table view apps in the future. If you have any questions or comments, please join the forum discussion below!


One thought on “Add Search Into a Table View

Add yours

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a free website or blog at

Up ↑

%d bloggers like this: