Please note, this blog entry is from a previous course. You might want to check out the current one.
Let the user search inside the tags table view. This is a non-trivial extra credit exercise, but you’ll definitely want to use NSSearchBarController.
During a search we will adjust the predicate of the fetched results controller using a new private property:
@property (nonatomic, strong) NSPredicate *searchPredicate; ... @synthesize searchPredicate = _searchPredicate; ... - (void)setupFetchedResultsController { ... request.predicate = self.searchPredicate; ... }
To save screen space we initialize the search only when a search button in the navigation toolbar is pressed:
@property (nonatomic, strong) IBOutlet UIBarButtonItem *searchButton; ... @synthesize searchButton = _searchButton; ... - (UIBarButtonItem *)searchButton { if (!_searchButton) { _searchButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSearch target:self action:@selector(searchButtonPressed:)]; } return _searchButton; } ... - (void)viewDidLoad { [super viewDidLoad]; [VacationHelper openVacation:self.vacation usingBlock:^(BOOL success) { [self setupFetchedResultsController]; self.navigationItem.rightBarButtonItem = self.searchButton; }]; }
When this button gets pressed the search bar is displayed the header of the table view. If it gets pressed again, it is removed. In addition the search predicate is reset.
- (IBAction)searchButtonPressed:(id)sender { if (self.tableView.tableHeaderView) { self.tableView.tableHeaderView = nil; } else { self.tableView.tableHeaderView = self.searchBar; if (self.searchPredicate) { self.searchPredicate = nil; [self setupFetchedResultsController]; } } }
The search bar is initialized using lazy instantiation, setting the search display controller at the same time. Setting self as searchResultsDelegate and searchResultsDataSource reuses the existing table methods. In addition the table view controller needs to be the delegate of the search display controller to actually start and run a search:
@interface TagsTableViewController () <UISearchDisplayDelegate> ... @property (nonatomic, strong) UISearchBar *searchBar; @property (nonatomic, strong) UISearchDisplayController *searchDisplayController; ... @synthesize searchBar = _searchBar; @synthesize searchDisplayController; ... - (UISearchBar *)searchBar { if (!_searchBar) { _searchBar = [[UISearchBar alloc] initWithFrame:self.navigationController.navigationBar.frame]; self.searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:_searchBar contentsController:self]; self.searchDisplayController.searchResultsDelegate = self; self.searchDisplayController.searchResultsDataSource = self; self.searchDisplayController.delegate = self; } return _searchBar; }
As mentioned above the search is done using the existing fetched results controller by adjusting its predicate:
- (BOOL) searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString { if ([searchString isEqualToString:@""]) self.searchPredicate = nil; else { self.searchPredicate = [NSPredicate predicateWithFormat:@"name like %@", [@"*" stringByAppendingString:[searchString stringByAppendingString:@"*"]]]; } [self setupFetchedResultsController]; return YES; }
When a search is finished (when the user clicks on “cancel”) we remove the search field:
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller { self.tableView.tableHeaderView = nil; }
The complete code for this task is available at github.