views:

1835

answers:

2

Hello!

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.

  1. 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.
  2. 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
A: 

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.

mjdth
Hm , I not understand it. I already have a method "searchBarSearchButtonClicked:". In this I send the request t the server and the "setData" method set the data to the TableView and perform a reloadData, but this does not work.
Tim
Did you override the searchBarTextDidChange to prevent that from possibly reloading the data?
mjdth
I solved the problem. I set a flag, and only if the flag is true, which indicates, that a search was complete, then I return YES; in the delegate methods searchDisplayController:controller shouldReloadTableFor...The other problem was in the listContent. It does the reloadData, but the focus was on the filteredContentList!
Tim
Hi Tim, I'm doing something similar and have the same problem but I have not been able to solve it. I tried setting a flag as you describe here and only returning YES in those two delegate methods if the response from the server has been received.However, I'm not seeing those methods called following the query to the server. How are you getting the table to reload with the filtered results? Is that triggered by the server's response? Also, I don't see the second problem you describe here (with reloadData) in your code above.
Eric
A: 

Problem is solved, see in the comments.

Tim