5

我正在重用标题,将它们保存在字典中,其中节号是键,标题视图是值。

在我动画删除了一个部分并重新加载另一个部分后,只有这些部分的标题视图“消失了”。经过一番搜索,我发现这些标题视图的框架已更改。从屏幕宽度 (320) 向右移位器Frame.X删除的部分的标题视图,以及用移位器重新加载的部分。UITableViewRowAnimationRightUITableViewRowAnimationAutomaticY

主要问题和困惑来自我无法更改这些框架的事实!好吧,它们一开始会发生变化,但在视觉上没有任何变化,并且在另一次调用viewForHeaderInSection:帧时再次移动。

我是这样做的:

假设我们有 3 个部分,每个层的数据位于数组_first和之一_second_third。通过按钮的 touchupinside 我们调用handleTouchUp:. (_second保持不变)

- (void) handleTouchUp: (UIButton *)sender
{
    if ([_first count] == 0) {
        sectionCount = 3;
        _first = [NSMutableArray arrayWithObjects: @"First 1", @"First 2", nil];
        _third = [NSMutableArray arrayWithObjects: @"Third 1", @"Third 2", @"Third 3", @"Third 4", @"Third 5", nil];
        [tableView reloadData];
    } else {
        sectionCount = 2;
        [_first removeAllObjects];
        _third = [NSMutableArray arrayWithObjects: @"First 1", @"First 2", @"Third 1", @"Third 2",
                                                   @"Third 3", @"Third 4", @"Third 5", nil];
        [tableView beginUpdates];
        [tableView deleteSections: [NSIndexSet indexSetWithIndex: 0] withRowAnimation:UITableViewRowAnimationRight];
        [tableView reloadSections: [NSIndexSet indexSetWithIndex: 2] withRowAnimation:UITableViewRowAnimationAutomatic];
        [tableView endUpdates];
    }
}


- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    NSNumber *key = [NSNumber numberWithInteger: section];
    UIView *header = [_headers objectForKey: key];
    if (header) {

    } else {
        header = [[UIView alloc] init];
        switch (section) {
            //Doing something with header here (e.g. set background color)
        }
        [_headers setObject:header forKey: key];
    }

    [header setFrame: CGRectMake(0, 0, 320, 25)];

    return header;
}

这是 ViewController.m测试项目的完整代码

更新: 项目中可能还有其他一些错误,但这并不重要——它是测试项目。唯一重要的问题是无效的标题视图。删除无效视图并创建新视图不是解决方案——它扼杀了重用标题视图的意义。

4

1 回答 1

3

我在您的项目中发现了几个错误,这些错误实际上导致了一些崩溃,我相信他们也对您在应用程序中看到的标题视图错误负责。在我修复了这些之后,应用程序对我来说似乎可以正常运行。

这是您项目的更新版本:https ://bitbucket.org/reydan/tableheaderviews

我试图让它与你最初的项目尽可能相似,但我不得不做一些改变。如果您认为它与您想要的功能不匹配,请发表评论,我会更新(这是我推断项目应该表现的样子)下面解释的所有更改,也可以在这个更新的项目中看到。

我发现的问题与以下事实有关:如果您从表视图中删除一个部分,则该部分之后的所有部分的索引都会更改(使用-1)。这是正常的,因为节索引实际上是一个索引,而不是分配给每个节的唯一键。

删除第一个 section 后,第三个 section 现在 index = 1 并且 table view 委托方法读取数据不正确(包括标题视图),因为您拥有基于 section index 的所有内容。

为了解决这个问题,我更改了标题视图缓存以使用数据数组作为键(_first、_second 或 _third)。为了使这更容易,我创建了一个可以从多个位置调用并根据节索引和当前节计数返回正确数据数组的方法:

- (NSArray*)dataArrayForSection:(NSInteger)section
{
    if (sectionCount == 3)
    {
        switch (section)
        {
            case 0: return _first;
            case 1: return _second;
            case 2: return _third;
        }
    }
    else
    {
        switch (section)
        {
            case 0: return _second;
            case 1: return _third;
        }
    }

    return nil;
}

现在头部缓存使用数据数组作为键,在handleTouchUp方法中重新分配数组时删除头部条目很重要。

- (void) handleTouchUp: (UIButton *)sender
{
    NSLog(@"Touched!");
    if ([_first count] == 0)
    {
        sectionCount = 3;

        // **INSERTED** Removes the cache for the _first and _third keys because you are recreating the arrays again,
        // and so you are losing the initial key. Not doing this, results in a memory leak
        [_headers removeObjectForKey:_first];
        [_headers removeObjectForKey:_third];

        _first = [NSMutableArray arrayWithObjects: @"First 1", @"First 2", nil];
        _third = [NSMutableArray arrayWithObjects: @"Third 1", @"Third 2", @"Third 3", @"Third 4", @"Third 5", nil];

        [tableView reloadData];
//        [tableView beginUpdates];
//        [tableView reloadSections: [NSIndexSet indexSetWithIndex: 0] withRowAnimation:UITableViewRowAnimationRight];
//        [tableView reloadSections: [NSIndexSet indexSetWithIndex: 2] withRowAnimation:UITableViewRowAnimationAutomatic];
//        [tableView endUpdates];
    }
    else

我改进的另一件事是重用表格视图单元格。您应该始终尝试并重用现有的 UITableViewCells(除了少数例外)。现在cellForRowAtIndexPath看起来像这样:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSArray* dataarray = [self dataArrayForSection:indexPath.section];


    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"my_text_cell"];
    if (cell == nil)
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"my_text_cell"];


    if (dataarray == nil)
        cell.textLabel.text = @"default";
    else
        cell.textLabel.text = [dataarray objectAtIndex:indexPath.row];


    return cell;
}

如果您有任何问题,请发表评论。

编辑

我尝试按照您的要求更新我的测试项目,以保持缓存完好无损,并且我确实能够重现错误 - 在删除/重新加载动画之后,最后一部分的标题不可见。

我发现 iOS 选择的 Automatic reload 动画是 Fade 动画。这会导致标题视图淡出。我通过在应用程序中添加另一个按钮来测试这一点,该按钮为所有缓存的标题视图设置 alpha=1。重新加载动画后,我按下了这个按钮,标题视图正确显示。

我尝试使用其他动画,但它们看起来并不好。

最后,我认为 Apple 不建议缓存标头视图。在文档中,从 iOS 6.0 开始,有一种新方法可以解决此问题。它的行为类似于出列单元格,但用于页眉/页脚视图
- (id)dequeueReusableHeaderFooterViewWithIdentifier:(NSString *)identifier

因此,如果您的目标是 iOS 6.0+,那么您可以使用它而不是手动缓存标头。否则,我找到的解决方案是仅从缓存中删除已删除和重新加载部分(_first 和 _third)的标题视图。

我更新了项目以在刷新时仅删除最后一节标题

于 2013-06-11T15:37:30.340 回答