Joeran
Joeran

Reputation: 888

Strange UISearchDisplayController Crash with hidden keyboard

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

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

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

But, here it comes!

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?

Upvotes: 1

Views: 1228

Answers (1)

NJones
NJones

Reputation: 27147

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.

Upvotes: 5

Related Questions