The application starts out with a Core Data model consisting of Products and Categories. The classes are generated using mogenerator.
The products are displayed in a UITableViewController using custom cells.
First, we want to add a search bar to the header of our table view:
- (void)setupSearchBar {
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)];
self.tableView.tableHeaderView = self.searchBar;
// scroll just past the search bar initially
CGPoint offset = CGPointMake(0, self.searchBar.frame.size.height);
self.tableView.contentOffset = offset;
}
Now we just call this method in viewDidLoad as well as setting up a new NSMutableArray for holding the results of a search.
- (void)viewDidLoad {
[super viewDidLoad];
[self setupSearchBar];
self.searchResults = [NSMutableArray array];
}
// in setupSearchBar
self.searchController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar
contentsController:self];
self.searchController.searchResultsDataSource = self;
self.searchController.searchResultsDelegate = self;
self.searchController.delegate = self;
Now that our controller is now responding to two different delegate & dataSource we need to update the places where we responding and verify which tableView we are responding for:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.tableView) {
id sectionInfo = [self.fetchedResultsController sections][section];
return [sectionInfo numberOfObjects];
} else {
return self.searchResults.count;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
ProductCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[ProductCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
Product *product = nil;
if (tableView == self.tableView) {
product = (Product *)[self.fetchedResultsController objectAtIndexPath:indexPath];
} else {
product = [self.searchResults objectAtIndex:indexPath.row];
}
cell.product = product;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (!self.detailViewController) {
self.detailViewController = [[DetailViewController alloc] initWithNibName:@"DetailViewController" bundle:nil];
}
Product *product = nil;
if (tableView == self.tableView) {
product = (Product *)[self.fetchedResultsController objectAtIndexPath:indexPath];
} else {
product = [self.searchResults objectAtIndex:indexPath.row];
}
self.detailViewController.detailItem = product;
[self.navigationController pushViewController:self.detailViewController animated:YES];
}
Now we need to actually do the searching when the user has typed in a search term. To do this, we simply respond to a delegate method:
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self filterProductsForTerm:searchString];
return YES;
}
And then we need to filter the products that match that string, stuffing the results into our array from before:
- (void)filterProductsForTerm:(NSString *)term {
[self.searchResults removeAllObjects];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
fetchRequest.entity = [Product entityInManagedObjectContext:[self managedObjectContext]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name contains[cd] %@ or category.name contains[cd] %@", term, term];
fetchRequest.predicate = predicate;
NSError *error = nil;
NSArray *results = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];
if (error) {
[NSException raise:NSGenericException format:@"Error filtering for term: %@ -- %@", term, error];
}
[self.searchResults addObjectsFromArray:results];
}