Introduction
If you’ve used an iPhone or iPad before you’ve probably used a Search Bar. This tutorial will show how to add a Search Bar to your project. Again we will be using the Staff Manager project from previous parts of this tutorial set. Specifically we will be adding the Search Bar to the Roles Table View of the Staff Manager project. Here’s what we’re aiming at:
Prerequisites
We follow on from Part 6, so download the project from the end of Part 6 then extract and open it with Xcode.
Adding a Search Bar
Select MainStoryboard.storyboard and find the Roles Table View Controller on the canvas. Drag a Search Bar and Search Display Controller on to the Roles Table View. Be careful not to drag it into the prototype cell.
The Roles Table View should now look like this with a Search Bar:
Run the app and go to the Roles tab. Start typing in the Search Bar and you will notice the table view does not filter out data which doesn’t match the search criteria. Useless!
Making the Search Bar Useful
The data in the Table View should be filtered to show search results as a user types in the Search Bar. In order for this to work we need to set up the RolesTVC class to implement the UISearchDisplayDelegate and UISearchBarDelegate protocols. Update the following line in RolesTVC.h:
@interface RolesTVC : CoreDataTableViewController <AddRoleTVCDelegate, RoleDetailTVCDelegate, UISearchDisplayDelegate, UISearchBarDelegate> |
To display a filtered subset of data we need to store the search results in an array. What better place than in a searchResults NSMutableArray! Add the following property to RolesTVC.h:
@property (nonatomic, retain) NSMutableArray *searchResults; |
Of course, you will then need to add the following to RolesTVC.m:
@synthesize searchResults; |
Also, create the following methods in RolesTVC.m. They are responsible for initial setup and removal of the searchResults array:
- (void)viewDidLoad { self.searchResults = [NSMutableArray arrayWithCapacity:[[self.fetchedResultsController fetchedObjects] count]]; [self.tableView reloadData]; } - (void)viewDidUnload { self.searchResults = nil; } |
So far in the tutorial series the CoreDataTableViewController has shielded us from needing to configure the numberOfRowsInSection method required by a TableView. This method simply tells the table how many rows to display. The answer will be different depending on whether we are showing all data or only searchResults. Add the following method to RolesTVC.m:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if (tableView == self.searchDisplayController.searchResultsTableView) { return [self.searchResults count]; } else { return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects]; } } |
Likewise, the existing cellForRowAtIndexPath method also needs to differentiate between data from the searchResults and otherwise. Change the cell configuration in the cellForRowAtIndexPath method within RolesTVC.m as follows:
// Configure the cell... Role *role = nil; if (tableView == self.searchDisplayController.searchResultsTableView) { NSLog(@"Configuring cell to show search results"); role = [self.searchResults objectAtIndex:indexPath.row]; } else { NSLog(@"Configuring cell to show normal data"); role = [self.fetchedResultsController objectAtIndexPath:indexPath]; } |
The next method is where most of the search filter magic happens. When you adapt the code to your own needs you will need to know what is happening here. Here’s the overview:
- All searchResults are removed.
- A role object is created for every object fetched from Core Data.
- A role object is put into the searchResults array for all roles with a role name that matches searchText.
Add the following method to RolesTVC.m:
#pragma mark - #pragma mark Content Filtering - (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope { NSLog(@"Previous Search Results were removed."); [self.searchResults removeAllObjects]; for (Role *role in [self.fetchedResultsController fetchedObjects]) { if ([scope isEqualToString:@"All"] || [role.name isEqualToString:scope]) { NSComparisonResult result = [role.name compare:searchText options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch) range:NSMakeRange(0, [searchText length])]; if (result == NSOrderedSame) { NSLog(@"Adding role.name '%@' to searchResults as it begins with search text '%@'", role.name, searchText); [self.searchResults addObject:role]; } } } } |
Finally add these two additional small methods to RolesTVC.m:
#pragma mark - #pragma mark UISearchDisplayController Delegate Methods - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString { [self filterContentForSearchText:searchString scope:@"All"]; return YES; } - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption { [self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:@"All"]; return YES; } |
Note that I haven’t shown you how to configure search scopes or anything fancy. I didn’t want to show too much at once. If you’re comfortable with the basics, check out Apple’s TableSearch project to learn more.
Run the app now and you should be able to filter the Roles using the Search Bar:
_______
That’s it!
Here’s the complete source code so far
If you liked this tutorial or found something wrong with it please let me know!
If you want to support my work and have an iPad please consider purchasing iSoccer *wink*
-Tim
Go to Part 8 or the Tutorials Index











Abhishek Sonawane
March 5, 2012 at 9:16 pm
Awesome tutorial Tim!!
Amazing and super easy!!
Cheers,
Abhishek.
John Hebebrand
March 4, 2013 at 4:16 am
Although most all of these Core Data tutorials are spot on, the search code does not function as described. Seong got me most of the way there…selecting and segueing to the detail object. However when multiple items are returned with the search it always segues to the first item in the list. Has anyone figured out the search example and can you post the code? Thanks John
Graham Gardiner
April 6, 2013 at 5:12 pm
Hi john Hebebrand Did you work out what was to be done in the code to sort the problem of sending the top or first item on the search list? I have been trying to work it out. Please let me know if you have worked this problem out. Tim thank you for your great tutorials.
Adrian
March 6, 2012 at 1:12 pm
Hi Tim
Great tutorial pal. Very useful man. I see you changed the mobile theme on your web site. Pretty cool, pretty cool indeed. I like the way you use the pre loaded data in core data example bro. There are so many tuts in core data in ios 5 out there but are mostly generic stuff right out of apple docs , yours has a nice and easy approach. I hope everyone else enjoy following them as I do. Keep up the good work and be blessed.
Adrian
Maksanzhi
March 6, 2012 at 8:46 pm
Hi Tim,
Great tutorial!
I am a beginner in ios,
can you make next tutorial: how to add to Favorites?
Thank you
Tim Roadley
March 7, 2012 at 8:19 pm
Add favourites? like in safari? no idea!!
Maksanzhi
March 11, 2012 at 6:56 am
I mean bookmarks ) sorry my english is very bad
Tim Roadley
March 11, 2012 at 8:22 am
I’m still unsure what you’re trying to do. You want to add an item from a tableview in to your safari bookmarks? I don’t think that’s possible? Do you have examples from google searches of what you want to do?
Christina
March 11, 2012 at 9:38 am
Hi Tim,
Tutorial looks to be JUST what I need for an app I have been struggling with in core data. Thanks, fellow Aussie!
Tim Roadley
March 11, 2012 at 8:49 pm
Glad you find them useful, good luck with you app!
-Tim
shibi
December 27, 2012 at 5:37 pm
thanks
santiago
March 21, 2012 at 10:39 pm
Hi Tim,
great Job. Many thanks. I have one doubt. You see that the table view cells with the results are diferent to your custom previous ones. Could they have the same format and funcionality (go to modify the role)?
Santi
Seong
March 23, 2012 at 4:26 pm
Hello Tim, I am a fan of yours.
I succeeded this searchbar work easily by ur kind help.
But when I touch a cell filtered, the segue to the detailview does not perform. Its untouchable.
Have I missed something?
Thank you again anyway.
Seong
March 25, 2012 at 7:15 pm
I ve got to know, the tableview that searchdisplaycontroller ceated is a new vanilla one so it doesnt hear anything other than just displaying data. So I ve tried to tell him to work ”didselectrow -> perfom the segue -> and show detailTVC with searchedresult’ . I should have told which data to show in preparing segue, so I tried to say if(seached), show searchedresult, else just show selected index row. But I ve failed to say ‘if(seached)’ condition. Maybe I m lost. Thank you as always.
Seong
March 27, 2012 at 2:12 am
At last! I found the way. First, I wanna say Thank you so much, Tim. I ve grown up by your tutorials.
This is how I succeed:
at
cellForRowAtIndexPath,UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];i.s. self. is the point. It makes the tableviewcells those searchdisplaycontroller made to look and to work just like the original tableviewcells that I made. Of course Segue is performed too.And when
prepareForSegue,if (searchResults)self.selectedDbPx = [self.searchResults objectAtIndex:indexPath.row];
else
self.selectedDbPx = [self.fetchedResultsController objectAtIndexPath:indexPath];
shows the exact detail view for the searched result cells.
Thata’s all that I needed!
It’s been so hard 3 days for me thou.
Than you again, Tim.
Seong
March 27, 2012 at 3:11 am
Sorry but the solution above I wrote missed something. not works properly. More investigation is needed.
santiago
March 27, 2012 at 4:58 am
Hi Seong,
I have tried many things and the only solution I have found is this:
cell = [[[NSBundle mainBundle] loadNibNamed:@”CCell” owner:self options:nil] objectAtIndex:0];
Although I am using StoryBoard I created a xib file to define the cell. In the tablevew I don’t define any prototipe cell.
The cell is never null …
I hope It is useful for you.
Freitas
October 22, 2012 at 3:46 am
Hello Tim,
I confess I’m also a bit lost in this situation.
someone already has a simple solution.
Tim Roadley
March 27, 2012 at 5:41 am
Seong,
Sorry I’ve not replied in a while, I have been so busy at work recently I haven’t had time! A quick search led me to this question on stack overflow. Have you tried that?
When I get a change I will look in to this issue more
Cheers
Seong
March 27, 2012 at 1:02 pm
Thank you Tim and santiago.
As always, Tim saved me.
//santiago ‘s help was very useful too:)
Finally it works, maybe.
using a hint used in this tutorial just what Tim suggested me.
at
cellForRowAtIndexPath,UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
is still doing great.
After that, like the tutorial, set a property of an
NSString *savedSearchTerm. and@synthsize savedSearchTerm;.and insert a line at,
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
[self setSavedSearchTerm:searchText];
......
And the last one thing,
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
[self setSavedSearchTerm:nil];
}
and that’s all.
Now
if (savedSearchTerm){
works, in preparing for Segue.
the savedSearchTerm does not save any search term though. :)
it looks not so beautiful, but it works yet :)
Thank you.
Zachary Fisher
May 31, 2012 at 7:47 am
Seong,
Thanks for helping figure this out. It has been giving me a headache for days… one question. When I use your code, it only really works if there is a single object in the search results. If, for example, I type in the character “c”, both c++ and chief financial officer are choices. No matter which one I select, it goes to the c++ detail. I am assuming this is because the indexpath.row info is not being passed in the searched table, so it can’t grab the second item. Did you notice this? Any thoughts on how to fix?
Thanks!
Zack
Rimusz
June 9, 2012 at 5:40 am
Zack,
I was able to fix that search problem using answer two form here http://stackoverflow.com/questions/9325103/how-to-segue-from-a-uisearchbardisplaycontroller-result-to-a-detailviewcontrolle
Basically not using the segue but opening the viewcontroller programatically.
Also Tim I would like to thank you for such brilliant tutorials
which saved me a lot of time.
charlesGray
April 4, 2012 at 10:21 am
very helpful tutorials … i like your ‘style’ of instruction … thanks
this tutorial would be much more helpful IF after the selection process you could actually ‘select’ one of the “cellsForRowAtIndexPath” and seque to the ‘edit’ tableView.
what’s next?
Tim Roadley
April 4, 2012 at 10:29 am
I understand the issue completely and am working on a solution. I’ll let you know when I have one.
Cheers
charlesGray
April 4, 2012 at 10:51 am
thanks for your timely reply and understanding ….
i’ll let you know if i craft a solution, before hearing from you
Chrys
May 25, 2012 at 12:53 pm
Any update on this? Not sure the best way to segue from a searched cell? …
Chrys
May 25, 2012 at 1:04 pm
Never mind, I posted without reading properly – Seong’s solution is awesome
charlesGray
April 4, 2012 at 10:22 am
follow-up requested
charlesGray
April 6, 2012 at 2:20 am
i have a continuing issue with adding the managedObjectContext obj setup in the appDelegate …
saw the same compilation error the first time i wrote your tutorial … but it ‘went away’ … unfortunately don’t know what i did to correct the problem
code is all correct, but i get a “Receiver type … forward declaration” error …
what causes this? and how do you make it happy ?
charlesGray
April 6, 2012 at 3:30 am
found the answer … F.Y.I.
by including the Stanford CoreDataTableViewController you get the import of CoreData/CoreData.h … until that point the ‘… forward declaration’ error is stubbornly present
http://stackoverflow.com/questions/8088030/cannot-create-a-managed-object-context-on-ios
thanks again for all you’re doing to further the education process for so many!
Tim Roadley
April 6, 2012 at 6:35 am
Great news that you found the fix so quickly, gotta love Stack Overflow :)
Tony Short
May 22, 2012 at 6:26 am
I have followed this tutorial and have found it most useful. My question to Tim or to his followers is this:
Rather than fetching from a search bar, could I have a Text Field and a button that allows the user to enter a search criteria followed by clicking submit to search?
The app that I’m aiming to create will have pre-loaded data and I need a fetch mechanism to allow the user to search on the data.
Thank You
Tony
zav
June 23, 2012 at 4:28 am
Great, but what do we do now if we want to move the search bar out of the tableView and into its own view so it doesn’t scroll away?
It’s not exactly smooth sailing in the storyboard or in restructuring the code.
Any advice would be appreciated.
Jack
August 11, 2012 at 11:53 pm
@zav thats what I’d like to know
Reno Chong
June 26, 2012 at 6:31 pm
Hey, I would like to ask how you configure the cancel function on the search bar but when i look at the Roles.m file, I could not find any cancel method for the search bar. Hope to hear from you soon.
Thank you.
Cuong Duong
June 29, 2012 at 3:10 pm
Hi Tim,
Thank you for your examples.
I’m a newer for Objective-C programming, your examples is very helpful for me.
I have a question for part 7, Why I can’t tab on search result for view detail? How do I for view detail when tab on seach result?
Thanks so much.
Cuong Duong
June 29, 2012 at 3:11 pm
Hi Tim,
Thank you for your examples.
I’m a newer for Objective-C programming, your examples is very helpful for me.
I have a question for part 7, Why I can’t tab on search result for view detail? How do I for view detail when tab on search result?
Thanks so much.
Johny Mnemonic
October 4, 2012 at 6:20 am
Tim, let the power of Universe be with you ! ! !
Cheers from Ukrainian developer !
Leandro Davi de Freitas
October 22, 2012 at 5:37 am
Hello Cuong Duong and Seong,
for those who are still having the problem.
I got the solution I found was the following:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == self.searchDisplayController.searchResultsTableView)
{
RoleDetailTVC *nv = [self.storyboard instantiateViewControllerWithIdentifier:@"identifierName"];//click on the storyboard view and put that name as an identifier
nv.delegate = self;
nv.managedObjectContext = self.managedObjectContext;
self.selectedRole = [self.searchResults objectAtIndex:indexPath.row];
nv.role = self.selectedRole;
[self.navigationController pushViewController:nv animated:YES];
}
}
I have helped.
JP
January 12, 2013 at 4:20 am
Hi Leandro,
That doesn’t seem to work for me. It opens it up but doesn’t display the selected role, also it adds to the stack somehow so when clicking the back button you have to do it twice.
Hi Tim,
I’ve downloaded the completed code after section 8, and in the completed code the search doesn’t work either since from a search you can’t actually go to the detail. Has anyone found the suggested solution?
Thanks
Chen Lala
January 13, 2013 at 11:32 am
Hi Tim if I use sql to be database , how do I to connect with XXX.jpg file ? My purpose is to write [Hotel App] so I need to show hotel photo. Do you have any suggestion about framework! thanks for your respond.