As with most things there are a bunch of tutorials on the internet about how to search a UITableView, but when I was attempting to implement my search I hit a brick wall with it and ended back where I started - Apple's developer docs - the code from which will be used in this here tutorial. I trawled through many tutorials and cannibalised a load of code, but finally I tamed the beast and got myself a lovely search bar. So this post is as much a form of catharsis for me, as it is a tutorial for you 🙂 Let's get started.
To begin we need to think about what we need for this - we want a search bar, we need a search button, we may even need some scope titles to allow us to filter our results. Something which might not be too obvious to begin with is the fact that we also need to think about the UITableView in which to load our results. When we've finished loading this UITableView, it will display it to the user to scroll through. In terms of variables we also need to consider the fact that we will need a duplicate of our main data - be it an array or dictionary, so that we can store our search results. Lastly we need to store the search term and selected scope so that if our users switch to another view we can retain that information. All of this code is available from Apple, but I'll show you it here in a real world implementation - they use their own "Product" class, but I'll show you how to accomplish search with an NSDictionary and NSArray. Now at this point you may be wondering what you've let yourself in for, after all I've just gone through a rather long list of UI elements that are required to search our table, but luckily for us Apple provides a UISearchDisplayController class, which handles the UI, and expects only the back-end implementation to be written by us. So I'm going to create a new view-based project and get started.
My data structure will be as follows: I will have an NSArray populated with NSDictionary objects holding all my data, I will then populate my table-view with that data, and implement the search functionality based on that structure. If you want to use some other type of object, just replace all mention of NSArray etc. You should also note that Apple's implementation places code inside a view controller based on the UITableView class, not the UIView class - this will affect what variables we need, and what we need to hook up in IB. In this tutorial we will put a UITableView within a UIView.
Notice the mutability of our filtered content array - this is very important as we will be changing the content of this data store each time the user performs a search. If you want to use a dictionary at this point, you know what to do. And with that we can wave goodbye to our header, and welcome our implementation. Once you've synthesised your variables, we need to look at the methods required by the protocols we promised to implement.
[filteredListItems removeAllObjects]; // Clear the filtered array.
This function first clears the filtered array, and then uses fast enumeration to search through our main array for matches. If you don't know what fast enumeration is, in short it is a more efficient way of iterating of collections of data. Inside our loop we compare the scope to "All", and if we have a match we use some nifty functions to see if our search term matches what we have in our array. Notice the if statement also features two other parts - the second compares our "type" key - so maybe dinner or snack, to the scope buttons, and the last checks to see if the scope is nil - meaning we haven't set any buttons for our scope. So if we select the "Snack" scope button it will only compare items with a type set to "Snack".
This next bit of code is for your viewDidLoad method.
// create a filtered list that will contain products for the search results table.
// restore search settings if they were saved in didReceiveMemoryWarning.
Now we need to think about our already-implemented UITableView methods - no you're not going mad, I've not written them here, but we need to change them up a bit. At the moment they all return data based on our main list called "listItems", this is okay if we need to display our table to users, but when in search mode, our functions will be returning the unfiltered data - let's get a fixin'.
Now we are ready to run it! Go ahead and hit run, and type something in the search bar, you should see a filtered list appear as you type. Now you may be getting a warning or two if you used the variable name "tableView", about the method parameters hiding your instance variables, in this case it's not a big deal, but it might be a good idea to change the name to something a little more descriptive in the future.
So what if we want to implement some scope buttons? Well now we've done all the work behind the scenes, we can jump into IB, and add some titles. So go to IB and select your search-bar. In the attributes inspector you should see a box that says "Scope titles", just above that is a check box that says "Shows Scope Bar" - check that, and use the +/- controls to add titles. Then if you want your scope bar to be visible all the time leave the check box checked, but if you want it to only appear when the search becomes active, uncheck the box. Now when you run the program you should see that a scope bar has appeared!
And with that, we have successfully added search to our app. If you have any issues with this code do let me know, and if you want the completed project use the link below.
Thanks for putting this together, Tom and making it simple. I really appreciate it.
I appreciate you putting this tutorial together. It was a big help.
Straight forward freat example, exactly what I needed. Thx for posting!