【问题标题】:Custom cell on empty table with NSFetchedResultsController带有 NSFetchedResultsController 的空表上​​的自定义单元格
【发布时间】:2013-07-20 10:24:20
【问题描述】:

当我的 UITableView 为空时,我正在尝试放置一个自定义的空单元格。

我使用以下代码:

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

    int count = [self.fetchedResultsController fetchedObjects].count;
    if (count == 0) {
        return 1;
    }
    return count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell * cell = nil;
    int count = [self.fetchedResultsController fetchedObjects].count;
    if (count == 0) {
        // return empty cell
        cell = [self getEmptyCellOfTableView:tableView cellForRowAtIndexPath:indexPath];
    } else {
        cell = [self getMyQuestionCellOfTableView:tableView cellForRowAtIndexPath:indexPath];
    }
    return cell;
}

FRC didChangeObject 实现:

- (void)controller:(NSFetchedResultsController *)controller
   didChangeObject:(id)anObject
       atIndexPath:(NSIndexPath *)indexPath
     forChangeType:(NSFetchedResultsChangeType)type
      newIndexPath:(NSIndexPath *)newIndexPath
{       
    if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext)
    {
        switch(type)
        {
            case NSFetchedResultsChangeInsert:
                [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
                break;

            case NSFetchedResultsChangeDelete:
                [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
                break;

            case NSFetchedResultsChangeUpdate:
                [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
                break;

            case NSFetchedResultsChangeMove:
                [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
                [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
                break;
        }
    }
}

这很好用,问题是当 fetchedObject.count > 0 比我得到以下错误:

CoreData: error: Serious application error.  
An exception was caught from the delegate of NSFetchedResultsController during a call to -
controllerDidChangeContent: Invalid update: invalid number of rows in section 0.
The number of rows contained in an existing section after the update (1)
must be equal to the number of rows contained in that section before the update (1),
plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) 
and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). 
with userInfo (null)

我知道发生这种情况是因为我插入了 1 个新行并且计数保持在 1 而不是变为 2。

我该如何解决它以适应我的行为?

【问题讨论】:

  • 显示您的 FRC 委托方法实现。更改为 [tableView reloadData] 进行快速修复。
  • @Wain 您能否举例说明在何处更改为[tableView reloadData] 以及您希望我展示哪些 FRC 方法?
  • @Danpe:每个更改都必须致电reloadData,这样您就不会再收到任何动画更新。我认为这就是为什么 Wain 称其为“快速修复”的原因。 - 如我的回答所示,应该可以通过适当的动画表格视图更新来解决这个问题。

标签: ios objective-c core-data uitableview


【解决方案1】:

表视图更新后,numberOfRowsInSection 返回的值必须与 匹配前面的行数加上插入的行数减去 删除的行。

在您的情况下,当插入第一个对象时,insertRowsAtIndexPaths 在新对象的 FRC 委托方法中被调用,但不是显示额外的行,而是“空” 单元格被替换为不同的单元格,所以行数仍然是一。

如果您修改 FRC 委托方法,它应该可以工作 controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: 如下:

case NSFetchedResultsChangeInsert:
    if ([[self.fetchedResultsController fetchedObjects] count] == 1) {
        // First object inserted, "empty cell" is replaced by "object cell"
        [tableView reloadRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
    } else {
        [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
    }
    break;

case NSFetchedResultsChangeDelete:
    if ([[self.fetchedResultsController fetchedObjects] count] == 0) {
        // Last object removed, "object cell" is replaced by "empty cell"
        [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
    } else {
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
    }
    break;

【讨论】:

    【解决方案2】:

    PSA:如果您还没有查看 Yang Meyer 的在空表上显示单元格的解决方案,我建议您这样做。这是处理空表或空 NSFetchedResultsController 的一种聪明且简单得多的方法。

    Yang Meyer's Best Practice for Handling Empty Table Views

    我使用它的方式是在我的viewDidLoad 中,我最初假设结果是空的。只有当我的fetchedResultsController 有对象时,我才会将数据源改回来。

    FJFetchedDataController.h

    @interface FJFetchedDataTableController : UITableViewController <NSFetchedResultsControllerDelegate, UITableViewDelegate>
    
    @end
    

    FJFetchedDataController.m

    @implementation FJFetchedDataTableController {
    @private
        FJEmptyDataSource *_emptyDataSource;
    };
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // create the empty data source
        _emptyDataSource = [[FJEmptyDataSource alloc] init];
        // point the tableView to the empty data source
        self.tableView.dataSource = _emptyDataSource;
    
        self.fetchedResultsController.delegate = self;
    
        // TODO: configure your fetchedResultsController here
    
        NSError *error;
        if (![self.fetchedResultsController performFetch:&error]) {
            // TODO: error handling
        }
    
        if ([self.fetchedResultsController.fetchedObjects count]) {
            // set the table source back to itself (or whatever you want as the data source)
            // since we know we have results
            self.tableView.dataSource = self;
            return;
        }
    }
    

    简单,但非常有效。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-01
      • 2010-10-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多