1

I have a IUSearchBar with an UISearchDisplayController, and everything is working well:

  • When I touch the search bar, the keyboard shows up, the TableView has a black cover on it and I can start typing

screenshot 1 http://joeranbosma.nl/xcode/sdc1.png

  • When I type something the clear button (x) appears, and so do my search results.

screenshot 1 http://joeranbosma.nl/xcode/sdc2.png

  • This works all great. When I click on the clear (x) button, it just returns to the first screenshot state (empty search bar, and a black box above the results)

But, here it comes!

  • When I type something and then move the search result, the keyboard hides (standard UISearchDisplayController thing) Like this:

screenshot 1 http://joeranbosma.nl/xcode/sdc3.png

When I then click on the clear (x) button, the program crashes with a SIGABRT error:

screenshot 1 http://joeranbosma.nl/xcode/sdc4.png

.. and I have no idea how to fix this :(

Here is my source code: (no idea what you need to solve the question) http://joeranbosma.nl/xcode/wearch_stack.zip

But I think this is the most useful part:

- (void)viewDidLoad {
    [super viewDidLoad];

    //Initialize the array.
    listOfItems = [[NSMutableArray alloc] init];
    copyListOfItems = [[NSMutableArray alloc] init];
    staticlist = [[NSMutableArray alloc] init];

    staticlist = listOfItems;

    //Add items
    [listOfItems addObject:@"Iceland"];
    [listOfItems addObject:@"Greenland"];
    [listOfItems addObject:@"Switzerland"];
    [listOfItems addObject:@"Norway"];
    [listOfItems addObject:@"New Zealand"];
    [listOfItems addObject:@"Greece"];
    [listOfItems addObject:@"Rome"];
    [listOfItems addObject:@"Ireland"];

    //Set the title
    self.navigationItem.title = @"Wearch";

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
   return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (searching)
        return [copyListOfItems count];
    else {
        return [listOfItems count];
    }
}
// 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];
    }

    // Configure the cell.
    if(searching)
        cell.textLabel.text = [copyListOfItems objectAtIndex:indexPath.row];
    else {
        NSString *txtLbl = [listOfItems objectAtIndex:indexPath.row];
        cell.textLabel.text = [txtLbl stringByAppendingString:@"x"];
    }

    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

    return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

    //Get the selected word

    NSString *selectedWord = nil;

    if(searching)
        selectedWord = [copyListOfItems objectAtIndex:indexPath.row];
    else {
        selectedWord = [listOfItems objectAtIndex:indexPath.row];
    }

    //Initialize the detail view controller and display it.
    DetailViewController *dvController = [[DetailViewController alloc] initWithNibName:@"DetailView" bundle:[NSBundle mainBundle]];
    dvController.selectedWord = selectedWord;
    [self.navigationController pushViewController:dvController animated:YES];
    [dvController release];
    dvController = nil;  
}
- (void) searchBarTextDidBeginEditing:(UISearchBar *)theSearchBar {
    searching = YES;
}
- (void)searchBar:(UISearchBar *)theSearchBar textDidChange:(NSString *)searchText {
    //Remove all objects first.
    [copyListOfItems removeAllObjects];

    if([searchText length] > 0) {
        searching = YES;
        [self searchTableView];
    }
    else {
        searching = NO;
    }

    [self.tableView reloadData];
}
- (void) searchBarSearchButtonClicked:(UISearchBar *)theSearchBar {
    [self searchTableView];
}
- (void) searchTableView {

    NSString *searchText = searchBar.text;
    NSMutableArray *searchArray = [[NSMutableArray alloc] init];

    NSMutableArray *results1 = [[NSMutableArray alloc] init];
    NSMutableArray *results2 = [[NSMutableArray alloc] init];

    [searchArray addObjectsFromArray:listOfItems];

    for (NSString *sTemp in searchArray) {
        NSRange titleResultsRange = [sTemp rangeOfString:searchText options:NSCaseInsensitiveSearch];

        if (titleResultsRange.length > 0){
            if (titleResultsRange.location == 0) {
                [results1 addObject:sTemp];
            }
            else{
                [results2 addObject:sTemp];
            }
        }
    }

    for (NSString *sTemp in results1) {
        [copyListOfItems addObject:sTemp];
    }
    for (NSString *sTemp in results2) {
        [copyListOfItems addObject:sTemp];
    }

    [searchArray release];
    searchArray = nil;
}

I hope one of you can solve this.

Can you also say if you like the images?

Joeran
  • 888
  • 7
  • 12

1 Answers1

5

This is an interesting one. The long answer is that searchBarTextDidBeginEditing: is being called after tableView:numberOfRowsInSection: but before tableView:cellForRowAtIndexPath:. The result is that the tableView is expecting [listOfItems count] number of rows but by the time it calls tableView:cellForRowAtIndexPath: the searching BOOL is YES so the view controller uses copyListOfItems to populate the table which of course does not contain a sufficient number of objects to populate the table.

The short answer is, stop telling the view controller you are searching until you are searching; Set searching = YES; when there is actual text you are using to search not just when the searchBar is first responder.

The really short answer:

- (void) searchBarTextDidBeginEditing:(UISearchBar *)theSearchBar {
    // Comment this assignment out
    //searching = YES;
}

It seems to work fine.

A couple of tips for such a scenario:

1) Look at the printed stacktrace in your console. It would have let you see that the problem was:

'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'

2) Use NSLogs profusely; I find the most helpful to be:

NSLog(@"%@",NSStringFromSelector(_cmd));

Edit In response to the non-symbolicated stack-trace in the console(i.e. 0x14d3ec9 0x36e5c2):

3) Use an exception breakpoint; as pictured:

enter image description here

4) Add an uncaught exception handler; As described here.

Community
  • 1
  • 1
NJones
  • 27,139
  • 8
  • 70
  • 88
  • Wow, thank you so much! That was indeed the problem. But, besides you found the problem, I understand the language better now. And I never looked at the console because the was a lot of this: '422af1 0x14d3ec9 0x36e5c2 0x36e55a 0x413b76 0x41403f 0x4132fe 0x393a30 0x393c56 0x37a384 0x36daa9 0x1bc8fa9 0x14a61c5 0x140b022 0x140990a 0x1408db4 0x1408ccb 0x1bc7879 0x1bc793e 0x36ba9b 0x256d 0x24e5)' And I just didn't knew there was something useful, so I didn't looked further. So: thank you very much! – Joeran Mar 26 '12 at 06:17
  • 1
    Glad to help, especially someone who wants to learn more. So I added a couple more tips to help with the useless hex stack-trace you mentioned. – NJones Mar 27 '12 at 15:14
  • It goes really upward with my app :) – Joeran Mar 27 '12 at 18:18