Search Your Table's Data
Adding search functionality to your UITableView
can be achieved using the built-in UISearchBar
object and implementing the UISearchBarDelegate
protocol. I am going to take us through an example that searches an Article
instance (data object) properties for the users' entered search string, displaying those articles that match. Each article object corresponds to a record in Core Data; this isn't needed for this example, any data structure would be fine. For the sake of fullness, the Article
class is listed below:
@interface Article : NSManagedObject {
}
@property (nonatomic, retain) NSNumber * boolFavorite;
@property (nonatomic, retain) NSString * strResourceName;
@property (nonatomic, retain) NSString * strExtension;
@property (nonatomic, retain) NSString * strArticleTitle;
@property (nonatomic, retain) NSString * strDescription;
@property (nonatomic, retain) NSString * strImageName;
@end
Back to the search functionality:
.h
@interface ArticlesViewController : UIViewController {
NSArray *dataSource;
NSMutableArray *tableData;
UITableView *tableView;
}
@end
So in the above code snippet, we have the original (full) dataset - dataSource
. tableData
is the data that will be updated to show those that match the user's search string, and the tableView that will be updated itself. Important to note at this stage that this class is not a UITableViewController
, so the datasource and delegate methods are in another class.
I haven't used a xib
in this example, so that you can see all the source code needed and follow all the connections made in the one file. Below, I shall show the snippets of code necessary to add search functionality to an already existing tableview setup.
.m
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if ([self.view viewWithTag:kTableViewTag] == nil) { //don't redraw if view already exists
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 35)];
searchBar.delegate = self;
[self.view addSubview:searchBar];
tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, (searchBar.frame.size.height + searchBar.frame.origin.y), self.view.frame.size.width, (self.view.frame.size.height - (searchBar.frame.size.height + searchBar.frame.origin.y))) style:UITableViewStylePlain];
tableView.tag = kTableViewTag;
tableView.backgroundColor = [UIColor clearColor];
dataSource = [[CoreDataAccess getRecordsForTable:@"Article"] retain];
tableData = [dataSource mutableCopy];
ArticleTableController *tableViewController = [[ArticleTableController alloc] initWithDataSet:tableData];
tableView.dataSource = tableViewController;
tableView.delegate = tableViewController;
[self.view addSubview:tableView];
[tableView release];
}
}
Of note in the above is the setup of the UISearchBar
:
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 35)];
searchBar.delegate = self;
[self.view addSubview:searchBar];
Nothing too complicated in the above code snippet; we are creating a UISearchBar
, giving it a frame, setting its delegate, and adding it to the overall view.
The next important section is below:
dataSource = [[CoreDataAccess getRecordsForTable:@"Article"] retain];
tableData = [dataSource mutableCopy];
ArticleTableController *tableViewController = [[ArticleTableController alloc] initWithDataSet:tableData];
In the above, I am getting my article objects, i.e. tableviews content from the Core Data, setting up my search array - tableData
with a mutable copy of the original dataset and passing this mutable dataset as the table's content. This is an important step, if you pass the original dataset, then when you search, you won't see any change to the tableview, as it is this tableData that we will be updating. CoreDataAccess
is a set of helper methods that I've created to simplify retrieving data through CoreData. It can be downloaded here
Time for the UISearchBarDelegate
methods:
#pragma mark -
#pragma mark UISearchBarDelegate
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
searchBar.showsCancelButton = YES;
searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
//empty previous search results
[tableData removeAllObjects];
}
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
searchBar.showsCancelButton = NO;
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
//empty previous search results
[tableData removeAllObjects];
if([searchText isEqualToString:@""] || searchText==nil){
//show original dataset records
[tableData addObjectsFromArray:dataSource];
[tableView reloadData];
}else {
for(Article *art in dataSource){
NSRange foundInTitle = [[art.strArticleTitle lowercaseString] rangeOfString:[searchText lowercaseString]];
if(foundInTitle.location != NSNotFound){
[tableData addObject:art];
}else {
NSRange foundInDescription = [[art.strDescription lowercaseString] rangeOfString:[searchText lowercaseString]];
if(foundInDescription.location != NSNotFound){
[tableData addObject:art];
}
}
}
[tableView reloadData];
}
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
[tableData removeAllObjects];
[tableData addObjectsFromArray:dataSource];
[tableView reloadData];
[searchBar resignFirstResponder];
searchBar.text = @"";
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[searchBar resignFirstResponder];
}
Using the above methods, you produce a case-insensitive search on Article
object's two properties - strDescription
and strArticleTitle