How to create a searchable UITableView in iOS 9

TableSearch

Creating searchable table views was always a bit of a nightmare before iOS 8. Until then we had to use a UISearchDisplayController, and sometimes a separate UISearchBar object that needed to be scrolled in and out of vision to make it look elegant. We also had to conform to several protocols. All this was cumbersome and error prone.

Since iOS 8 we can use something much easier called the UISearchController. Sadly the Apple documentation isn’t exactly forthcoming on how to use this thing. Let me show you how to do it in this article.

I’m using Objective-C, Xcode 7, a bit of KVO magic and a simple array for dummy values (no Core Data). If you prefer video instructions, check out my 4 part screencast on building this project over here.

Project Setup

Let’s start with a single view application template in Xcode. We want to add two new classes to our project, both of which will inherit from the UITableViewController. I’ll call one “Table View” and the other one “Search Results”. The first will hold the numbers 1 to 20 written out as strings, and the other one will show us filtered results when users search through those values. Feel free to delete the ViewController h and m files – we won’t need those.

In the storyboard I’ll delete the single view controller and add two table view controllers instead. The first one will be embedded into a navigation controller, to which we’ll add a search button at the top. Hook up an action so that we can do something when the user taps it (i.e. present the search bar and let them search).

Add the right classes for each table view controller, and make sure to give each table view cell a unique reuse identifier (I’ll use “Cell” for both). I also recommend to change each cell style from “Custom” to “Plain” so that we can populate it with some text later.

Screen Shot 2015-10-21 at 09.28.25

The second table view controller won’t be connected to anything – we’ll instantiate it manually. For us to do that we’ll need a storyboard ID. Anything will do here. I’ll call mine “SearchResults”. It’s only appropriate.

Screen Shot 2015-10-21 at 09.43.48

One last thing: make the navigation controller the initial view controller – otherwise we’ll end up with a black screen and nothing else. That would be un-fun.

Screen Shot 2015-10-21 at 09.44.50

Setting up the Main Table View Controller

Before we go any further I’ll make sure our table view has something to display. An NSArray property with some dummy values will do nicely. We can create those with a custom initialiser:

In the screencast I’ll show you a nifty way to create several thousand dummy values with the help of a number formatter. Check it out if you’re interested.

To make sure the table view displays only one section and the correct amount of rows, we’ll tweak the data source methods accordingly. They have a pragma warning there by default as a reminder that something needs to be changed here. Feel free to delete them and make them look like this:

Now we need to populate the table view with some data. The following method is called for each cell. We can use the indexPath property to deduce which value needs to be displayed in each cell. We’ll do that by extracting the correct object from our array and show the text in the cell’s text label:

Make sure to change the dequeueReusableCellWithIdentifier to the same value as you set in the storyboard.

One last thing we should do to make our table view elegant and operational is to deselect a row if it has been tapped. By default when we tap a cell it will highlight, but the highlight never goes away until we select another cell. Read: super ugly. One final method will take care of it: this one is called when someone selects a cell:

All we’ll do is deselect said cell. Run the app and see if it’s working. You should see a simple table view with the numbers 1 to 20 displayed.

Adding the Search Controller

We need a property to hold a reference to our new search controller. And much like with the array above, we’ll use a custom initialiser to configure it at this point:

[emember_protected]

This looks more complex than it is: first we grab a reference to our Search Results View Controller. That’s just another table view we’ve added to the storyboard earlier.

Next we’ll init our search controller’s initWithSearchResultsController method and pass in our special view controller. This will make it easier in code later: one table view controller can take care of displaying the unfiltered results, and another one can focus on showing the filtered results. No ugly if-then statements in every method that way.

The search controller also needs a delegate and a searchResultsUpdater, both of which we’ll take care of in our class. The delegate isn’t strictly necessary, but I’ll show you how you can use it to your advantage.

So far so good. But how do we activate this thing?

That’s the confusing but: the UISearchController is a subclass of UIViewController. To make it visible, we need to present it just like any other view controller. Although Apple allow us to mess with the presentation style, if we want the super slick “fade into view and take the position of the navigation bar” type experience, we don’t have to do a thing – because that’s now the default behaviour.

In our method that’s connected to the search button, all we have to do is present our search controller. This will show the search bar, bring up a keyboard and a cancel button. If someone hits cancel, it’ll mysteriously disappear again. Seriously. That is all. No need to track if this thing is active or move search bars into place. Thank you, Apple!

Run the app now and see what happens. The search bar should appear and disappear already, but no matter what you type won’t make a difference right now. We’ll take care of that next.

Filtering Search Results

The search controller notifies a special delegate if anything in our search bar has changed. This protocol is called the SearchResultsUpdating delegate. For those methods to be called we need to conform to it in our main table view class.

In addition, we need another array to hold all the filtered out values we’ll receive in a moment

Now every time a user enter a single letter, the following method is called. That’s our cue to filter the whole values array and return items matching the current search text. To do this we’ll employ an NSPredicate, another magical class made by the friendly folks in Cupertino. The syntax looks a little weird if you’re not used to it, and I won’t go into detail on how predicates work, but in a nutshell the following method will return an array of filtered results:

Before the method goes to work, we’ll clear out the previous search results by setting the array to nil. The last line simply prints the search results as a log message so we can check if this works in principle. Take it out when you’re happy.

Displaying the Search Results

To show our search results, we need a way to tell our second table view controller about the search results array we’ve just filtered out. There are several ways to do this, but one of the easiest is to use KVO, or Key Value Observing. I’ve written how to use it in this article.

In our main table view controller, we need to import our search results table view controller, grab a reference to it and add an observer for the array. I’ll do that in viewDidload:

To pick this up in our search results table view controller, we need to a property for the array and implement the following method. This will be called every time the array in our main table view controller changes. We’ll take this opportunity to update the array in our search results view controller and reload the data in our table view to update them accordingly:

Great, now that we have some data in our search results table view, let’s configure the table view so that it will display our search results every time the array changes. This is very similar to what we did in our main table view (i.e. configure the number of sections, rows and cells):

Try running the app again – everything should be working now.

[/emember_protected]

Demo Project

Here’s the code I’ve built in the screencast I spoke about earlier. It’s pretty much the same as what we’ve discussed here:

Further Reading

About Jay Versluis

Jay is a medical miracle known as Super Survivor. He runs two YouTube channels, five websites and several podcast feeds. To see what else he's up to, and to support him on his mission to make the world a better place, check out his Patreon Campaign.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.