tableView:cellForRowAtIndexPath:不仅为可见单元格调用吗?

Posted

技术标签:

【中文标题】tableView:cellForRowAtIndexPath:不仅为可见单元格调用吗?【英文标题】:tableView: cellForRowAtIndexPath: get called not only for visible cells? 【发布时间】:2012-02-14 23:57:29 【问题描述】:

我有一个带有部分的 tableView,可以打开和关闭。所以,当我点击一个部分来打开它时,它会被单元格填满,-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *) 被调用的次数与我在-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 中提供的次数完全相同。

正确吗?不应该只是可见细胞的数量吗?

因为在我的情况下,我的情况很糟糕:我有很多自定义单元格(50~100 个单元格)并且为每个单元格调用 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *) 会减慢一个部分的打开速度,因为每次从 nib 读取执行和单元格内容正在填充图像。 我检查了-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *) 中单元格的可见性,如下所示:

if ([[self.tableView indexPathsForVisibleRows] containsObject:indexPath])
    NSLog(@"visible %@", indexPath);

它显示在 45 个单元格中,只有 6 或 7 个是可见的。其他人不在可见区域内。但是仍然可以创建细胞。 代码如下:

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath    
static NSString *CellIdentifier = @"IVCell";
IVCamera *camera = [server.cameras objectAtIndex:indexPath.row];

IVServerListViewCell *cell = (IVServerListViewCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) 
    [[NSBundle mainBundle] loadNibNamed:@"IVServerListCell" owner:self options:nil];
    cell = (IVServerListViewCell*)_tableViewCell;
    self.tableViewCell = nil;



[cell textLabel].text = camera.cameraName;
cell.preview = camera.preview;
cell.userData = camera; 
cell.isEnabled = (server.isInactive)?NO:camera.isOnline;

return cell;

它仍然正确吗?还是我错过了什么?

【问题讨论】:

对于动态大小的单元格,tableView 需要知道 contentSize 是什么用于滚动性能。如果您没有指定估计的CellHeight,那么它将加载每个单元格。要解决您的问题,只需添加一个近似高度的估计HeightForRowAt,然后您会看到它只会加载可见单元格。 【参考方案1】:

增加你的

UITableview 的estimatedRowHeight。

【讨论】:

谢谢大佬,很好的解决方案!【参考方案2】:

好吧,我以某种方式解决了我的问题。以下是我的想法和想法,我是如何找到解决方案的。也许它可能对某人有帮助。

我已经在开始部分事件期间使用 Instruments 指示了内存分配和调用堆栈。它告诉我,大部分时间都花在从 nib 文件加载单元格上。

首先,我所做的是减小 nib 文件的大小,即最小化自定义 tableview 单元格中使用的视图数量(现在它只有 2 个视图和 2 个标签,而不是 6 个视图,之前有 2 个图像和 2 个标签)。它让我在细胞加载方面有了一些改进。 Apple 文档建议使用尽可能少的视图并且不要使用透明度。所以请注意这些建议。

其次,正如我之前发现的,并非所有由-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *) 创建的单元格都是可见的,我决定以某种方式减少从 nib 文件加载新单元格的数量。为了实现这一点,我想到了一个简单的想法:为不可见的行返回空白默认单元格,同时为可见的行从 nib 加载自定义单元格。这是一段代码:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

    if ([self index:indexPath isInvisibleInTableView:tableView])
        return [self getBlankCellForTableView:tableView];

    // the rest of the method is the same
    ...


-(BOOL)index:(NSIndexPath*)indexPath isInvisibleInTableView:(UITableView*)tableView

    NSMutableArray *visibleIndexPaths = [self getExtendedVisibleIndexPathsForTableView:tableView];

    return ![visibleIndexPaths containsObject:indexPath];


-(UITableViewCell*)getBlankCellForTableView:(UITableView*)tableView

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"IVBlankCell"];
    if (!cell)
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"IVBlankCell"] autorelease];

    return cell;

如您所见,我不只是使用-(NSArray*)indexPathsForVisibleRows 的tableview 方法来检测可见单元格。相反,我编写了自己的方法-(NSMutableArray*)getExtendedVisibleIndexPathsForTableView:(UITableView*)tableView。这是必要的,因为由于某种原因,在使用 -(NSArray*)indexPathsForVisibleRows 时,最后一个可见单元格旁边的单元格或第一个可见单元格之前的单元格被创建为空白单元格,并且在滚动时看起来像空单元格。为了克服这个问题,在-(NSMutableArray*)getExtendedVisibleIndexPathsForTableView: (UITableView*)tableView 中,我将边框单元格添加到可见数组单元格中:

-(NSMutableArray*)getExtendedVisibleIndexPathsForTableView:(UITableView*)tableView
    NSArray *visibleIPs = [tableView indexPathsForVisibleRows];

    if (!visibleIPs || ![visibleIPs count])
        return [NSMutableArray array];

    NSIndexPath *firstVisibleIP = [visibleIPs objectAtIndex:0];
    NSIndexPath *lastVisibleIP = [visibleIPs objectAtIndex:[visibleIPs count]-1];

    NSIndexPath *prevIndex = ([firstVisibleIP row])?[NSIndexPath indexPathForRow:[firstVisibleIP row]-1  inSection:[firstVisibleIP section]]:nil;
    NSIndexPath *nextIndex = [NSIndexPath indexPathForRow:[lastVisibleIP row]+1 inSection:[lastVisibleIP section]];

    NSMutableArray *exVisibleIndexPaths = [NSMutableArray arrayWithArray:[tableView indexPathsForVisibleRows]];

    if (prevIndex)
        [exVisibleIndexPaths addObject:prevIndex];
    [exVisibleIndexPaths addObject:nextIndex];

    return exVisibleIndexPaths;

因此,我减少了打开包含大量自定义单元格的部分的时间,这已通过 Instruments 跟踪证明并在体验应用时感受到。

【讨论】:

太棒了,我也有这样的问题,你的技术非常有用,问题已经解决了。【参考方案3】:

只需添加 UITableViewCell 的估计高度

在我的情况下,问题是:cellforRowAtIndexPath 被调用 array.count 次数,而显示的单元格少于 array.count。

为了解决这个问题,我刚刚更换了,

(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

与,

(CGFloat)tableView:(UITableView)tableViewestimateHeightForRowAtIndexPath:(nonnull NSIndexPath)indexPath;

【讨论】:

【参考方案4】:

检查您的 tableview 大小。 可能是您的 tableview 高度非常大,它会一直加载单元格,直到您的单元格填满所有 tableview 大小..

【讨论】:

当我知道我的所有单元格的高度 > 320pts 时,我将 tableView 的estimatedRowHeight 设置为 100pts 的低值。这导致不必要地加载额外的单元格,直到 100 + 100 + ... > tableView 可见边界。然后我将estimatedRowHeight 更改为320,加载的行数减少了。【参考方案5】:

这似乎是正确的。关于优化加载本身的想法在于“dequeueReusableCellWithIdentifier”的工作原理。 如果您从远程位置加载图像,这就是您想要优化代码的地方。但不是来自单元格的加载,因为这里看起来是正确的。

【讨论】:

听起来很伤心。我已经暂时关闭了用图像填充单元格,与只有 9 个单元格的部分相比,打开 42 个单元格的部分仍然有小的烦人的延迟。我认为这是唯一的方法 - 减少自定义单元格的笔尖大小并减少其中使用的 uiviews 的数量。你觉得怎么样? 我很好奇您是如何展开/折叠 UITableView 中的部分的。因为默认情况下不存在。 使用insertRowsAtIndexPaths:withRowAnimation:deleteRowsAtIndexPaths: withRowAnimation: 这是否意味着您的部分是正常单元格,只是看起来不同?尝试通过添加这些单元格来重新加载表格视图。您需要将一些代码添加到 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *) 以过滤是否要返回单元格,并且您还需要更改单元格的计数。跨度> 没有。部分是它定义的 UITableView 含义的“部分”。因此它们的单元格是在委托的特殊 -(UIView*)tableView:(UITableView*)tableView viewForHeaderInSection:(NSInteger)section 方法中生成的【参考方案6】:

我使用了一些类似的技术,但由于 indexPathsForVisibleRows 已排序,因此您不需要使用 containsObject。相反,您可以这样做:

//
// Checks if indexPath is visible in current scroll state, we are expanding bounds by 1
// because the cells that are next to the last one visible or the cells that are previous
// to the first one visible could look empty while scrolling.
//
- (BOOL)isIndexPathVisible:(NSIndexPath *)indexPath

    NSInteger row = [indexPath row];
    NSArray *visible = [self.tableView indexPathsForVisibleRows];
    NSInteger count = [visible count];
    NSInteger first = (count > 0) ? MAX([visible[0] row] - 1, 0): 0;
    NSInteger last = (count > 1) ? [visible[1] row] + 1: first + 2;

    return row >= first && row <= last;

顺便说一句;这假设您只使用一个部分。它不适用于一个以上的部分。

【讨论】:

【参考方案7】:

添加一个 else 解决了我的问题。 我重置了对单元格所做的任何更改。

if (! self.cell) 
    self.cell = [[LanguageCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
    self.cell.accessoryType = UITableViewCellAccessoryNone;

else

    self.cell.checkImage.image = NO;


【讨论】:

以上是关于tableView:cellForRowAtIndexPath:不仅为可见单元格调用吗?的主要内容,如果未能解决你的问题,请参考以下文章

我的应用程序崩溃,原因'UITableView dataSource 必须从 tableView 返回一个单元格:cellForRowAtIndexPath

如何在一个 tableView 中使用两个不同的自定义单元格?使用情节提要,iOS 7

以编程方式更改表格单元格中按钮的图像

图像下载后调整表格单元格大小

设置静态表格视图单元格的复选标记

添加单元格和使用 UITableView 滚动时出现图形故障