0

I'm having issues with implementing the UISearchBarDelegate on my UITableView with sections issue where every time I try to search for something inside the UITableView, either it doesn't display the desired result or it crashes the app with an error saying:

Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]'

Here's inside my header file:

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate, UISearchDisplayDelegate, UISearchBarDelegate>
{
    UITableView *mainTableView;

    NSMutableArray *contentsList;
    NSMutableArray *searchResults;
    NSString *savedSearchTerm;

    NSMutableArray *ivSectionKeys;
    NSMutableDictionary *ivSectionContents;
}

@property (nonatomic, retain) IBOutlet UITableView *mainTableView;
@property (nonatomic, retain) NSMutableArray *contentsList;
@property (nonatomic, retain) NSMutableArray *searchResults;
@property (nonatomic, copy) NSString *savedSearchTerm;

@property (nonatomic, retain) NSMutableArray *sectionKeys;
@property (nonatomic, retain) NSMutableDictionary *sectionContents;

- (void)handleSearchForTerm:(NSString *)searchTerm;

@end

while this is inside my implementation file:

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

@synthesize mainTableView;
@synthesize contentsList;
@synthesize searchResults;
@synthesize savedSearchTerm;

@synthesize sectionKeys = ivSectionKeys;
@synthesize sectionContents = ivSectionContents;

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    NSMutableArray *keys = [[NSMutableArray alloc] init];
    NSMutableDictionary *contents = [[NSMutableDictionary alloc] init];

    NSString *colorKey = @"Colors";
    NSString *clothingKey = @"Clothing";
    NSString *miscKey = @"Misc";

    [contents setObject:[NSArray arrayWithObjects:@"Red", @"Blue", nil] forKey:colorKey];
    [contents setObject:[NSArray arrayWithObjects:@"Pants", @"Shirt", @"Socks", nil] forKey:clothingKey];
    [contents setObject:[NSArray arrayWithObjects:@"Wankle Rotary Engine", nil] forKey:miscKey];

    [keys addObject:clothingKey];
    [keys addObject:miscKey];
    [keys addObject:colorKey];

    self.sectionKeys = keys;
    self.sectionContents = contents;

    // Restore search term
    if (self.savedSearchTerm)
    {
        self.searchDisplayController.searchBar.text = self.savedSearchTerm;
    }
}

- (void)viewDidUnload
{
    [super viewDidUnload];

    // Save the state of the search UI so that it can be restored if the view is re-created.
    self.savedSearchTerm = self.searchDisplayController.searchBar.text;

    self.searchResults = nil;
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    [self.mainTableView reloadData];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)handleSearchForTerm:(NSString *)searchTerm
{
    self.savedSearchTerm = searchTerm;

    if (self.searchResults == nil)
    {
        NSMutableArray *array = [[NSMutableArray alloc] init];
        self.searchResults = array;
        array = nil;
    }

    [self.searchResults removeAllObjects];

    if ([self.savedSearchTerm length] != 0)
    {
        for (NSString *currentString in self.sectionContents)
        {
            if ([currentString rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound)
            {
                [self.searchResults addObject:currentString];
            }
        }
    }
}

#pragma mark -
#pragma mark UITableViewDataSource Methods

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    NSInteger sections = [self.sectionKeys count];

    return sections;
}

- (NSString *)tableView:(UITableView *)tableView
titleForHeaderInSection:(NSInteger)section
{
    NSString *key = [self.sectionKeys objectAtIndex:section];

    return key;
}

- (NSInteger)tableView:(UITableView *)tableView
 numberOfRowsInSection:(NSInteger)section
{
    NSString *key = [self.sectionKeys objectAtIndex:section];
    NSArray *contents = [self.sectionContents objectForKey:key];

    NSInteger rows;

    if (tableView == [[self searchDisplayController] searchResultsTableView])
        rows = [self.searchResults count];
    else
        rows = [contents count];

    NSLog(@"rows is: %d", rows);

    return rows;
}

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *key = [self.sectionKeys objectAtIndex:indexPath.section];
    NSArray *contents = [self.sectionContents objectForKey:key];
    NSString *contentForThisRow = [contents objectAtIndex:indexPath.row];

    if (tableView == [self.searchDisplayController searchResultsTableView])
        contentForThisRow = [self.searchResults objectAtIndex:indexPath.row];
    else
        contentForThisRow = [contents objectAtIndex:indexPath.row];

    static NSString *CellIdentifier = @"CellIdentifier";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }

    cell.textLabel.text = contentForThisRow;

    return cell;
}

#pragma mark -
#pragma mark UITableViewDelegate Methods

- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
}

#pragma mark -
#pragma mark UISearchDisplayController Delegate Methods

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchString
{
    [self handleSearchForTerm:searchString];

    // Return YES to cause the search result table view to be reloaded.

    return YES;
}

- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
    self.savedSearchTerm = nil;

    [self.mainTableView reloadData];
}

@end

I think my issue is inside my for-loop but I'm not really sure.

halfer
  • 19,824
  • 17
  • 99
  • 186
jaytrixz
  • 4,059
  • 7
  • 38
  • 57

1 Answers1

0

You need to update all of your table view data source and delegate methods to handle both tables (you only do this in some):

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    NSInteger sections = [self.sectionKeys count];

    return sections;
}

should be:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    if (tableView == self.tableView) {
        NSInteger sections = [self.sectionKeys count];

        return sections;
    } else { // assume it's the search table
        // replace this with the actual result
        return numberOfSectionsForSearchTable;
    }
}
rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • I tried doing this: if (tableView == [[self searchDisplayController] searchResultsTableView]) { NSInteger sections = [self.sectionContents count]; return sections; } else { NSInteger sections = [self.sectionKeys count]; return sections; } yet it still returns the same error. – jaytrixz Nov 11 '12 at 00:16
  • OK, and what happened? Did you update the rest of the methods? – rmaddy Nov 11 '12 at 00:17
  • I only updated the numberOfSectionsInTableView method. What methods do I still need to update? – jaytrixz Nov 11 '12 at 00:27
  • You need to update all of the table view data source and delegate methods. `tableView:numberOfRowsInSection:` and `tableView:didSelectRowAtIndexPath:` at a minimum (over what you've already done). – rmaddy Nov 11 '12 at 00:53