无限滚动 UITableView 在从服务器检索数据后向上滚动时出现故障

Posted

技术标签:

【中文标题】无限滚动 UITableView 在从服务器检索数据后向上滚动时出现故障【英文标题】:Infinite Scrolling UITableView is glitchy when scrolling up after retrieving data from server 【发布时间】:2015-01-30 11:17:43 【问题描述】:

我有一个包含来自服务器的 15 个帖子的提要。当我在列表末尾向下滚动到 3 时,我会 ping 服务器以查看接下来的 15 个帖子。这个功能很好用。但是,当我开始向上滚动时,UITableViewCells 经常跳起来,好像 Cell 5 现在正在填充 Cell 4,而 Cell 4 现在正在填充 Cell 3,等等。要么就是这样,要么 UITableView 滚动只是向上跳。

当我到达 UITableView 的最顶部,然后继续向下滚动浏览我的所有数据然后备份时,它可以完美运行。我的桌子有绘图问题吗?

编辑:所以,我了解到这是因为我所有单元格的高度都是动态的。我很确定当我向上滚动时,我的 UITableView 正在计算并设置适当的高度,这导致了跳跃动作。我不确定如何减轻这种情况。

【问题讨论】:

我不希望有太多帮助。不幸的是,如果您无法运行实际项目,这些问题几乎是无法调试的。您必须在所有滚动和 tableDataSource 方法上添加 println(),并查看诸如 contentOffset 和请求的 table 的 indexPaths 之类的东西。一旦你弄清楚了故障发生时究竟发生了什么,你就成功了。 如果这是 ios8 的另一个建议 - 不要将嵌套视图放在单元格的内容视图中,而是将您的内容直接放在内容视图中。我有一个非常相似的问题,我以这种方式解决了。 @GuybrushThreepwood 我不确定我明白你的意思 单元格的内容。它们是否在单元格内容视图中的视图中? 我的单元格的内容直接在 ContentView 中,而不是 View。 【参考方案1】:

我从未在 iOS8 中使用过动态单元格大小的新功能,但我可以给你一些提高表格视图性能的建议。应该是评论,但不合适。

如果可以的话,缓存已经显示的单元格的高度。很容易将字典与某种 id 配对就可以解决问题 请注意,您的单元格子视图之间没有复杂的布局 检查您是否正在绘制需要离屏渲染的内容,例如角半径、剪裁等

我不知道动态单元在 ios8 上是如何工作的,但我分享了我的一段代码。这很简单。我有一个用作原型的单元格,每次我需要计算一个单元格高度时,我都会用我的数据提供它,我强制它的布局让我得到正确的高度。获得高度后,我将其保存在 NSDictionary 中,使用 postID(它是一个类似 twitter 的应用程序)作为键。 仅当未缓存单元格高度时才会发生这种情况。如果它被缓存,则返回高度。

- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
    CGSize  size = CGSizeZero;
    NSDictionary * data = self.timelineData[indexPath.row];
    if (data[KEY_CELL_IDENTIFIER] == CellIdentifierPost) 
        NSNumber * cachedHeight = [self.heightCaches objectForKey:@([(AFTimelinePostObject*)data[KEY_CELL_DATA] hash])];//[(AFTimelinePostObject*)data[KEY_CELL_DATA] timelinePostObjectId]];
        if (cachedHeight) 
            return (CGFloat)[cachedHeight doubleValue];
        
        [_heightCell configureCellWith:data[KEY_CELL_DATA]];
        size = [_heightCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
        [self.heightCaches setObject:@(size.height) forKey:@([(AFTimelinePostObject*)data[KEY_CELL_DATA] hash])];//[(AFTimelinePostObject*)data[KEY_CELL_DATA] timelinePostObjectId]];
    
    else if (data[KEY_CELL_IDENTIFIER] == CellIdentifierComment)
        NSNumber * cachedHeight = [self.heightCaches objectForKey:@([(AFTimelinePostComments*)data[KEY_CELL_DATA] hash])];//[(AFTimelinePostObject*)data[KEY_CELL_DATA] timelinePostObjectId]];
        if (cachedHeight) 
            return (CGFloat)[cachedHeight doubleValue];
        
        [_heightCommentCell configureCellWith:data[KEY_CELL_DATA]];
        size = [_heightCommentCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
        if (size.height < 80.0f) 
            size = (CGSize) 
                .width = NSIntegerMax,
                .height = 115.f
            ;
        
        else if (size.height > 180.0f) 
            size = (CGSize) 
                .width = NSIntegerMax,
                .height = 180.f
            ;
        

        [self.heightCaches setObject:@(size.height) forKey:@([(AFTimelinePostComments*)data[KEY_CELL_DATA] hash])];//[(AFTimelinePostObject*)data[KEY_CELL_DATA] timelinePostObjectId]];

    
    else 
        size = (CGSize) 
            .width = NSIntegerMax,
            .height = 50.f
        ;
    

    return size.height;

【讨论】:

我认为缓存不会有任何作用。我仍然会注意到效果。问题源于它使用我的 EstimatedHeight ,然后当它显示在屏幕上时,它会迅速将高度更改为应有的高度。当然,因为我是无限滚动的,我认为我什至不应该费心重新加载屏幕底部单元格上方的任何单元格的数据。重新加载数据使其重新计算,但我不需要它来这样做。我只需要它从我的单元格向下计算。也许有办法做到这一点。 我更新了我的评论。不小心按入过早的进入。 我做了一个应用程序,它根据内容(带/不带文本和带/不带图像)计算动态单元格高度,当您想要向后滚动时,缓存高度实际上产生了巨大的差异。它大大提高了性能。由于每次在单元格内推送数据时出队的机制始终是它的新数据,并且无论如何它都需要获得高度。就像你有一个细胞池,当一个人走出屏幕进入这个池子时,当电视进入这个池子时。需要它从那里拉出并根据 indexpath 更改它的数据。无限滚动没关系,当然也可以是iOS8的bug。 我应该在哪里调用缓存?以及如何?【参考方案2】:

不幸的是,似乎没有一个简单的答案。我在多个 iOS 应用程序上一直在努力解决它。

我发现的唯一解决方案是在 UITableView 再次出现时以编程方式滚动到它的顶部。

[self.tableView setContentOffset:CGPointMake(0, 0 - self.tableView.contentInset.top) animated:YES];

self.tableView.contentOffset = CGPointMake(0, 0 - self.tableView.contentInset.top);

希望这是一个可接受的解决方法,同时仍然能够使用动态单元格高度 =)

【讨论】:

以上是关于无限滚动 UITableView 在从服务器检索数据后向上滚动时出现故障的主要内容,如果未能解决你的问题,请参考以下文章

UITableView 在更新 NSFetchedResultsController 时保持滚动在正确的位置

无限网格滚动和局部过滤

UITableView 使用 UIPIckerView 滚动到特定部分?

如果我缩放浏览器,无限滚动会停止

如何使用主干分批获取数据以进行“无限滚动”?

滚动上的 UITableView 不会调用 didSelectRowAtIndexPath