在 TableView 中使用 Core Data 和 UISearchDisplayController 自动完成

Posted

技术标签:

【中文标题】在 TableView 中使用 Core Data 和 UISearchDisplayController 自动完成【英文标题】:Autocomplete with Core Data and UISearchDisplayController in a TableView 【发布时间】:2013-11-19 18:03:55 【问题描述】:

我在TableViewheader 中实现UISearchBar,我想用Core DataNSFetchedResultsController 管理结果,同时给这个结果表一个自定义的外观和感觉。我有一个TableView 和:Header,它是UISearchBar; 3 个部分:第一部分是一个国家/地区的名称,该国家/地区可以从位置中获取或从UISearchResults TableView 中选择。第二部分是:你想要什么,它有一些行,第三部分是一个过滤器,它的行中有更多选项..

TableView 是来自 CocoaControls.com 的 SidePanel(JASidePanel 控件)。

现在,我的问题是我在 searchResults Tableview 中看不到 Core Data 请求的结果。

我已经关注了这个链接Filter NSFetchedResultController with Core Data

这个日志输出显示0;

 NSLog(@"Numbers of Sections = %i",
              [[aFetchedResultsController sections] count]);

这是我正在使用的代码:

@interface WPFilterSidePanelViewController ()

    NSString        *savedSearchTerm_;
    NSInteger       savedScopeButtonIndex_;
    BOOL            searchWasActive_;

      NSFetchedResultsController *searchFetchedResultsController_;


@property (nonatomic, retain) NSFetchedResultsController *searchFetchedResultsController;


@property (nonatomic, copy) NSString *savedSearchTerm;
@property (nonatomic) NSInteger savedScopeButtonIndex;
@property (nonatomic) BOOL searchWasActive;
@end

#pragma mark - Search Display Delegate methods

#pragma mark Content Filtering
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSInteger)scope

    // update the filter, in this case just blow away the FRC and let lazy evaluation create another with the relevant search info
    self.searchFetchedResultsController.delegate = nil;
    self.searchFetchedResultsController = nil;
    // if you care about the scope save off the index to be used by the serchFetchedResultsController
    //self.savedScopeButtonIndex = scope;



#pragma mark -
#pragma mark Search Bar
- (void)searchDisplayController:(UISearchDisplayController *)controller willUnloadSearchResultsTableView:(UITableView *)tableView;

    // search is done so get rid of the search FRC and reclaim memory
    self.searchFetchedResultsController.delegate = nil;
    self.searchFetchedResultsController = nil;


- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString

    [self filterContentForSearchText:searchString
                               scope:[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 filterContentForSearchText:[self.searchDisplayController.searchBar text]
                               scope:[self.searchDisplayController.searchBar selectedScopeButtonIndex]];

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



#pragma mark - NSFetchedResultsController Delegate methods
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller

    UITableView *searchTableView = self.searchDisplayController.searchResultsTableView;
    [searchTableView beginUpdates];


- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller

    UITableView *searchTableView =  self.searchDisplayController.searchResultsTableView;
    [searchTableView endUpdates];



- (NSFetchedResultsController *)newFetchedResultsControllerWithSearch:(NSString *)searchString


    NSLog(@"string search bar%@", searchString);

    NSArray *sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES selector:nil]];
    NSPredicate *filterPredicate = nil;//[NSPredicate predicateWithFormat:@"name = %@",searchString ];

    /*
     Set up the fetched results controller.
     */
    // Create the fetch request for the entity.
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    // Edit the entity name as appropriate.
    NSEntityDescription *callEntity = [NSEntityDescription entityForName:kEntityStore inManagedObjectContext:mainContext];
    [fetchRequest setEntity:callEntity];

    NSMutableArray *predicateArray = [NSMutableArray array];
    if(searchString.length)
    
        // your search predicate(s) are added to this array
        [predicateArray addObject:[NSPredicate predicateWithFormat:@"name CONTAINS[cd] %@", searchString]];
        // finally add the filter predicate for this view
        if(filterPredicate)
        
            filterPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:filterPredicate, [NSCompoundPredicate orPredicateWithSubpredicates:predicateArray], nil]];
        
        else
        
            filterPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicateArray];
        
    
    [fetchRequest setPredicate:filterPredicate];

    // Set the batch size to a suitable number.
    [fetchRequest setFetchBatchSize:20];

    [fetchRequest setSortDescriptors:sortDescriptors];


    NSLog(@"%@", fetchRequest);
    // Edit the section name key path and cache name if appropriate.
    // nil for section name key path means "no sections".
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                                                                                managedObjectContext:mainContext
                                                                                                  sectionNameKeyPath:nil
                                                                                                           cacheName:nil];

    NSLog(@"Numbers of Rows = %i",
          [[[aFetchedResultsController sections] objectAtIndex:0] numberOfObjects]);
    aFetchedResultsController.delegate = self;

    NSLog(@"%@", aFetchedResultsController);
    NSError *error = nil;
    if (![aFetchedResultsController performFetch:&error])
    
        /*
         Replace this implementation with code to handle the error appropriately.

         abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
         */
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    

    return aFetchedResultsController;



- (NSFetchedResultsController *)searchFetchedResultsController

    NSLog(@"SearchBar text :%@ ", self.searchDisplayController.searchBar.text);

    searchFetchedResultsController_ = [self newFetchedResultsControllerWithSearch:self.searchDisplayController.searchBar.text];
    return searchFetchedResultsController_ ;



- (void)viewDidLoad

    [super viewDidLoad];
   [self.view addSubview:self.tableView];
    [self creatingSearchBar];

    if (self.savedSearchTerm)
    
        [self.searchDisplayController setActive:self.searchWasActive];
        [self.searchDisplayController.searchBar setSelectedScopeButtonIndex:self.savedScopeButtonIndex];
        [self.searchDisplayController.searchBar setText:savedSearchTerm];

        self.savedSearchTerm = nil;
    


- (void)didReceiveMemoryWarning

    self.searchWasActive = [self.searchDisplayController isActive];
    self.savedSearchTerm = [self.searchDisplayController.searchBar text];
    self.savedScopeButtonIndex = [self.searchDisplayController.searchBar selectedScopeButtonIndex];

    fetchedResultsController_.delegate = nil;
    fetchedResultsController_ = nil;
    searchFetchedResultsController_.delegate = nil;
    searchFetchedResultsController_ = nil;

    [super didReceiveMemoryWarning];


- (void)viewDidDisappear:(BOOL)animated

    // 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];



-(void)creatingSearchBar

    searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width, 44.0)];
    searchBar.autoresizingMask = (UIViewAutoresizingFlexibleWidth);
    searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
    self.tableView.tableHeaderView = searchBar;

    self.searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
    self.searchDisplayController.delegate = self;
    self.searchDisplayController.searchResultsDataSource = self;
    self.searchDisplayController.searchResultsDelegate = self;
    self.searchDisplayController.searchResultsTableView.backgroundColor = [UIColor blackColor];

【问题讨论】:

【参考方案1】:

您缺少表格视图datasource 方法。在调用self.fetchedResultsController 时,它们应该重新创建您获取的结果控制器。我假设您已经忘记了它们,因为您选择发布很多不相关的代码。

此外,在您的代码中,您将谓词设置为nil,然后检查它是否为 nil。无论您使用仅包含一个元素的谓词数组来构造复合谓词。这两种策略都不是很有意义。

【讨论】:

您的意思是我的主 TableView 的数据源?或搜索结果表视图。我的 Main TableView 运行良好且没有 fetchedResultContoller,因为我不需要它。我只有 SearchResults 有问题。主tableview的代码我没有展示,因为我觉得代码太多 为表视图数据源设置单独的数组似乎是个坏主意。您应该使用获取的结果控制器。我确定你的问题在那里。也许您想删除大部分已发布的代码并包含数据源数组的机制。 但我需要一个部分数组,每个部分都有一个数组,因为它是一个具有不同过滤器的表格视图。而且我还需要 SearchBar 仅用于获取国家,如果用户在一个国家/地区按下,那么我将第一部分的名称更改为国家/地区名称。我认为我可以在 SidePanel TableView 上使用带有搜索结果的不同 TableView。 你的评论中没有什么是从 FRC 复制数据的好论据。 FRC 能够完成您提到的所有事情,同时优化内存和性能。 不.. 但我不是在复制 FRC,尽管你说的很有趣。我只将 FRC 用于国家/地区列表。

以上是关于在 TableView 中使用 Core Data 和 UISearchDisplayController 自动完成的主要内容,如果未能解决你的问题,请参考以下文章

Swift 3. 使用 Core Data 隐藏一个空的 tableView

将 Core Data 与图像或按钮(或除了 tableView 之外的任何东西)一起使用

使用 Core Data 使用 didSelectRowAtIndexPath 编辑 TableView 单元格

Swift UITableView 删除行项 - tableview 数据源是 Core Data

Core Data 属性计数并在 TableView 中显示结果

Core Data TableView - 编辑模式下的多选