NSFetchedResultsController前置一行或一节

时间:2012-06-09 22:39:57

标签: ios uitableview nsfetchedresultscontroller nsindexpath

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

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

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

Screenshot of Twitter app's suggestions page, with the relevant sections highlighted

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

2 个答案:

答案 0 :(得分:33)

这可以以相当干净的方式进行。

我假设你开始使用标准的tableview设置标准NSFetchResultsController,它使用Apple的示例代码。

首先,您需要两个实用功能:

- (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;
}

这些应该是相当自我解释的 - 当我们想要使用来自获取的结果控制器的索引路径来访问表时,或者当它们以另一种方式移除时,它们只是处理添加额外行的帮助。 / p>

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

- (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 ...

答案 1 :(得分:3)

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

另一种解决方案不一定非常复杂,变得更加麻烦。

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