I'm using UISearchResultsController
to filter data from a fetchedResultsController
. Here is the relevant code:
In RootVieweController.h
:
@interface RootViewController : UITableViewController <NSFetchedResultsControllerDelegate, AdditionViewControllerDelegate, UISearchBarDelegate, UISearchDisplayDelegate> {
NSArray *filteredListContent;
NSString *savedSearchTerm;
NSInteger savedScopeButtonIndex;
BOOL searchIsActive;
}
@property (nonatomic, retain) NSArray *filteredListContent;
@property (nonatomic, copy) NSString *savedSearchTerm;
@property (nonatomic) NSInteger savedScopeButtonIndex;
@property (nonatomic) BOOL searchIsActive;
In RootViewController.m
:
-(void)viewDidLoad {
[snip]
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 300, 40)];
searchBar.delegate = self;
searchBar.scopeButtonTitles = [NSArray arrayWithObjects:@"Scope 1", @"Scope 2", nil];
[searchBar sizeToFit];
searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
self.tableView.tableHeaderView = searchBar;
[searchBar release];
[self.tableView setContentOffset:CGPointMake(0, 40)];
UISearchDisplayController *searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
[self performSelector:@selector(setSearchDisplayController:) withObject:searchDisplayController];
[searchDisplayController setDelegate:self];
[searchDisplayController setSearchResultsDataSource:self];
[searchDisplayController setSearchResultsDelegate:self];
[searchDisplayController release];
self.filteredListContent = [NSMutableArray arrayWithCapacity:[[[self fetchedResultsController] fetchedObjects] count]];
if (self.savedSearchTerm) {
[self.searchDisplayController setActive:self.searchIsActive];
[self.searchDisplayController.searchBar setSelectedScopeButtonIndex:self.savedScopeButtonIndex];
[self.searchDisplayController.searchBar setText:savedSearchTerm];
self.savedSearchTerm = nil;
}
}
- (NSInteger)tableView:(UITableView *)theTableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
if (theTableView == self.searchDisplayController.searchResultsTableView) {
NSLog(@"Search Cells: %i", [self.filteredListContent count]);
return [self.filteredListContent count];
}
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
NSLog(@"Normal cells: %i", [sectionInfo numberOfObjects]);
return [sectionInfo numberOfObjects];
}
-(void)configureCell:(CustomTableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
//Called from cellForRowAtIndexPath
Object *object = nil;
if (self.searchIsActive) {
object = [[self filteredListContent] objectAtIndex:[indexPath row]];
} else {
object = [fetchedResultsController objectAtIndexPath:indexPath];
}
[snip]
}
#pragma mark -
#pragma mark Search functions
-(void)filterContentForSearchText:(NSString *)searchText scope:(NSString *)scope {
if ([scope isEqualToString:@"Scope 1"]) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"scope1 BEGINSWITH[cd] %@", searchText];
self.filteredListContent = [[[self fetchedResultsController] fetchedObjects] filteredArrayUsingPredicate:predicate];
} else {
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"scope2 BEGINSWITH[cd] %@", searchText];
self.filteredListContent = [[[self fetchedResultsController]fetchedObjects] filteredArrayUsingPredicate:predicate];
}
}
#pragma mark -
#pragma mark UISearchDisplayController Delegate Methods
- (void)searchDisplayController:(UISearchDisplayController *)controller willShowSearchResultsTableView:(UITableView *)theTableView {
NSLog(@"Showing search results");
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self filterContentForSearchText:searchString scope:
[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:
[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
NSLog(@"Reloading for string");
return YES;
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
[self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:
[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption]];
NSLog(@"Reloading for scope");
return YES;
}
-(void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
self.searchDisplayController.searchResultsTableView.rowHeight = 55;
self.searchIsActive = YES;
}
-(void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller {
self.searchIsActive = NO;
}
This works great in the simulator. But on a device, it crashes on object = [[self filteredListContent] objectAtIndex:[indexPath row]];
from configureCell
when trying to show the searchResultsTableView. I get the error [NSMutableArray objectAtIndex:]: index 7 beyond bounds for empty array
. Always index 7. What am I missing here?
--UPDATE--
Fixed: flipped the searchIsActive
BOOL
to switch on searchDisplayControllerDidBeginSearch
instead of searchDisplayControllerWillBeginSearch
and searchDisplayControllerWillEndSearch
instead of searchDisplayControllerDidEndSearch
. That keeps the table from trying to configure cells that don't exist. No clue why the Simulator didn't catch this