【问题标题】:NSFetchedResultsController prepend a row or sectionNSFetchedResultsController 前置一行或部分
【发布时间】:2012-06-13 11:20:08
【问题描述】:

我有一个使用标准 NSFetchedResultsController 填充的 UITableView。但是我想在前面添加一行或一个部分(最好是行,但两者都可以正常工作。)

我现在可能看到这样做的唯一方法是在处理处理来自 NSFetchedResultsController 的数据的部分/行时手动重写所有 NSIndexPath,以欺骗它查看索引 0 处的部分并从索引 0 处的行开始. 然而,这似乎是一个非常糟糕的主意,很快就会让人感到困惑,所以我希望避免这种情况。

当你第一次启动官方 Twitter 应用程序时,一个很好的例子是,我会引导你从你的朋友列表中添加一些人。

红色部分几乎是我想要实现的,我假设黄色部分是同一部分中 NSFetchedResultsController 的结果(尽管使用它们的自定义样式,它可能是一个单独的部分。)

【问题讨论】:

标签: ios uitableview nsfetchedresultscontroller nsindexpath


【解决方案1】:

这可以以相当干净的方式完成。

我假设您从使用 Apple 示例代码的标准 NSFetchResultsController 设置的标准 tableview 开始。

首先你需要两个实用函数:

- (NSIndexPath *)mapIndexPathFromFetchResultsController:(NSIndexPath *)indexPath
{
    if (indexPath.section == 0)
        indexPath = [NSIndexPath indexPathForRow:indexPath.row+1 inSection:indexPath.section];

    return indexPath;
}

- (NSIndexPath *)mapIndexPathToFetchResultsController:(NSIndexPath *)indexPath
{
    if (indexPath.section == 0)
        indexPath = [NSIndexPath indexPathForRow:indexPath.row-1 inSection:indexPath.section];

    return indexPath;
}

这些应该是不言自明的——当我们想使用来自获取的结果控制器的索引路径来访问表时,它们只是处理添加额外行的助手,或者在采用其他方式时删除它。

然后我们需要创建额外的单元格:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"MyCellId";

    if (indexPath.section == 0 && indexPath.row == 0)
    {
        UITableViewCell *cell;
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] autorelease];
        cell.selectionStyle = UITableViewCellSelectionStyleGray;
        cell.textLabel.text = NSLocalizedString(@"Extra cell text", nil);

        return cell;
    }

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

    [self configureCell:cell atIndexPath:indexPath];

    return cell;
}

确保我们正确配置它(configurecell 只会为来自获取结果控制器的单元格调用):

// the indexPath parameter here is the one for the table; ie. it's offset from the fetched result controller's indexes
- (void)configureCell:(SyncListViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
    indexPath = [self mapIndexPathToFetchResultsController:indexPath];

    id *obj = [fetchedResultsController objectAtIndexPath:indexPath];
    <... perform normal cell setup ...>
}

并告诉tableview它存在:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    NSInteger numberOfRows = 0;

    if ([[fetchedResultsController sections] count] > 0) {
        id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
        numberOfRows = [sectionInfo numberOfObjects];
    }

    if (section == 0)
        numberOfRows++;

    return numberOfRows;
}

并响应选择:

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

    if (indexPath.section == 0 && indexPath.row == 0)
    {
        [self doExtraAction];
        return;
    }

    ... deal with selection for other cells ...

然后重新映射我们从结果控制器获得的任何更新:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
    UITableView *tableView = self.tableView;

    indexPath = [self mapIndexPathFromFetchResultsController:indexPath];
    newIndexPath = [self mapIndexPathFromFetchResultsController:newIndexPath];

    switch(type) {
        ... handle as normal ...

【讨论】:

  • 如果采用这种方式,我喜欢动态删除第一行是否可行。
  • @cocoaNoob 是的,那会很好。只要您正确映射 indexPath,您基本上可以做任何事情。
  • 是的,我尝试使用它而不是使用 1,我使用的是实例变量。一个非常好的和清晰的方法。感谢您分享您的解决方案@JosepH。
  • 这种方法是否可能不适用于使用瞬态属性作为sectionNameKeyPath: 参数的 fetchedResultsController?我看到我的 tableView 在第 0 部分包含 2 行(其中第一行是手动添加的),在第 1 部分包含 4 行;如果我从第 0 部分删除 1 行,我会收到这个非常奇怪的错误:无效更新:第 0 部分中的行数无效。更新后现有部分中包含的行数 (5) 必须等于数字更新前该部分中包含的行数 (4),加上或减去行数...
  • ... 从该部分插入或删除(0 插入,0 删除)加上或减去移入或移出该部分的行数(0 移入,0 移出). 正如我在 tableView 中看到的那样,fetchedResultsController 认为我在第 0 节中有 5 个对象,而不是第 1 节中的 1 + 4 节,这让我认为这种方法行不通使用 sectionNameKeyPath 生成的部分。我说的对吗?
【解决方案2】:

我理解您对复杂性的担忧,但实际上只是在cellForRowAtIndexPath: 中将numberOfRowsInSection: 加1 和indexPath.row 加1(除了为第0 行添加代码)。

另一种解决方案不必非常复杂就会变得更加麻烦。

话虽如此,您提出的“标题”似乎确实是节标题的典型候选者。

【讨论】:

  • 这是一个公平的观点,我主要是在检查 NSFetchedResultsController 上是否有一些我不知道的方法可以让你在它返回的结果中添加或附加一组行,这样你可以自定义它们。
猜你喜欢
  • 2012-12-30
  • 1970-01-01
  • 1970-01-01
  • 2014-01-29
  • 1970-01-01
  • 1970-01-01
  • 2016-04-20
  • 2021-03-05
  • 1970-01-01
相关资源
最近更新 更多