一个用于 UITableview 和 UISearchDisplayController 的 NSFetchedResultController

Posted

技术标签:

【中文标题】一个用于 UITableview 和 UISearchDisplayController 的 NSFetchedResultController【英文标题】:One NSFetchedResultController for UITableview and UISearchDisplayController 【发布时间】:2014-01-07 12:09:53 【问题描述】:

也许这是一个愚蠢的问题,但我觉得我可以改进我的项目,或者至少让它更具可读性。

这是交易。我有一个 UITableView 填充了来自服务器的数据。对于我使用MagicalRecord 和处理UITableView 的所有内容,我使用NSFetchedResultController。到目前为止,一切都很好。当我尝试实现UISearchDisplayController 时出现问题。不使用NSFetchedResultController,我只是从Core Data 中获取数据并将其传递给NSArray。这是我用来排序的方法:

- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope

    NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:@"SELF.name contains[c] %@", searchText];
    searchResults = [Group MR_findAllSortedBy:@"caseInsensitiveName" ascending:YES withPredicate:resultPredicate];

当然,所有UITableView 样板代码都填充了if 条件检查类型UITableView。 我想我可以使用一个NSFetchedResultController 来处理这两项任务(同时填充UITableView's)并省略这个searchResult 数组,但我不确定......所以我会很感激任何建议、提示或其他任何东西。

另一个问题是当我同时使用从其他来源填充​​的UITableViews 时,当我启用sections 时它们会发生冲突。简单地说,UISearchDisplayController.tableView 被父 UITableViewsections 覆盖。

编辑

这里是一些用于处理搜索和填充UITableView的代码

#pragma mark - Table View

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

    // Count sections in FRC
    if (tableView == self.searchDisplayController.searchResultsTableView) 
        return 1;
     else 
        return [[self.groupsFRC sections] count];
    


-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

    // Count groups in each section of FRC
    if (tableView == self.searchDisplayController.searchResultsTableView) 
        return [searchResults count];
     else 
        return [[[self.groupsFRC sections] objectAtIndex:section] numberOfObjects];
    


-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

    // Get reusable cell
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"GroupCell"];

    // If there isn't any create new one
    if (cell == nil) 
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"GroupCell"];
    

    Group *group = [self determineGroupInTableViev:tableView atIndexPath:indexPath];

    cell.textLabel.text = group.name;

    // Checking if group has been selected earlier
    if ([self.selectedGroups containsObject:@@"name" : group.name, @"id" : group.cashID]) 
        [cell setAccessoryType:UITableViewCellAccessoryCheckmark];
     else 
        [cell setAccessoryType:UITableViewCellAccessoryNone];
    

    return cell;


// Adding checkmark to selected cell
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

    UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
    Group *group = [self determineGroupInTableViev:tableView atIndexPath:indexPath];

    // Checking if selected cell has accessory view set to checkmark and add group to selected groups array
    if (selectedCell.accessoryType == UITableViewCellAccessoryNone)
    
        selectedCell.accessoryType = UITableViewCellAccessoryCheckmark;
        [self.selectedGroups addObject:@@"name" : group.name, @"id" : group.cashID];
        NSLog(@"%@", self.selectedGroups);
    
    else if (selectedCell.accessoryType == UITableViewCellAccessoryCheckmark)
    
        selectedCell.accessoryType = UITableViewCellAccessoryNone;
        [self.selectedGroups removeObject:@@"name" : group.name, @"id" : group.cashID];
        NSLog(@"%@", self.selectedGroups);
    

    // Hiding selection with animation for nice and clean effect
    [tableView deselectRowAtIndexPath:indexPath animated:YES];


#pragma mark - Filtering

- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope

    NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:@"SELF.name contains[c] %@", searchText];
    searchResults = [Group MR_findAllSortedBy:@"caseInsensitiveName" ascending:YES withPredicate:resultPredicate];


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

    [self filterContentForSearchText:searchString
                               scope:[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
    return YES;


-(void)searchDisplayController:(UISearchDisplayController *)controller didHideSearchResultsTableView:(UITableView *)tableView

    // Realoading main table view when search result did hide
    [self.tableView reloadData];

【问题讨论】:

如果您使用的是魔法唱片,那么您为什么要使用 Fechcontroller?最好的方法是您使用一个通用数组并获取已过滤和未过滤的数据并重新加载您的表格视图 我使用 'NSFetchedResultController' 因为它提供了运行中的人口。 提供有关您的表视图和搜索控制器的详细信息。仅使用搜索栏并根据更改更新 FRC 谓词可能更容易...... @Wain 我已经用源代码更新了我的问题。如果您想要更多内容,请告诉我。 【参考方案1】:

我通常不觉得UISearchDisplayController 很有用。这是因为:

    我已经有一个表格视图 搜索时表格视图单元格的显示应该和正常的一样 我仍然需要提供所有的表委托和数据源方法 我还是要提供搜索代理方法

因此,我通常为表格视图提供单一数据源,并根据输入的搜索条件操作该数据源。

这适用于 FRC 或简单数组。对于 FRC,编辑获取请求(谓词)和performFetch:,其他一切都是自动的。对于数组,使用sortedArrayUsingDescriptors: 并重新加载(我有一个源数据的原始数组,以及用于表方法的另一个数组引用。第二个数组指向原始数组实例或从排序创建的新实例) .

在这两种情况下,我都有与使用UISearchDisplayController 类似的方法,但表方法中没有if 语句。任何if 语句和数据源的变异,仅在搜索方法中处理(调用频率要低得多)。


给类添加一个属性:

@property (strong, nonatomic) NSPredicate *filterPredicate;

在搜索文本中更改委托:

if (text.length > 0) 
    self.filterPredicate = [NSPredicate predicateWithFormat:...];
 else 
    self.filterPredicate = nil;


[self refreshFRC];

在搜索取消时,只需将谓词置零并刷新。

还有刷新:

- (void)refreshFRC 
    NSFetchRequest *fetch = ...;

    if (self.filterPredicate != nil) 
        fetch.predicate = self.filterPredicate;
    

    self.frc.fetchRequest = fetch;

    [self.frc performFetch:...];

【讨论】:

那么,如果没有UISearchDisplayController,我该如何处理这种排序?通过提供原始表数据源? 基本上,使用UISearchBarDelegate 代替UISearchDisplayDelegate 方法,并编辑搜索内容的获取请求。从技术上讲,您可以保留搜索控制器,这是个人喜好。关键是编辑获取请求并重新获取。删除搜索控制器只会使代码更清洁恕我直言。 你的意思是搜索方法?我假设标准显示是所有组实体? 是的,主要的UITableView 包含所有组。我不知道如何处理这个UISearchBar 员工......我认为我的主表NSFetchedResultController 很好。 我想我明白了你的意思并使用单个 FRC 实现了这一点,但我仍然有一个 problem 可以重新填充。

以上是关于一个用于 UITableview 和 UISearchDisplayController 的 NSFetchedResultController的主要内容,如果未能解决你的问题,请参考以下文章

将一个 NSManagedObject 用于 UITableView 部分,它与另一个 NSManagedObject 的关系用于行?

NSLayoutConstraint 用于 UITableView 上的视图

ContentInsets 不适用于 UITableView

从两个 NSArrays 中包含的数据准备一个 NSDictionary 用于 UITableView

如何传递和收集 UITableView 中按下的信息

UITableView detailTextLabel 不适用于自定义单元格