How To Add Search Bar in Table View
Update: If you’re developing on Xcode 5 and iOS 7, check out the updated tutorial here.
One common questions I got about UITableView is how to implement a search bar for data searching. This tutorial will show how to add a search bar to the Tab Bar project. With the search bar, the app lets users search through the recipe list by specifying a search term.
Well, it’s not difficult to add a search bar but it takes a little bit of extra work. We’ll continue to work on the Xcode project we developed in the previous tutorial. If you haven’t gone through the tutorial, take some time to check it out or you can download the project here.
Understanding Search Display Controller
You can use search display controller (i.e. UISearchDisplayController class) to manage search in your app. A search display controller manages display of a search bar and a table view that displays the results of a search of data.
When a user starts a search, the search display controller will superimpose the search interface over the original view and display the search results. Interestingly, the results are displayed in a table view that’s created by the search display controller.
Like other view controllers, you can either programmatically create the search display controller or simply add it into your app using Storyboard. In this tutorial, we’ll use the later approach.
Adding a Search Display Controller in Storyboard
In Storyboard, drag and add the “Search Bar and Search Display Controller” right below the navigation bar of Recipe Book View Controller. If you’ve done correctly, you should have a screen similar to the below:
Before proceeding, try to run the app and see how it looks. Without implementing any new code, you already have a search bar. Tapping the search bar will bring you to the search interface. Yet, the search won’t give you the correct search result.
We Did Nothing but Why Search Results Show All Recipes?
As mentioned earlier, the search results are displayed in a table view created by the search display controller. When developing the table view app, we implement the UITableViewDataSource protocol to tell Table View how many rows to display and the data in each row.
Like UITableView, the table view created by the search display controller adopts the same approach. By referring to the official documentation of UISearchDisplayController, here are the available delegates that let you interact with the search result and search bar:
The search results table view’s data source.
This object is responsible for providing the data for the results table.The search results table view’s delegate.
This object is responsible for, amongst other things, responding to the user’s selection of an item in the results table.The search display controller’s delegate.
The delegate conforms to the UISearchDisplayDelegate protocol. It is notified of events such as when the search starts or ends, and when the search interface is displayed or hidden. As a convenience, it may also be told about changes to the search string or search scope, so that the results table view can be reloaded.The search bar’s delegate.
This object is responsible for responding to changes in the search criteria.
Typically, the original view controller is used as the source object for the search results data source and delegate. You do not need to manually link up the data source and delegate with the view controller. As you insert the search bar into the view of Recipe Book View Controller, the appropriate connections to the search display controller are automatically configured. You can press the “control” key and click on the “Search Display Controller” to reveal the connections.
Both table views (i.e. the table view in Recipe Book View Controller and the search result table view) shares the same view controller to handle data population. If you refer to the code, these are the two methods being invoked when displaying table data:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [recipes count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *simpleTableIdentifier = @"RecipeCell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier]; } cell.textLabel.text = [recipes objectAtIndex:indexPath.row]; return cell; } |
This explains why the search results show a full list of recipes regardless of your search term.
Implementing Search Filter
Obviously, to make the search works, there are a couple of things we have to implement/change:
- Implement methods to filter the recipe names and return the correct search results
- Change the data source methods to differentiate the table views. If the tableView being passed is the table view of Recipe Book View Controller, we show all recipes. On the other hand, if it’s a search result table view, only the search results are displayed.
First, we’ll show you how to implement the filter. Here we have an array to store all recipes. We create another array to store the search results. Let’s name it as “searchResults”.
1 2 3 4 |
@implementation RecipeBookViewController { NSArray *recipes; NSArray *searchResults; } |
Next, add a new method to handle the search filtering. Filtering is one of the common tasks in iOS app. The straightforward way to filter the list of recipes is to loop through all the names and filter the result with the if-statement. There is nothing wrong with such implementation. But iOS SDK offers a better way known as Predicate to handle search queries. By using NSPredicate (which is an object representation of a predicate), you write less code. With just two lines of code, it searches through all recipes and returns the matched results.
1 2 3 4 5 6 7 8 |
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope { NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:@"SELF contains[cd] %@", searchText]; searchResults = [recipes filteredArrayUsingPredicate:resultPredicate]; } |
Basically, a predicate is an expression that returns a Boolean value (true or false). You specify the search criteria in the format of NSPredicate and use it to filter data in the array. NSArray provides filteredArrayUsingPredicate: method which returns a new array containing objects that match the specified predicate. The SELF keyword in the predicate “SELF contains[cd] %@” refers to the objects being evaluated (i.e. recipes). The operator “[cd]” means the comparison is case- and diacritic-insensitive.
Implementing Search Display Controller Delegate
Now we’ve created a method to handle the data filtering. But when should it be called? Apparently the filterContentForSearchText: method is invoked when user keys in the search term. The UISearchDisplayController class comes with a shouldReloadTableForSearchString: method which is automatically called every time the search string changes. So add the following method in the RecipeBookViewController.m:
1 2 3 4 5 6 7 8 9 10 |
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString { [self filterContentForSearchText:searchString scope:[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]]; return YES; } |
Display Search Results in searchResultsTableView
As explained earlier, we have to change the data source methods as to differentiate the table views (i.e. the table view in Recipe Book View Controller and the search result table view). It’s pretty easy to differentiate the table view. We simply compare the tableView object against the “searchResultsTableView” of the “searchDisplayController”. If the comparison is positive, we display the search results instead of all recipes. Here are the changes of the two methods:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if (tableView == self.searchDisplayController.searchResultsTableView) { return [searchResults count]; } else { return [recipes count]; } } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *simpleTableIdentifier = @"RecipeCell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier]; } if (tableView == self.searchDisplayController.searchResultsTableView) { cell.textLabel.text = [searchResults objectAtIndex:indexPath.row]; } else { cell.textLabel.text = [recipes objectAtIndex:indexPath.row]; } return cell; } |
Test the App Again
Once you complete the above changes, test your app again. Now the search bar should work properly!
Handling Row Selection in Search Results
Despite the search works, it doesn’t respond to your row selection. We want to make it work like the recipe table view. When user taps on any of the search results, it’ll transit to the detail view displaying the name of selected recipe.
Earlier, we use Segue to link up the recipe table cell and detail view. (If you forgot how it was done, revisit the Segue tutorial to see how it works.)
Obviously, we have to create another Segue in Storyboard to define the transition between the search result and detail view. The problem is we can’t do that. The search result table view is a private variable of search display controller. It’s impossible to handle the row selection of the search results using Storyboard.
The search display table view, however, lets you interact with the user’s selection of the results table by using a delegate. When the user selects a row, the didSelectRowAtIndexPath: method will be called. Therefore, what we have to do is to implement the method:
1 2 3 4 5 6 |
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (tableView == self.searchDisplayController.searchResultsTableView) { [self performSegueWithIdentifier: @"showRecipeDetail" sender: self]; } } |
We simply invoke the performSegueWithIdentifier: method to manually trigger the “showRecipeDetail” segue. Before proceeding, try to run the app again. When you select any of the search results, the app shows the detail view with a recipe name. But the name is not always correct.
Referring to the prepareForSegue: method, we use the “indexPathForSelectedRow” method to retrieve the indexPath of the selected row. As mentioned earlier, the search results are displayed in a separate table view. But in our original prepareForSegue: method, we always retrieve the selected row from the table view of Recipe Book View Controller. That’s why we got the wrong recipe name in detail view. To make the selection of search results properly, we have to tweak the prepareForSegue: method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([segue.identifier isEqualToString:@"showRecipeDetail"]) { RecipeDetailViewController *destViewController = segue.destinationViewController; NSIndexPath *indexPath = nil; if ([self.searchDisplayController isActive]) { indexPath = [self.searchDisplayController.searchResultsTableView indexPathForSelectedRow]; destViewController.recipeName = [searchResults objectAtIndex:indexPath.row]; } else { indexPath = [self.tableView indexPathForSelectedRow]; destViewController.recipeName = [recipes objectAtIndex:indexPath.row]; } } } |
We first determine if the user is using search. When searching, we retrieve the indexPath from searchResultsTableView, which is the table view for search results. Otherwise, we just get the indexPath from the table view of Recipe Book View Controller.
That’s it. Run the app again and the search selection should work as expected.
Tell Us What You Think
Hope you find this tutorial useful. If you like the tutorial and have any ideas about future tutorial, leave us comment and tell us what you think. We need your support to keep publishing free tutorials 🙂
Update: You can now download the full source code for your reference.
Comments
Keane Kwa
AuthorThanks, great tutorial.
Nazmul
AuthorGreat tutorial. I shall be grateful if you could make one tutorial for how to consume web service. Thanks!
Simon Ng
AuthorThanks for your suggestion. I’ll put it in my to-do list. 🙂
Simon Ng
AuthorThanks for your suggestion. I’ll put it in my to-do list. 🙂
Anonymous
AuthorCould you make a tutorial where you can search the valueForKey:@”info” (for example) of every cell in your table view… In other words, can you make it so you can search a cell’s array instead of its title? Thanks.
Jake
Authorgreat tutorial this was really helpful
halunke
AuthorGreat tutorial as always! I would like to see something about further DetailView customizations (like images, lists, geo-location, weather or so) all in relation to what list-item you selected.
Or something about CoreData 🙂
Fredrik
AuthorReally good tutorial! Thanks!
Bubumuk
AuthorGreat tutorial. Thanks!
I’d like to see something about CoreData and WebServices too like @7116a3171a2725aed8c83acd95a411cb:disqus and @fd7aca0e82906fbbf517913ee5a91232:disqus said.
Simon Ng
AuthorThanks for the suggestions. I’ll definitely cover both topics in the advanced tutorials.
Simon Ng
AuthorThanks for the suggestions. I’ll definitely cover both topics in the advanced tutorials.
Divz
AuthorGreat tutorial.This was really helpful.Thanks..
Bubumuk
AuthorHey, what do you think about a tutorial about pagination of the tableView?
Thanks.
James
AuthorExcellent tutorial. I got a challenge for you. Instead of displaying the name in the detail view display a description and image. So like an image of egg benedict and a description of it;)
Simon Ng
AuthorLater I’ll definitely cover this part. But it shouldn’t be difficult to implement. I suggest to try it out yourself. In the detail view, add an image view and a label for the description. Then declare two instance variables in the detail view controller and link the variables with the UI elements. You can then pass the description and image to the detail view via segue. That’s very similar to the things we’ve done before.
James
AuthorThanks for the reply I will give it a shot:)
nancy
Authorplease i wan a your urgent help:
i wana add search bar that applied for all the application for example if i divide the recipe into two menu such as chicken and desserts how do i execute the search through these two table views….
in other words how to determine the scope of my search bar…
thank you sooo much in advance..
dar
AuthorAwsome man. Been doing all your tutorials, never done xCode before. Really clear and helpfull.
Sharaf
Authorgr8 and very simple … i don’t have to be super in coding .. just follow the instructions and at the end everything will be working like charm 😉 .. one small issue, it’s if you may specify where to copy and paste the code as .h .m and the name it will be easier for the rest … my self it worked out with me 100% .. Thank you so much
Mike
AuthorI cant get it to work, can someone send me the finished code?
Rodrigo Rojas
Authorx2
Nel Martins
AuthorGreat tutorial. I have a question about the Accessory when a search is performed. Is there a way to set the Accessory to Disclosure Indicator to have the search list and non-search list appear identical?
Todd
AuthorLike Mike below, I can’t get this to work either; entering text in the search bar does not display search results (the entire list remains in the view). I am on Xcode 4.5, and my guess is that 4.5 does not wire up the search widget automatically, at least not the same way. When I look at the connections on RecipeBookViewController, I don’t see all the same connections as shown in your screenshot (see my screenshot below).
Todd
Authorah ha! My mistake — I used the ‘Search Bar’ component, instead of the ‘Search Bar and Search Display’ component. Now with the correct controller in place, search works perfectly.
Simon Ng
AuthorCool! Glad to hear the problem got solved.
Shalin Shah
AuthorHow can I make a search bar in a listView instead of a tableView?
napolux
AuthorIs there a way to get from the search tableview the original indexPath of the cell in the “parent” tableview?
Diogo
AuthorI just didn’t understand why you create that method with the scope parameter if you are not using.
ASH
Authorhow to work with NSMutableArray? (that contains title and text for detailView – both are nsstring…)
how to make it filter titles?!!
Guest
AuthorI got a problem… after entering the word in search bar nothing changes….
ASH
AuthorI got a problem… after entering the word in search bar nothing changes. what to do?
Arseniy
Authorit just works 😉 thank you!
Jay Santos
AuthorGreat tutorial as always! Can you also have the recipe tweaked to be grouped as food kinds sections i.e. high calorie, low calorie, etc. and have the table display it with the header named as the sections. It’ll make this tutorial super complete.
Appsicode
AuthorWhen adding the Predicate. Im getting and error “use of undeclared identifier ‘filtercontentForSearchText” can someone help with with this please.
HelloWorlD!
Authorit means the predicate has not yet being executed,
just type the rest of the code and the problem should be gone
🙂
Me Gusta
AuthorThanks for the tutorial – from an iOS newbie
Malkit Singh
Authorthanks for useful tut….
tiendh
AuthorIt’s great
Mark Thien
AuthorHi Samuel, would you mind to provide us a download link for this project please? I got everything working except that when return from Detail View by tapping of upper left corner button, it goes back to the same Detail View screen then I have to tab again on the button to go back to the table view screen.
Michael Ozeryansky
AuthorThank you!
Basil Bourque
AuthorGreat tutorial. But it seems to have left out one step. The method:
– (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
…needs to be updated. Rather than a single line of code:
return [recipes count];
…it needs an IF() statement to test if we are in Search mode. If searching, return count of “searchResults” rather than count of “recipes”.
Complete method…
– (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [searchResults count];
} else {
return [recipes count];
}
}
Alan
AuthorExcellent tutorials, more please!!!
Simon Ng
AuthorThanks. Glad to hear that. You can check out all our tutorials using the below link. More are coming 🙂
http://104.131.120.244/ios-programming-course/
Erik Fisher
AuthorFirst of all; your tutorials are absolutely amazing! I discovered this site a couple of days ago and I’ve been sitting with it ever since!
A quick question popped up during this one. At the prepare ForSegue I am able to pass the title to the DetailView, but I am struggling to pass along the image I also have in the DetailView.
I was able to do it by changing the image names to be exactly the same as the recipeNames. That way I could use [UIImage imageNamed:[searchResults objectAtIndex:indexPath.row]];
But that doesn’t seem like a very good way of doing it..
Can you show me some coding magic on this topic?
Thanks again!
Erik Fisher
AuthorFirst of all; your tutorials are absolutely amazing! I discovered this site a couple of days ago and I’ve been sitting with it ever since!
A quick question popped up during this one. At the prepare ForSegue I am able to pass the title to the DetailView, but I am struggling to pass along the image I also have in the DetailView.
I was able to do it by changing the image names to be exactly the same as the recipeNames. That way I could use [UIImage imageNamed:[searchResults objectAtIndex:indexPath.row]];
But that doesn’t seem like a very good way of doing it..
Can you show me some coding magic on this topic?
Thanks again!
Simon Ng
AuthorYou may create a Recipe class with name and image name properties. Then you can pass the recipe object to the detail view controller. We have a tutorial about OOP. You may want to take a look: http://104.131.120.244/objective-c-object-oriented-programming-intro/
rohan
AuthorHey, great tutorial, but I get a SIGABRT error when I click on the tab I put the search bar in.
javifernandezr
AuthorVery Useful
mevdev
AuthorGreat tutorial. Thanks!
Lafayette Kirsi Noel
AuthorGood Day Simon! I got stuck up in Displaying search results in searchResultsTableView. When I tried running it and search something, the app terminates and a SIGABRT will occur under main in ” return UIApplicationMain(argc, argv, nil, NSStringFromClass (RecipeBookAppDelegate class])); “.
Im using this syntax of RMD from the previous tutorial’s comments
Recipe *thisRecipe=[recipes objectAtIndex:indexPath.row];cell.textLabel.text=thisRecipe.name;
in order to display the recipe names on tableview.. Im not quite sure if it’s connected to the problem in searching and displaying.
Giancarlo Leonio
AuthorAwesome tutorial Simon! Just was I needed. I made a list of resources on this topic of implementing a Search Bar in TableView. Other may find useful: http://www.verious.com/board/Giancarlo-Leonio/implementing-an-ios-search-bar-in-table-view
Khalid Mehmood Awan
Authorthings are getting a little difficult now 😀 ….
anyways, excellent work…
Paul Lau
AuthorGreat tutorial !! ^_^
Do you have any idea to customize the TableCell for UISearchDisplayController ? Like your Tutorial #4 – Customize Table View Cells for UITableView.
Many Thanks…
EA
AuthorIn cellForRowAtIndexPath, in the statement where you use searchResult array, you can manipulate the style of the cell.
//forcing the cell to have disclosure indicator like the tableView.
if (tableView == self.searchDisplayController.searchResultsTableView) {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.text = [searchResults objectAtIndex:indexPath.row];
Felix
AuthorHey,
I have some problems, it throws an error “can’t use in/contains operator with collection” when I try to write something into the searchbar.
Here’s my code:
//
// RecipeViewController.m
// CustomTableView
//
// Created by Simon on 1/1/13.
// Copyright (c) 2013 Appcoda. All rights reserved.
//
#import “RecipeViewController.h”
#import “Recipe.h”
@interface RecipeViewController ()
@end
@implementation RecipeViewController {
NSArray *recipes;
NSArray *searchResults;
}
– (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:@”SELF contains[cd] %@”,
searchText];
searchResults = [recipes filteredArrayUsingPredicate:resultPredicate];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
– (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
– (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.title = @”Minecraft IDs”;
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:@”Back” style:UIBarButtonItemStyleBordered target:nil action:nil];
[[self navigationItem] setBackBarButtonItem:backButton];
// Create recipe array
Recipe *recipe1 = [Recipe new];
recipe1.name = @”Mushroom Risotto”;
recipe1.detail = @”Delicious mushroom risotto made with vegetable broth, cream, and a variety of fresh vegetables. Serve as a side dish or filling main course.”;
recipe1.imageFile = @”mushroom_risotto.jpg”;
Recipe *recipe2 = [Recipe new];
recipe2.name = @”Egg Benedict”;
recipe2.detail = @”30 min”;
recipe2.imageFile = @”egg_benedict.jpg”;
Recipe *recipe3 = [Recipe new];
recipe3.name = @”Full Breakfast”;
recipe3.detail = @”20 min”;
recipe3.imageFile = @”full_breakfast.jpg”;
Recipe *recipe4 = [Recipe new];
recipe4.name = @”Hamburger”;
recipe4.detail = @”30 min”;
recipe4.imageFile = @”hamburger.jpg”;
Recipe *recipe5 = [Recipe new];
recipe5.name = @”Ham and Egg Sandwich”;
recipe5.detail = @”10 min”;
recipe5.imageFile = @”ham_and_egg_sandwich.jpg”;
recipes = [NSArray arrayWithObjects:recipe1, recipe2, recipe3, recipe4, recipe5, nil];
}
– (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark – Table view data source
– (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
– (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [searchResults count];
} else {
return [recipes count];
}
}
– (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @”Cell”;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell…
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if (tableView ==self.searchDisplayController.searchResultsTableView) {
Recipe *recipe = [searchResults objectAtIndex:indexPath.row];
UIImageView *recipeImageView = (UIImageView *)[cell viewWithTag:100];
recipeImageView.image = [UIImage imageNamed:recipe.imageFile];
UILabel *recipeNameLabel = (UILabel *)[cell viewWithTag:101];
recipeNameLabel.text = recipe.name;
UILabel *recipeDetailLabel = (UILabel *)[cell viewWithTag:102];
recipeDetailLabel.text = recipe.detail;
}
// Display recipe in the table cell
Recipe *recipe = [recipes objectAtIndex:indexPath.row];
UIImageView *recipeImageView = (UIImageView *)[cell viewWithTag:100];
recipeImageView.image = [UIImage imageNamed:recipe.imageFile];
UILabel *recipeNameLabel = (UILabel *)[cell viewWithTag:101];
recipeNameLabel.text = recipe.name;
UILabel *recipeDetailLabel = (UILabel *)[cell viewWithTag:102];
recipeDetailLabel.text = recipe.detail;
return cell;
}
/*
// Override to support conditional editing of the table view.
– (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
*/
/*
// Override to support editing the table view.
– (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
– (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/
/*
// Override to support conditional rearranging of the table view.
– (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
#pragma mark – Table view delegate
– (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
/*
*detailViewController = [[ alloc] initWithNibName:@”” bundle:nil];
// …
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
*/
}
@end
Yanet
AuthorYou have to put this in the search query (specify the attribute to search, ‘name’) :
SELF.name contains[cd] %@
Alsoto visualize the result have to put this (look the self before tableView):
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
rajohns
Authorjust in case anyone made the same mistake I did and clicking a search result always shows the same recipe detail:
i had already implemented the didSelectRowAtIndexPath method to deselect the option. If you did this, make sure you put the “[tableView deselectRowAtIndexPath:indexPath animated:YES];” line AFTER the performSegueWithIdentifier line. Otherwise the row will be deselected, and the indexPath will not evaluate to anything in the prepareForSegue function when the search display is active.
Yanet
AuthorIssue:
I’m working with a Customized Cell and the searchTableView is not picking those cells. What I mean is that I’m trying to change the background and size of the cells for the searchTableView so it looks more like my tableView before the search. However is not working. Any idea?
Htun Lin Aung
Authori have a search bar with two scope bar buttons. if i click the search button, i need to check which scope bar button was clicked.
How to check which button i clicked?
cyburt
AuthorGreat stuff! Helped me out a lot…
Henry D’Andrea
AuthorI keep getting a crash on line cell.textLabel.text = [recipes objectAtIndex:indexPath.row];
Any help?
Jacob
Authorperhaps the problem is that your recipes array is an array of dictionaries (something I would have done especially if I get the recipes through an api of some sort). If this is the case, you’re trying to set a dictionary in place of a string. try something like this: [[recipes objectAtIndex:indexPath.row] objectForKey:@”name”] where @”name” is the key of the name of the recipe in your dictionary.
I don’t think Xcode would complain pre-compile since an array returns an (id).
Hannan
Authorem facing a prblem that my search bar is displayed at the bottom of the page, can any one tell me abt this
Ali Alem
AuthorExcellent tutorial. Works perfectly for me. I have one question: I would like to put the UISearchBar in the tableview header like in the Mail app. The problem is I don’t have a reference to the search bar using this tutorial cuz it’s hooked to the search display controller. I tried self.tableview.header.view = self.uisearchdisplaycontroller.searchbar; but this crashed. Any ideas?
Marc
AuthorLoved it! thank you guys so much!
Paul
AuthorHi, i am also having an issue with the search bar for my customised ‘recipe’ as i have changed it to Fire Stations etc. I wonder if someone could spare some time to have a look at my code to see if they can identify the issue. As with some of the other comments, it kicks out the simulator and takes me to an error with SIGABRT at the following string;
return UIApplicationMain(argc, argv, nil, NSStringFromClass([RecipeBookAppDelegate class]));
Please be mindful that i have little to no programming knowledge and i am trying to teach myself. Any help would be greatly appreciated. Paul
//
// RecipeBookViewController.m
// RecipeBook
//
// Created by Simon Ng on 14/6/12.
// Copyright (c) 2012 Appcoda. All rights reserved.
//
#import “FireStationViewController.h”
#import “StationDetailViewController.h”
#import “Information.h”
@interface FireStationViewController ()
@end
@implementation FireStationViewController {
NSArray *stations;
NSArray *searchResults;
}
@synthesize tableView = _tableView;
– (void)viewDidLoad
{
[super viewDidLoad];
Information *station1 = [Information new];
station1.name = @”Alresford”;
station1.prepTime = @”36″;
station1.imageFile = @”36alresford.jpg”;
station1.ingredients = [NSArray arrayWithObjects:@”Pound Hill”, @”Alresford”, @”SO24 9BP”, @””, @”Watch Manager – P Roach”, @””, @”Drill night”, @”Monday 19.00 – 21.00″, nil];
Information *station2 = [Information new];
station2.name = @”Alton”;
station2.prepTime = @”05″;
station2.imageFile = @”05alton.jpg”;
station2.ingredients = [NSArray arrayWithObjects:@”Butts Road”, @”Alton”, @”Hampshire”, @”GU34 1LL”, @””, @”Watch Manager – I Craley”, @””, @”Drill night”, @”Thursday 18.30 – 21.30″, nil];
Information *station3 = [Information new];
station3.name = @”Andover”;
station3.prepTime = @”31″;
station3.imageFile = @””;
station3.ingredients = [NSArray arrayWithObjects:@”London Street”, @”Andover”,@”SP10 2PF”,@””,@”Station Manager – M White”,@””,@”Drill night”,@”Monday 18.30 – 21.30″, nil];
Information *station4 = [Information new];
station4.name = @”Basingstoke”;
station4.prepTime = @”01″;
station4.imageFile = @”01basingstke.jpg”;
station4.ingredients = [NSArray arrayWithObjects:@”West Ham Close”,@”Basingstoke”,@”RG22 6PH”,@””,@”Station Manager – D Graham”,@””,@”Drill night”,@”Tuesday 19.00 – 22.00″,@””, nil];
Information *station5 = [Information new];
station5.name = @”Beaulieu”;
station5.prepTime = @”49″;
station5.imageFile = @””;
station5.ingredients = [NSArray arrayWithObjects:@”Off High Street”,@”Beaulieu”,@”SO42 7YF”,@””,@”Watch Manager – R Knight”,@””,@”Drill night”,@”Monday 19.00 – 22.00″, nil];
Information *station6 = [Information new];
station6.name = @”Bishops Waltham”;
station6.prepTime = @”40″;
station6.imageFile = @”40bishopswaltham.jpg”;
station6.ingredients = [NSArray arrayWithObjects:@”Lower Lane”,@”Bishops Waltham”,@”SO32 1AS”,@””,@”Watch Manager – A J Smith”,@””,@”Drill night”,@”Monday 19.00 – 22.00″, nil];
Information *station7 = [Information new];
station7.name = @”Bordon”;
station7.prepTime = @”03″;
station7.imageFile = @”03bordon.jpg”;
station7.ingredients = [NSArray arrayWithObjects:@”Conde Way”,@”Bordon”,@”Hampshire”,@”GU35 0XF”, @””, @”Watch Manager – P Hoban”,@””,@”Drill night”,@”Thursday 18.45 – 21.45″, nil];
Information *station8 = [Information new];
station8.name = @”Botley”;
station8.prepTime = @”38″;
station8.imageFile = @””;
station8.ingredients = [NSArray arrayWithObjects:@”Winchester Street”,@”Botley”,@”Southampton”,@”Hampshire”,@”SO30 2EB”,@””,@”Watch Manager – M Woods”,@””, @”Drill night”,@”Tuesday 19.00 – 22.00″, nil];
Information *station9 = [Information new];
station9.name = @”Brockenhurst”;
station9.prepTime = @”50″;
station9.imageFile = @””;
station9.ingredients = [NSArray arrayWithObjects:@”Lyndhurst Road”,@”Brockenhurst”,@”SO42 7RL”,@””,@”Watch Manager – K Baker”,@””,@”Drill night”,@”Tuesday 19.30 – 21.30″, nil];
Information *station10 = [Information new];
station10.name = @”Burley”;
station10.prepTime = @”52″;
station10.imageFile = @””;
station10.ingredients = [NSArray arrayWithObjects:@”Ringwood Road”,@”Burley”,@”BH24 4BU”,@””,@”Watch Manager – T Gray”,@””,@”Drill night”,@”Monday 19.30 – 21.30″, nil];
Information *station11 = [Information new];
station11.name = @”Cosham”;
station11.prepTime = @”23″;
station11.imageFile = @””;
station11.ingredients = [NSArray arrayWithObjects:@”Wayte Street”,@”Cosham”,@”Portsmouth”,@”PO6 3BS”,@””,@”Station Manager – B Gordon”, nil];
Information *station12 = [Information new];
station12.name = @”Droxford”;
station12.prepTime = @”41″;
station12.imageFile = @””;
station12.ingredients = [NSArray arrayWithObjects:@”Union Lane”, @”Droxford”, @”Hampshire”, @”SO32 3QP”, @””, @”Watch Manager – K Sherfield”, @””, @”Drill night”, @”Monday 19.30 – 21.30″, nil];
Information *station13 = [Information new];
station13.name = @”Eastleigh”;
station13.prepTime = @”32″;
station13.imageFile = @””;
station13.ingredients = [NSArray arrayWithObjects:@”Steele Close”, @”Eastleigh”, @”SO53 3AA”, @””, @”Station Manager – S Ransley”, @””, @”Drill night”, @”Monday 19.00 – 20.00″, nil];
Information *station14 = [Information new];
station14.name = @”Emsworth”;
station14.prepTime = @”26″;
station14.imageFile = @””;
station14.ingredients = [NSArray arrayWithObjects:@”North Street”, @”Emsworth”, @”Hampshire”, @”PO10 7DD”, @””, @”Watch Manager – P Merry”, @””, @”Drill night”, @”Wednesday 19.00 – 21.00″, nil];
Information *station15 = [Information new];
station15.name = @”Fareham”;
station15.prepTime = @”17″;
station15.imageFile = @””;
station15.ingredients = [NSArray arrayWithObjects:@”Station Approach”, @”Fareham”, @”PO16 0HZ”, @””, @”Station Manager – P Coates”, @””, @”Drill night”, @”Tuesday 19.00 – 21.00″, nil];
Information *station16 = [Information new];
station16.name = @”Fleet”;
station16.prepTime = @”04″;
station16.imageFile = @””;
station16.ingredients = [NSArray arrayWithObjects:@”89 Connaught Road”, @”Fleet”, @”Hampshire”, @”GU51 3LP”, @””, @”Watch Manager – C Gregory”, @””, @”Drill night”, @”Monday 19.15 – 21.15″, nil];
Information *station17 = [Information new];
station17.name = @”Fordingbridge”;
station17.prepTime = @”47″;
station17.imageFile = @””;
station17.ingredients = [NSArray arrayWithObjects:@”Station Road”, @”Fordingbridge”, @”SP6 1JN”, @””, @”Watch Manager – P White”, @””, @”Drill night”, @”Wednesday 19.00 – 21.00″, nil];
Information *station18 = [Information new];
station18.name = @””;
station18.prepTime = @””;
station18.imageFile = @””;
station18.ingredients = [NSArray arrayWithObjects:@””, @””, @””, @””, @””, @””, @”Drill night”, @”Wednesday 19.00 – 21.00″, nil];
Information *station19 = [Information new];
station19.name = @””;
station19.prepTime = @””;
station19.imageFile = @””;
station19.ingredients = [NSArray arrayWithObjects:@””, @””, @””, @””, @””, @””, @”Drill night”, @”Wednesday 19.00 – 21.00″, nil];
Information *station20 = [Information new];
station20.name = @””;
station20.prepTime = @””;
station20.imageFile = @””;
station20.ingredients = [NSArray arrayWithObjects:@””, @””, @””, @””, @””, @””, @”Drill night”, @”Wednesday 19.00 – 21.00″, nil];
Information *station = [Information new];
station.name = @””;
station.prepTime = @””;
station.imageFile = @””;
station.ingredients = [NSArray arrayWithObjects:@””, @””, @””, @””, @””, @””, @”Drill night”, @”Wednesday 19.00 – 21.00″, nil];
stations = [NSArray arrayWithObjects:station1, station2, station3, station4, station5, station6, station7, station8, station9, station10, station11, station12, station13, station14, station15, station16, station17, station18, station19, station20, station, nil];
}
– (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
– (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
– (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [stations count];
}
– (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = @”RecipeCell”;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
Information *station = [stations objectAtIndex:indexPath.row];
cell.textLabel.text = station.name;
return cell;
}
– (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@”showRecipeDetail”]) {
StationDetailViewController *destViewController = segue.destinationViewController;
NSIndexPath *indexPath = nil;
if ([self.searchDisplayController isActive]) {
indexPath = [self.searchDisplayController.searchResultsTableView indexPathForSelectedRow];
destViewController.station = [searchResults objectAtIndex:indexPath.row];
} else {
indexPath = [self.tableView indexPathForSelectedRow];
destViewController.station = [stations objectAtIndex:indexPath.row];
}
}
}
– (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:@”SELF contains[cd] %@”,
searchText];
searchResults = [stations filteredArrayUsingPredicate:resultPredicate];
}
#pragma mark – UISearchDisplayController delegate methods
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
// Hide bottom tab bar in the detail view
// destViewController.hidesBottomBarWhenPushed = YES;
}
@end
don
AuthorHi,
Using the code below from Appcoda tutorial. Code is crashing at this line “self.searchResults = [[self.searchSet filteredArrayUsingPredicate:resultPredicate] mutableCopy];”
I have spent significant time but am unable to understand why it should crash. Search Results is working properly and is filtering the data.
// SEARCH BAR: Par1: Implement filter using NSPRedicate
– (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:@”SELF contains[cd] %@”,
searchText];
self.searchResults = [[self.searchSet filteredArrayUsingPredicate:resultPredicate] mutableCopy];
// NSLog(@”%u”,[searchResults count]);
}
//Search display controller delegate
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
//Table view controller
– (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
static NSString *simpleTableIdentifier = @”prototypecell”;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
searchSet =[object objectForKey:@”levelName”];
// NSLog(@”%@”,searchSet);
// Configure the cell
if (tableView == self.searchDisplayController.searchResultsTableView) {
NSLog(@”crash here”);
cell.textLabel.text = [searchSet mutableCopy];
}else {
cell.textLabel.text = [searchSet mutableCopy];
}
// cell.textLabel.text = [searchSet mutableCopy];
return cell;
}
Arthur
AuthorHey, can you maybe make a tutorial about how to index this recipe book? That would be great! Awesome tutorial!
Michael Mork
AuthorThank you…
CEMSOFT SOFTWARE
Authorthanks, great tutorial.
dd
AuthorHi,
I am trying to implement the code for selecting a cell, but the selection is not responding. The cell highlights but nothing happens. Thanks
Madhur
Authorerror:–
no visible @interface for uiviewcontroller declares the selector ‘filter content for search text’
Tam Vo
AuthorNice tutorial, thanks a lot
Tyra
Authorhi , the search bar works perfectly fine . but after i searched for a specific recipe , found it but i cant open it from the search bar . but when i select it from the table view , i can open it as always 🙁
Jacob
Authorthe problem is because you are retrieving two different cells depending on if its a search or if its the full list. Its similar to the problem I’m having. I can solve it, but I was looking for better ways of doing it (thats why im here).
My solution is to make an outlet for the table view. By now you already know how to make an outlet but if not, its something you should learn before working with tables.
then change:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
into :
UITableViewCell *cell = [_tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
where _tableView is your name you gave to the outlet (add the underscore). I haven’t tried it with segues but I don’t see any reason it shouldn’t work
Komokurono
AuthorI’m afraid this won’t help. Just tried it.
Sabir
AuthorAwesome………Thanks for this tutorial…………..
toshi
AuthorI had some issue. When I typed in the search bar, it is covered up and invisible.
How I fixed is put the search bar into the Table View layer. Now it works.
hupili
Author+1 . This is worth a note in the OP. Same issue and solution on XCode 5 and iOS 7. Without this tip, the searchbar is covered by navigation bar
toshi
Author#import “ViewController.h”
#import “detailViewController.h” // I realized it’s not good practice. The first letter shold be the upper case
@interface ViewController ()
{
NSMutableArray *_recipes;
NSString *_recipeName;
NSArray *_searchResults;
}
@end
@implementation ViewController
– (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.tableView.delegate = self;
self.tableView.dataSource = self;
_recipes = [NSMutableArray arrayWithObjects:@”Egg Benedict”,@”Mushroom Risotto”,@”Full Breakfast”, @”Hamburger”,@”Ham and Egg Sandwich”, @”Cream Brelee”,@”White Chocolate Donut”,@”Starbucks Coffee”,@”Vegetable Curry”, @”Instant Noodle With Egg”,@”Noodle with BBQ Pork”, @”Japanese Noodle with Pork”,@”Green Tea”, @”Thai shripm Cake”, @”Angry Birds Cake”,@”Ham and Cheese Panini”,nil];
}
– (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
# pragma mark Dlegate method
// Data source protocol
– (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(tableView == self.searchDisplayController.searchResultsTableView)
{
return [_searchResults count];
}
else
{
return [_recipes count];
}
}
– (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *simpleTableIdentifier = @”RecipeCell”;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
if(tableView == self.searchDisplayController.searchResultsTableView)
{
cell.textLabel.text = _searchResults[indexPath.row];
}
else
{
cell.textLabel.text = _recipes[indexPath.row];
}
NSLog(@”%@”,_searchResults);
return cell;
}
// Delegate Protocol
– (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//NSLog(@”row %i and section %i”,indexPath.row,indexPath.section);
if(tableView == self.searchDisplayController.searchResultsTableView)
{
_recipeName = _searchResults[indexPath.row];
}
else
{
_recipeName = _recipes[indexPath.row];
}
[self performSegueWithIdentifier:@”CellSelectionSegue” sender:self];
}
# pragma mark Segue method
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSLog(@”segue”);
detailViewController *detailVC = segue.destinationViewController;
detailVC.string = _recipeName;
NSLog(@”%@”,_recipeName);
}
# pragma mark Searchbar datasource
– (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:@”SELF contains[cd] %@”,
searchText];
_searchResults = (NSArray *)[_recipes filteredArrayUsingPredicate:resultPredicate];
//NSLog(@”%@”,_searchResults);
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
– (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption
{
[self filterContentForSearchText:self.searchDisplayController.searchBar.text
scope:[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption]];
return YES;
}
@end
This also works
chandankr
AuthorAwesome Tutorial!!
d_alifda
AuthorHi, this is a great tutorial. When I just started using Xcode 4.5 (storyboards mode) I succesfully applied this tutorial into my tableView (populated manually with NSArray of strings). But when I changed the tableView datasource into data from XML parser, the search display controller doesn’t work.
As I start typing a character into the search bar, the ‘transparent layer’ disappears and only showing the original tableView behind (as in your case, table view containing the array ‘recipes’, instead of ‘searchResults’).
The only changes I made were the assignment of the array ‘recipes’ on viewDidLoad method as follows:
xmlParser = [[XMLParser alloc] loadXMLByURL:@”someURL”];
recipes = [xmlParser allrecipes];
What could possibly be missing here? Still confused X_X.
P.S.: I got no problems with the XML parsing part since the tableView is populated correctly
Tiago De Barros Hillesheim
AuthorI’m confused with the method filtercontentForSearchText:
It’s a custom method since it’s not defined anywhere. So why is that “scope” parameter? it’s not used inside the method …
And the searchDisplayController:shouldReloadTableForSearchString: method doesn’t show up
in the code hint, which is very annoying.
anyway, worked for me in the end. thanks
Steven
AuthorGreat tutorial but is it possible to have a search bar to lookup the entire App contents rather than just the table view ?
shasvat
AuthorReally, if we follow all your steps application will work without a single warning…
vishnuvarthan
Authoryour code works great but the problem is when i manually insert a new data it appears in the table but search bar is not searching the newly inserted data .help !!!
Hiren Patel
AuthorHi,
Can i use this methods for UITextField?
And If not than How can I customize the view of UISearchBar by replacing it by my custom UITextfield?
Kritbovorn Taweeyossak
AuthorUISearchDisplayController , searchDisplayController is deprecated :first deprecated in iOS 8
can solve this error Please.
cindy
AuthorsearchDisplayController is deprecated in IOS 8! Someone please help 🙁
cocoiws
AuthorI have the same problem 🙁 any boby knows how to update it? tks
Eric
AuthorGreat tutorial but iOS 8.0 does not support “searchDisplayController” anymore..
anyplan to update this tutorial ?
Phil
Authorthanks Simon you made my day !
sazzadhusen iproliya
Authorgreat tutorial …………..helpfull as
Marq
Authoris there a swift version?
Charles Robertson
AuthorOne word. Awesome…
Son Go Han
AuthorHello. I followed this tutorial but when I text into the search bar, it couldn’t search anything like the picture below. I got this issue when using Xcode 9:
This is my source code:
//
// ViewController.m
// Accessories Manager
//
// Created by Hackintosh on 9/23/17.
// Copyright © 2017 Hackintosh. All rights reserved.
//
#import “ViewController.h”
#import “AccessoryDetailViewController.h”
@interface ViewController ()
@end
@implementation ViewController
{
NSArray *recipes;
NSArray *searchResults;
}
@synthesize tableView; // Add this line of code
– (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
recipes = [NSArray arrayWithObjects:@”Egg Benedict”, @”Mushroom Risotto”, @”Full Breakfast”, @”Hamburger”, @”Ham and Egg Sandwich”, @”Creme Brelee”, @”White Chocolate Donut”, @”Starbucks Coffee”, @”Vegetable Curry”, @”Instant Noodle with Egg”, @”Noodle with BBQ Pork”, @”Japanese Noodle with Pork”, @”Green Tea”, @”Thai Shrimp Cake”, @”Angry Birds Cake”, @”Ham and Cheese Panini”, nil];
}
– (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
– (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@”showAccessoryDetail”]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
AccessoryDetailViewController *destViewController = segue.destinationViewController;
destViewController.accessoryName = [recipes objectAtIndex:indexPath.row];
}
}
– (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:@”SELF contains[cd] %@”, searchText];
searchResults = [recipes filteredArrayUsingPredicate:resultPredicate];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString scope:[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
return YES;
}
– (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [searchResults count];
} else {
return [recipes count];
}
}
– (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *accessoryTableIdentifier = @”AccessoryCell”;
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:accessoryTableIdentifier];
if(cell == nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:accessoryTableIdentifier];
}
if (tableView == self.searchDisplayController.searchResultsTableView) {
cell.textLabel.text = [searchResults objectAtIndex:indexPath.row];
} else {
cell.textLabel.text = [recipes objectAtIndex:indexPath.row];
}
return cell;
}
– (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 71;
}
@end
Chinmay
AuthorProject is missing from dropBox https://dl.dropboxusercontent.com/u/2857188/RecipeBook_Search_Bar.zip
please add the project so that we can refer it.