删除UITableView的部分后,标题视图的框架永远改变

时间:2013-06-07 08:06:02

标签: ios objective-c uitableview uiview

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

在我动画删除了一个部分并重新加载另一个部分之后,只有这些部分的标题视图“消失了”。经过一番搜索,我发现这些标题视图的帧已经改变了。标题视图的Frame.X已从UITableViewRowAnimationRight移位器删除到屏幕宽度(320)右侧,而UITableViewRowAnimationAutomatic移动了的Y移位了viewForHeaderInSection:

主要问题和困惑来自于我无法改变这些帧!好吧,他们一开始就改变了,但没有任何视觉上的变化,而_first帧的另一个调用再次转移。

我是这样做的:

假设我们有3个部分,每个部分的数据位于数组_second_thirdhandleTouchUp:之一。按钮的touchupinside我们称之为_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; } 保持不变)

{{1}}

Here is full code of ViewController.mtest project

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

1 个答案:

答案 0 :(得分:3)

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

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

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

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

删除第一部分后,第三部分现在有了index = 1,表视图委托方法读取的数据不正确(包括标题视图),因为你拥有基于部分索引的所有内容。

为了解决这个问题,我更改了标题视图缓存,以将数据数组用作键(_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选择的自动重装动画是Fade动画。 这会导致标题视图淡出。我通过在应用程序中添加另一个按钮来测试这一点,该按钮为所有缓存的标题视图设置alpha = 1。重新加载动画后,我按下此按钮,标题视图显示正确。

我尝试过使用其他动画,但看起来不太好。

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

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

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