Basically I'm trying to add a search function to my iPhone app, but I'm not having much luck at the moment. I've downloaded the Apple provided Table Search app, and have copied across the code to mine. It builds OK, but here's the problem. Unlike the Apple example, all my data is stored in an array, that is accessed by calling [ciParser.currentArray].
Any ideas on how to change the code to suit my needs? I'm getting an error "Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSCFArray objectAtIndex:]: index (0) beyond bounds (0)'" whenever I click on the search bar and the app exits. Below is the code in particular that the debugger highlights as being troublesome.
Apparently this error means the database trying to be searched is empty, but I could be wrong.
FYI my app downloads and parsers an RSS feed using a class which is referenced by ciParser - which in turn stores the downloaded content in an array that I need to search.
Update: It now seems my table isn't updating when it finds the information. Here's my code that should be doing that. Any ideas? Thanks.
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString scope:
[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption
{
[self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:
[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption]];
// Return YES to cause the search result table view to be reloaded.
Code for parsing:
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
/*
If the requesting table view is the search display controller's table view, configure the cell using the filtered content, otherwise use the main list.
*/
CurrentItem * nextCurrentItem=[ciParser.currentArray objectAtIndex:indexPath.row];
if (tableView == self.searchDisplayController.searchResultsTableView)
{
// **(Code which debugger points to as being wrong)**
nextCurrentItem = [self.filteredListContent objectAtIndex:indexPath.row];
}
else
{
nextCurrentItem = [ciParser.currentArray objectAtIndex:indexPath.row];
}
NSString*settingValue = [[NSUserDefaults standardUserDefaults]stringForKey:#"state"];
if ([settingValue isEqualToString:#"New South Wales"]) {
cell.textLabel.text=nextCurrentItem.title;
}
else if ([settingValue isEqualToString:#"Western Australia"]) {
cell.textLabel.text=#"The FESA does not provide any Current Incident reports.";
}
else if ([settingValue isEqualToString:#"Victoria"]) {
cell.textLabel.text=nextCurrentItem.title;
}
else if ([settingValue isEqualToString:#"South Australia"]) {
cell.textLabel.text=nextCurrentItem.title;
}
else if ([settingValue isEqualToString:#"Tasmania"]) {
cell.textLabel.text=nextCurrentItem.title;
}
// Set up the cell...
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
return cell;
}
Update: This is the section I don't think is right, as my search results now display blank, no matter what I type.
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
/*
Update the filtered array based on the search text and scope.
*/
[self.filteredListContent removeAllObjects]; // First clear the filtered array.
/*
Search the main list for products whose type matches the scope (if selected) and whose name matches searchText; add items that match to the filtered array.
*/
//**This part threw many errors and I eventually got it to this but I suspect this is my problem.**
for (currentItem in ciParser.currentArray)
{
if ([scope isEqualToString:#"All"] || [currentItem.title isEqualToString:scope])
{
NSComparisonResult result = [currentItem.title compare:searchText options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch) range:NSMakeRange(0, [searchText length])];
if (result == NSOrderedSame)
{
[self.filteredListContent addObject:currentItem];
}
}
}
}
You need to set breakpoints/logs in filterContentForSearchText:scope: to see where things are going wrong.
NSLog(#"searchText=%#",searchText);
NSLog(#"scope=%#",scope);
for (currentItem in ciParser.currentArray)
{
NSLog(#"currentItem=%#",currentItem);
if ([scope isEqualToString:#"All"] || [currentItem.title isEqualToString:scope])
{
NSLog(#"in ([scope isEqualToString:#"All"] || [currentItem.title isEqualToString:scope])");
NSComparisonResult result = [currentItem.title compare:searchText options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch) range:NSMakeRange(0, [searchText length])];
NSLog(#"result=%d",result);
if (result == NSOrderedSame)
{
NSLog(#"in (result == NSOrderedSame)");
[self.filteredListContent addObject:currentItem];
NSLog(#"self.filteredListContent=%#",self.filteredListContent);
}
}
}
This will give you a complete snap shot of the method in operation and tell you where the code is going wrong.
Did you update the code for 'numberOfRowsInSection' to return the number of filtered items when searching? If not, this sounds like your problem.
You need to implement:
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
And also:
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption
I recommend you start by focusing either on the search string or the search options. Even try implementing a function that just copies over each item to your filtered array and check if it works.
Related
Have a look at this video.
I get this glitch when running this code:
[NSFetchedResultsController deleteCacheWithName:#"Root"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"following == YES"];
[self.fetchedResultsController.fetchRequest setPredicate:predicate];
[self.fetchedResultsController performFetch:nil];
[self.tableView beginUpdates];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:THE_SECTION_WITH_THE_BLOGS] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
Why does the section header below the section I reload change it frame like this? Any solutions?
EDIT:
This is my viewForHeaderInSection:
(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
if (tableView != self.tableView)
return nil;
BLMenuTableSectionHeaderView *view = nil;
if (section != BLMenuSectionUser) {
view = [[BLMenuTableSectionHeaderView alloc] initWithFrame:CGRectZero];
if (section == BLMenuSectionFollowing){
if ([self blogCount] > 0){
[view setTitle:NSLocalizedString(#"Following", nil)];
[view addSubview:self.editButton];
}else{
self.tableView.editing = NO;
}
}
if (section == BLMenuSectionPosts){
[view setTitle:NSLocalizedString(#"Browse", nil)];
}
else if (section == BLMenuSectionSettings)
[view setTitle:NSLocalizedString(#"Settings", nil)];
}
return view;
}
Afaik, you shouldn't use -beginUpdates and -endUpdates in conjuction with a reload, but rather when inserting or deleting.
Not sure if this is causing your problems, but it might be worth checking out.
Is it the same regardless of animation type?
From Apple's docs:
Call this method if you want subsequent insertions, deletion, and
selection operations (for example, cellForRowAtIndexPath: and
indexPathsForVisibleRows) to be animated simultaneously. This group of
methods must conclude with an invocation of endUpdates. These method
pairs can be nested. If you do not make the insertion, deletion, and
selection calls inside this block, table attributes such as row count
might become invalid. You should not call reloadData within the group;
if you call this method within the group, you will need to perform any
animations yourself.
I don't want to show the "no results" text while my server is processing a search query.
I figured out the exact coordinates of the table cell that contains the label and attempted to cover it.
self.noResultsCoverView = [[[UIView alloc] initWithFrame:CGRectMake(
0.0,
44.0,
320.0,
43.0
)] autorelease];
self.noResultsCoverView.backgroundColor = [UIColor whiteColor];
[self.searchDisplayController.searchResultsTableView addSubview:self.noResultsCoverView];
To my chagrin, my cover was above the table view, but below the label. I need the cover to be above the label. searchResultsTableView::bringSubviewToFront didn't work, which makes me believe that the label isn't a child of the searchResultsTableView at all.
BTW, this Stack Overflow answer doesn't quite work for me. It works on the very first search, but flashes a weird black cover on subsequent searches.
this should do the work properly. The code to return at least one cell:
BOOL ivarNoResults; // put this somewhere in #interface or at top of #implementation
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
if (filteredList.count == 0) {
ivarNoResults = YES;
return 1;
} else {
ivarNoResults = NO;
return [filteredList count];
}
}
// {…}
// return the unfiltered array count
}
and for "showing" the clean cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == self.searchDisplayController.searchResultsTableView && ivarNoResults) {
static NSString *cleanCellIdent = #"cleanCell";
UITableViewCell *ccell = [tableView dequeueReusableCellWithIdentifier:cleanCellIdent];
if (ccell == nil) {
ccell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cleanCellIdent] autorelease];
ccell.userInteractionEnabled = NO;
}
return ccell;
}
// {…}
}
The easiest way to work around this is to return 1 in numberOfRowsInSection while the query is in progress and leave the dummy cell empty or set its hidden property to YES so it is not visible.
Try this it worked for me
In the UISearchDisplayController delegate do this:=
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 0.001);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
for (UIView* v in self.searchDisplayController.searchResultsTableView.subviews) {
if ([v isKindOfClass: [UILabel class]] &&
[[(UILabel*)v text] isEqualToString:#"No Results"]) {
[(UILabel*)v setText:#""];
break;
}
}
});
return YES;
}
You need to realize that when you have a UISearchDisplayController, and the search bar is active, the UITableView argument passed into your UITableView data source and delegate methods is in fact NOT your tableView object, but a tableView managed by the UISearchDisplayController, intended to display "live" search results (perhaps results filtered from your main data source, for example).
You can easily detect this in code, and then return the appropriate result from the delegate/data source method, depending on which tableView object is asking.
For example:
- (NSInteger)tableView:(UITableView *)tv numberOfRowsInSection:(NSInteger)section
{
if (tv == self.searchDisplayController.searchResultsTableView) {
// return the number of rows in section for the visible search results.
// return a non-zero value to suppress "No results"
} else {
// return the number of rows in section for your main data source
}
}
The point is that your data source and delegate methods are serving two tables, and you can (and should) check for which table is asking for data or delegation.
By the way, the "No results" is (I believe) provided by a background image which the UISearchDisplayController displays when the delegate says there are no rows... You are not seeing a 2-row table, the first blank and the second with text "No results". At least, that's what I think is happening there.
Ok so I am working on implementing a UISearchBar on a tableView that has sections. This may be wrong, but to populate the table view the first time, I have an array with lots of entries, and then populate the sections like this:
if(indexPath.section ==0){
[cell.textLabel setText:[tableData objectAtIndex:indexPath.row]];
}else if(indexPath.section ==1){
[cell.textLabel setText:[tableData objectAtIndex:indexPath.row+4]];
}else if(indexPath.section ==2){
[cell.textLabel setText:[tableData objectAtIndex:indexPath.row+8]];
}
Which is far from elegant, but it works. Now I am trying to hookup the UISearchBar, and this is the method that I am running into issues with:
- (void)searchBar:(UISearchBar *)sBar textDidChange:(NSString *)searchText
{
[tableData removeAllObjects];// remove all data that belongs to previous search
if([searchText isEqualToString:#""] || searchText==nil){
[tableView reloadData];
return;
}
NSInteger counter = 0;
for(NSString *name in dataSource)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
NSRange r = [name rangeOfString:searchText];
if(r.location != NSNotFound)
{
[tableData addObject:name];
}
[pool release];
}
[tableView reloadData];
}
So I am making an array again of entries that fit the search criteria, but then when I am trying to reload my tableView, it gets all bungled up because it is expecting sections. But all I want is the results in just a plain section-less tableView.
How can I implement this UISearchBar with a tableView with sections?
Thanks
set a BOOL when you enter search and adjust your section count accordingly
e.g.
in viewDidLoad
BOOL isSearching = NO;
set to YES when you enter the textDidChange method.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
int t;
if (isSearching) t = 1
else {
t= array.count;
}
return t;
}
You don't need to keep another variable around; just interrogate the tableView argument to see who is asking for the number of sections. For example, suppose your data is available in a fetchedResultsController:
if (tableView == self.searchDisplayDisplayController.searchResultsTableView) {
// your table is the search results table, so just return 1
return 1;
} else {
// your table is your "own" table
return [[self.fetchedResultsController sections] count];
}
I do the same thing in many of my table view delegate and data source methods.
I have a TabBar, NavBar, SearchBar with ScopeBar on my screen. I can search data via a remote server and list the content. So I have a NSMutableArray listContent and a filteredListContent like in the example of Apple (TableSearch - http://developer.apple.com/iphone/library/samplecode/TableSearch/index.html):
Now I added in
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
these line:
testDetailViewController *testDetailViewController = [[TestDetailViewController alloc] initWithNibName:#"TestDetailView" bundle:[NSBundle mainBundle]];
testDetailViewController.title = testClass.name;
testDetailViewController.myKey = testClass.keyId;
[[self navigationController] pushViewController:testDetailViewController animated:YES];
[testDetailViewController release];
testDetailViewController = nil;
Because of the NavigationBar, there is a "back" button. If I click this button, the TableView is empty, no matches/hits.
What I have to do, so the content will still be there?
Does anyone know?
Thanks a lot in advance & Best Regards.
Source Code:
#implementation SearchViewController
#synthesize listContent, filteredListContent, savedSearchTerm, savedScopeButtonIndex, searchWasActive;
- (void)viewDidLoad {
// restore search settings if they were saved in didReceiveMemoryWarning.
if (self.savedSearchTerm) {
[self.searchDisplayController setActive:self.searchWasActive];
[self.searchDisplayController.searchBar setSelectedScopeButtonIndex:self.savedScopeButtonIndex];
[self.searchDisplayController.searchBar setText:savedSearchTerm];
self.savedSearchTerm = nil;
}
}
- (void)viewDidUnload {
// Save the state of the search UI so that it can be restored if the view is re-created.
self.searchWasActive = [self.searchDisplayController isActive];
self.savedSearchTerm = [self.searchDisplayController.searchBar text];
self.savedScopeButtonIndex = [self.searchDisplayController.searchBar selectedScopeButtonIndex];
self.filteredListContent = nil;
}
- (void)dealloc {
[listContent release];
[filteredListContent release];
[super dealloc];
}
- (void)setData {
self.listContent = [NSMutableArray arrayWithCapacity:3];
[self.listContent addObject:[SearchObjects itemWithType:#"AAA" name:#"Test1"]];
[self.listContent addObject:[SearchObjects itemWithType:#"BBB" name:#"Test2"]];
[self.listContent addObject:[SearchObjects itemWithType:#"BBB" name:#"Test3"]];
// create a filtered list
self.filteredListContent = [NSMutableArray arrayWithCapacity:[self.listContent count]];
[self.tableView reloadData];
self.tableView.scrollEnabled = YES;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//If the requesting table view is the search display controller's table view, return the count of the filtered list, otherwise return the count of the main list.
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [self.filteredListContent count];
} else {
return [self.listContent count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *kCellID = #"cellID";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellID];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellID] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
/* If the requesting table view is the search display controller's table view, configure the cell using the filtered content, otherwise use the main list. */
SearchObjects *searchObject = nil;
if (tableView == self.searchDisplayController.searchResultsTableView) {
searchObject = [self.filteredListContent objectAtIndex:indexPath.row];
} else {
searchObject = [self.listContent objectAtIndex:indexPath.row];
}
cell.textLabel.text = searchObject.name;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// HERE IS THE SOURCE CODE FOR PUSHING TO THE NEXT VIEW
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
// DO SOME CALCULATIONS… AND THE setData METHOD IS CALLED
}
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
/* Update the filtered array based on the search text and scope. */
[self.filteredListContent removeAllObjects]; // First clear the filtered array.
/* Search the main list for whose type matches the scope (if selected) and whose name matches searchText; add items that match to the filtered array. */
for (SearchObjects *searchObject in listContent) {
if ([scope isEqualToString:#"All"] || [searchObject.type isEqualToString:scope]) {
NSComparisonResult result = [searchObject.name compare:searchText options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch) range:NSMakeRange(0, [searchText length])];
if (result == NSOrderedSame) {
[self.filteredListContent addObject:searchObject];
}
}
}
}
- (void)filterContentForScope:(NSString*)scope {
/* Update the filtered array based on the search text and scope. */
[self.filteredListContent removeAllObjects]; // First clear the filtered array.
/* Search the main list for whose type matches the scope (if selected); add items that match to the filtered array. */
for (SearchObjects *searchObject in listContent) {
if ([scope isEqualToString:#"All"] || [searchObject.type isEqualToString:scope]) {
[self.filteredListContent addObject:searchObject];
}
}
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self filterContentForScope:[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
[self filterContentForScope:[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption]];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
#end
You generally don't have to do anything in this case, the data should remain in place. Is there something which is unloading the data? Do you have a viewWillDisappear function which is unloading your array? Are you doing some of the array setup in viewWillAppear.
Put a log statement at the start of your methods to find out when they are being called, it will give you a clearer picture of what's happening.
It is solved. It was a problem which is not obvious with the given source code.
There was an error in my logic.
I am using this example of Apple's sample code:
http://developer.apple.com/iPhone/library/samplecode/TableSearch/index.html
In this example the UITableView get a list of content at startup. Clicking in the UISearchBar and typing, the content list will be filtered, also checking on scope of the ScopeBar.
I have to rebuild this kind of "instant search" to a "normal search": At the beginning I do not have data for the TableView. The user should click on the SearchBar, type something, press the button "Search" and a search request will be send to a webserver. The webserver´s response will be put into the TableView and the user can switch the scope to filter the result set. Changing the value of the SearchBar does not filter the result list. Only pressing the "Search" initiate a search request.
I took the example code and rebuild it (source code at the bottom).
But I have two problems with it.
At the initial call of the SearchViewController (with TabBar, SearchBar, ScopeBar, TableView), everything is okay. Thereis an empty TableView. But, clicking in the SearchBar and typing ony one character, there is a message that there are "no hits". How could I avoid that? This message should only appear if an user press "Search" and there are really no matches.
My second problem: Typing "hello" and pressing "Search" the TableView does not list the results. If I click "abort" or on a different scope, the results will be listed. So there must something like a missing "reload"?!
I hope someone could help me.
Thanks a lot in adavence & Best Regards.
My source code:
#implementation SearchViewController
#synthesize listContent, filteredListContent, savedSearchTerm, savedScopeButtonIndex, searchWasActive;
- (void)viewDidLoad {
// restore search settings if they were saved in didReceiveMemoryWarning.
if (self.savedSearchTerm) {
[self.searchDisplayController setActive:self.searchWasActive];
[self.searchDisplayController.searchBar setSelectedScopeButtonIndex:self.savedScopeButtonIndex];
[self.searchDisplayController.searchBar setText:savedSearchTerm];
self.savedSearchTerm = nil;
}
}
- (void)viewDidUnload {
// Save the state of the search UI so that it can be restored if the view is re-created.
self.searchWasActive = [self.searchDisplayController isActive];
self.savedSearchTerm = [self.searchDisplayController.searchBar text];
self.savedScopeButtonIndex = [self.searchDisplayController.searchBar selectedScopeButtonIndex];
self.filteredListContent = nil;
}
- (void)dealloc {
[listContent release];
[filteredListContent release];
[super dealloc];
}
- (void)setData {
self.listContent = [NSMutableArray arrayWithCapacity:3];
[self.listContent addObject:[SearchObjects itemWithType:#"AAA" name:#"Test1"]];
[self.listContent addObject:[SearchObjects itemWithType:#"BBB" name:#"Test2"]];
[self.listContent addObject:[SearchObjects itemWithType:#"BBB" name:#"Test3"]];
// create a filtered list
self.filteredListContent = [NSMutableArray arrayWithCapacity:[self.listContent count]];
[self.tableView reloadData];
self.tableView.scrollEnabled = YES;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//If the requesting table view is the search display controller's table view, return the count of the filtered list, otherwise return the count of the main list.
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [self.filteredListContent count];
} else {
return [self.listContent count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *kCellID = #"cellID";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellID];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellID] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
/* If the requesting table view is the search display controller's table view, configure the cell using the filtered content, otherwise use the main list. */
SearchObjects *searchObject = nil;
if (tableView == self.searchDisplayController.searchResultsTableView) {
searchObject = [self.filteredListContent objectAtIndex:indexPath.row];
} else {
searchObject = [self.listContent objectAtIndex:indexPath.row];
}
cell.textLabel.text = searchObject.name;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// HERE IS THE SOURCE CODE FOR PUSHING TO THE NEXT VIEW
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
// DO SOME CALCULATIONS… AND THE setData METHOD IS CALLED
}
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope {
/* Update the filtered array based on the search text and scope. */
[self.filteredListContent removeAllObjects]; // First clear the filtered array.
/* Search the main list for whose type matches the scope (if selected) and whose name matches searchText; add items that match to the filtered array. */
for (SearchObjects *searchObject in listContent) {
if ([scope isEqualToString:#"All"] || [searchObject.type isEqualToString:scope]) {
NSComparisonResult result = [searchObject.name compare:searchText options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch) range:NSMakeRange(0, [searchText length])];
if (result == NSOrderedSame) {
[self.filteredListContent addObject:searchObject];
}
}
}
}
- (void)filterContentForScope:(NSString*)scope {
/* Update the filtered array based on the search text and scope. */
[self.filteredListContent removeAllObjects]; // First clear the filtered array.
/* Search the main list for whose type matches the scope (if selected); add items that match to the filtered array. */
for (SearchObjects *searchObject in listContent) {
if ([scope isEqualToString:#"All"] || [searchObject.type isEqualToString:scope]) {
[self.filteredListContent addObject:searchObject];
}
}
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
[self filterContentForScope:[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
[self filterContentForScope:[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption]];
// Return YES to cause the search result table view to be reloaded.
return YES;
}
#end
For your first problem you should set up a delegate for the search bar and then implement – searchBarSearchButtonClicked: and put your searching code in there. You might also have to implement others such as – searchBarTextDidEndEditing: or – searchBar:textDidChange: and make sure that they do not perform the search.
For your second question, you might want to simply reload the tableView using the delegate again from the – searchBarSearchButtonClicked: to make sure that it happens after you have already searched. You can use [tableView reloadData] to accomplish this.
Problem is solved, see in the comments.