UISearchResultsController 在设备上崩溃(索引超出空数组的范围)

发布于 2024-09-09 05:59:44 字数 5676 浏览 8 评论 0原文

我正在使用 UISearchResultsController 过滤来自 fetchedResultsController 的数据。以下是相关代码:

RootVieweController.h 中:

@interface RootViewController : UITableViewController <NSFetchedResultsControllerDelegate, AdditionViewControllerDelegate, UISearchBarDelegate, UISearchDisplayDelegate> {

    NSArray *filteredListContent;   
    NSString *savedSearchTerm;
    NSInteger savedScopeButtonIndex;
    BOOL searchIsActive;
}
@property (nonatomic, retain) NSArray *filteredListContent;
@property (nonatomic, copy) NSString *savedSearchTerm;
@property (nonatomic) NSInteger savedScopeButtonIndex;
@property (nonatomic) BOOL searchIsActive;

RootViewController.m 中:

-(void)viewDidLoad {
    [snip]
    UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 300, 40)];
    searchBar.delegate = self;
    searchBar.scopeButtonTitles = [NSArray arrayWithObjects:@"Scope 1", @"Scope 2", nil];
    [searchBar sizeToFit];
    searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
    self.tableView.tableHeaderView = searchBar;
    [searchBar release];
    [self.tableView setContentOffset:CGPointMake(0, 40)];

    UISearchDisplayController *searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
    [self performSelector:@selector(setSearchDisplayController:) withObject:searchDisplayController];

    [searchDisplayController setDelegate:self];
    [searchDisplayController setSearchResultsDataSource:self];
    [searchDisplayController setSearchResultsDelegate:self];
    [searchDisplayController release];

    self.filteredListContent = [NSMutableArray arrayWithCapacity:[[[self fetchedResultsController] fetchedObjects] count]];

    if (self.savedSearchTerm) {
        [self.searchDisplayController setActive:self.searchIsActive];
        [self.searchDisplayController.searchBar setSelectedScopeButtonIndex:self.savedScopeButtonIndex];
        [self.searchDisplayController.searchBar setText:savedSearchTerm];
    
        self.savedSearchTerm = nil;
    }
}

- (NSInteger)tableView:(UITableView *)theTableView numberOfRowsInSection:(NSInteger)section {
    // Return the number of rows in the section.
    if (theTableView == self.searchDisplayController.searchResultsTableView) {
        NSLog(@"Search Cells: %i", [self.filteredListContent count]);
        return [self.filteredListContent count];
    }
    id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
    NSLog(@"Normal cells: %i", [sectionInfo numberOfObjects]);
    return [sectionInfo numberOfObjects];
}

-(void)configureCell:(CustomTableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
    //Called from cellForRowAtIndexPath

    Object *object = nil;
    if (self.searchIsActive) {
        object = [[self filteredListContent] objectAtIndex:[indexPath row]];
    } else {
        object = [fetchedResultsController objectAtIndexPath:indexPath];
    }

    [snip]
}


#pragma mark -
#pragma mark Search functions

-(void)filterContentForSearchText:(NSString *)searchText scope:(NSString *)scope {
    if ([scope isEqualToString:@"Scope 1"]) {
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"scope1 BEGINSWITH[cd] %@", searchText];
        self.filteredListContent = [[[self fetchedResultsController] fetchedObjects] filteredArrayUsingPredicate:predicate];
    } else {
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"scope2 BEGINSWITH[cd] %@", searchText];
        self.filteredListContent = [[[self fetchedResultsController]fetchedObjects] filteredArrayUsingPredicate:predicate];
    }
}

#pragma mark -
#pragma mark UISearchDisplayController Delegate Methods

- (void)searchDisplayController:(UISearchDisplayController *)controller willShowSearchResultsTableView:(UITableView *)theTableView {
    NSLog(@"Showing search results");
}

-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
    [self filterContentForSearchText:searchString scope:
     [[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:
      [self.searchDisplayController.searchBar selectedScopeButtonIndex]]];

    NSLog(@"Reloading for string");

    return YES;
}

-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
    [self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:
     [[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption]];

    NSLog(@"Reloading for scope");

    return YES;
}

-(void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
    self.searchDisplayController.searchResultsTableView.rowHeight = 55;
    self.searchIsActive = YES;
}

-(void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller {
    self.searchIsActive = NO;
}

这在模拟器中效果很好。但在设备上,当尝试显示 searchResultsTableView 时,它会在 configureCell 中的 object = [[selffilteredListContent] objectAtIndex:[indexPath row]]; 上崩溃。我收到错误 [NSMutableArray objectAtIndex:]: index 7超出空数组范围。始终索引 7。我在这里缺少什么?

--更新--

修复:翻转 searchIsActive BOOL 以打开 searchDisplayControllerDidBeginSearch 而不是 searchDisplayControllerWillBeginSearchsearchDisplayControllerWillEndSearch 而不是 searchDisplayControllerDidEndSearch。这可以防止表尝试配置不存在的单元格。不知道为什么模拟器没有捕捉到这个

I'm using UISearchResultsController to filter data from a fetchedResultsController. Here is the relevant code:

In RootVieweController.h:

@interface RootViewController : UITableViewController <NSFetchedResultsControllerDelegate, AdditionViewControllerDelegate, UISearchBarDelegate, UISearchDisplayDelegate> {

    NSArray *filteredListContent;   
    NSString *savedSearchTerm;
    NSInteger savedScopeButtonIndex;
    BOOL searchIsActive;
}
@property (nonatomic, retain) NSArray *filteredListContent;
@property (nonatomic, copy) NSString *savedSearchTerm;
@property (nonatomic) NSInteger savedScopeButtonIndex;
@property (nonatomic) BOOL searchIsActive;

In RootViewController.m:

-(void)viewDidLoad {
    [snip]
    UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 300, 40)];
    searchBar.delegate = self;
    searchBar.scopeButtonTitles = [NSArray arrayWithObjects:@"Scope 1", @"Scope 2", nil];
    [searchBar sizeToFit];
    searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
    self.tableView.tableHeaderView = searchBar;
    [searchBar release];
    [self.tableView setContentOffset:CGPointMake(0, 40)];

    UISearchDisplayController *searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
    [self performSelector:@selector(setSearchDisplayController:) withObject:searchDisplayController];

    [searchDisplayController setDelegate:self];
    [searchDisplayController setSearchResultsDataSource:self];
    [searchDisplayController setSearchResultsDelegate:self];
    [searchDisplayController release];

    self.filteredListContent = [NSMutableArray arrayWithCapacity:[[[self fetchedResultsController] fetchedObjects] count]];

    if (self.savedSearchTerm) {
        [self.searchDisplayController setActive:self.searchIsActive];
        [self.searchDisplayController.searchBar setSelectedScopeButtonIndex:self.savedScopeButtonIndex];
        [self.searchDisplayController.searchBar setText:savedSearchTerm];
    
        self.savedSearchTerm = nil;
    }
}

- (NSInteger)tableView:(UITableView *)theTableView numberOfRowsInSection:(NSInteger)section {
    // Return the number of rows in the section.
    if (theTableView == self.searchDisplayController.searchResultsTableView) {
        NSLog(@"Search Cells: %i", [self.filteredListContent count]);
        return [self.filteredListContent count];
    }
    id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
    NSLog(@"Normal cells: %i", [sectionInfo numberOfObjects]);
    return [sectionInfo numberOfObjects];
}

-(void)configureCell:(CustomTableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
    //Called from cellForRowAtIndexPath

    Object *object = nil;
    if (self.searchIsActive) {
        object = [[self filteredListContent] objectAtIndex:[indexPath row]];
    } else {
        object = [fetchedResultsController objectAtIndexPath:indexPath];
    }

    [snip]
}


#pragma mark -
#pragma mark Search functions

-(void)filterContentForSearchText:(NSString *)searchText scope:(NSString *)scope {
    if ([scope isEqualToString:@"Scope 1"]) {
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"scope1 BEGINSWITH[cd] %@", searchText];
        self.filteredListContent = [[[self fetchedResultsController] fetchedObjects] filteredArrayUsingPredicate:predicate];
    } else {
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"scope2 BEGINSWITH[cd] %@", searchText];
        self.filteredListContent = [[[self fetchedResultsController]fetchedObjects] filteredArrayUsingPredicate:predicate];
    }
}

#pragma mark -
#pragma mark UISearchDisplayController Delegate Methods

- (void)searchDisplayController:(UISearchDisplayController *)controller willShowSearchResultsTableView:(UITableView *)theTableView {
    NSLog(@"Showing search results");
}

-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
    [self filterContentForSearchText:searchString scope:
     [[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:
      [self.searchDisplayController.searchBar selectedScopeButtonIndex]]];

    NSLog(@"Reloading for string");

    return YES;
}

-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
    [self filterContentForSearchText:[self.searchDisplayController.searchBar text] scope:
     [[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:searchOption]];

    NSLog(@"Reloading for scope");

    return YES;
}

-(void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller {
    self.searchDisplayController.searchResultsTableView.rowHeight = 55;
    self.searchIsActive = YES;
}

-(void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller {
    self.searchIsActive = NO;
}

This works great in the simulator. But on a device, it crashes on object = [[self filteredListContent] objectAtIndex:[indexPath row]]; from configureCell when trying to show the searchResultsTableView. I get the error [NSMutableArray objectAtIndex:]: index 7 beyond bounds for empty array. Always index 7. What am I missing here?

--UPDATE--

Fixed: flipped the searchIsActive BOOL to switch on searchDisplayControllerDidBeginSearch instead of searchDisplayControllerWillBeginSearch and searchDisplayControllerWillEndSearch instead of searchDisplayControllerDidEndSearch. That keeps the table from trying to configure cells that don't exist. No clue why the Simulator didn't catch this

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

記柔刀 2024-09-16 05:59:44

如果它引用的数组为空,那么当您尝试从空数组填充表视图时,它的索引是什么并不重要。

您的 NSMutableArray filteredListContent 是否已填充(即您是否从获取的结果控制器返回搜索结果)?您检查过 filterContentForSearchText: 中数组的内容吗?

PS 我喜欢你的风格... BeerListTableViewCell :)

If the array it is referencing is empty, it doesn't matter what index it is if you are trying to populate the tableview from an empty array.

Is your NSMutableArray filteredListContent populated (i.e. are you returning search results from the fetched results controller)? Have you checked the contents of the array in filterContentForSearchText:?

P.S. I like your style... BeerListTableViewCell :)

2024-09-16 05:59:44

我在更新现有应用程序时遇到了完全相同的崩溃。该代码几乎与原始海报完全相同,我的应用程序中唯一更改的是数据库。 没有代码更改

对于旧数据库,当搜索显示控制器开始搜索时,不会调用表视图数据源方法。

对于新数据库,当搜索显示控制器开始搜索时,表视图数据源方法确实会被调用。

相同的核心数据实体、相同的数据库结构、相同的代码、相同的 nib。在模拟器或设备上出现同样的问题。

I ran into this exact same crash while working on an update to an existing app. The code is almost exactly the same as the original poster's and the only thing that changed in my app was the database. There were no code changes.

With the old database, the table view datasource methods don't get called when the search display controller starts a search.

With the new database, the table view datasource methods do get called when the search display controller starts a search.

Same Core Data entities, same database structure, same code, same nibs. Same problem in simulator or on the device.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文