如何加速UITableView?

时间:2011-05-30 04:00:39

标签: ios uitableview cgrectmake

我有一个UITableView,在200个部分中有大约400个单元格,它在响应用户交互(滚动,选择单元格)方面有点迟钝。我已经确保检索单元格和标题视图的方法尽可能少,因为它是跑步,我不认为我做任何与众不同的事情让它变得缓慢。单元格和标题只有一个背景图像和文本。有没有其他人遇到过这种问题,你知道如何让它运行得更快一点吗?

编辑:我正在提供赏金,因为我很想得到一些有用的反馈。我认为答案在于我的代码中的问题。相反,我正在寻找重新设计UITableView的策略,以便它运行得更快。我完全愿意添加新代码,我期待听到你们要说的话。

在模拟器和我的设备(iPhone 4)上都观察到了低迷。这是我viewForHeaderInSectioncellForRowAtIndexPath的实现,这是唯一实现非凡的UITableViewDelegate方法。我正在重复使用单元格和标题视图。

- (UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger) section
{
    HaikuHeaderView* view= [m_sectionViews objectAtIndex:section];
    NSMutableArray* array= [m_haikuSearch objectAtIndex:section];
    Haiku* haiku= [array objectAtIndex:0];

    [view.poetLabel setText:[haiku nameForDisplay]];

    return view;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];

        cell.backgroundView= [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"cell gradient2.png"]];

        // (Set up a bunch of label attributes in the cell...)
    }

    NSMutableArray* array= [m_haikuSearch objectAtIndex:indexPath.section];
    Haiku* haiku = [array objectAtIndex:indexPath.row];
    cell.textLabel.text = [haiku.m_lines objectAtIndex:0];

    return cell;
}

9 个答案:

答案 0 :(得分:48)

即使您的单元格实际上非常简单(背景图像和标签),也需要考虑一些事项

图片缓存 这是显而易见的事情 - 如果你在任何地方使用相同的图像,将它加载到UIImage中并重复使用它。即使系统将自己缓存它,直接使用已经加载的系统也不应该受到伤害。

快速计算 另一个相当明显的事情 - 尽可能快地计算高度和内容。不要进行同步提取(网络调用,磁盘读取等)。

图片中的Alpha通道 绘图时的成本是透明度的。由于您的单元格背景背后没有任何内容,请确保在没有Alpha通道的情况下保存图像。这节省了大量的处理。

透明标签 对于背景视图顶部的标签也是如此,不幸的是,使其不透明可能会破坏单元格的外观 - 但这取决于图像。

自定义单元格 通常,子类化UITableViewCell并自己实现drawRect:比构建子视图层次结构更快。您可以使图像成为所有实例使用的类变量。在drawRect:中,您可以在上面绘制图像和文字。

检查合成 模拟器有一个工具来突出显示由于透明度而渲染成本昂贵的部分(绿色正常,红色是alpha混合)。它可以在调试菜单中找到:“Color Blended Layers”

答案 1 :(得分:9)

如果您希望加快代码速度,那么您可以做的最好的事情就是对其进行分析。这有两个原因:

  1. 你可以阅读一些可以提高表性能的方法,例如使用固定高度的单元格和重新使用单元格,它可能有助于实现这些东西(看起来你已经完成了)。但是当谈到加速你的代码时,你真的需要知道你的应用程序花费大部分时间在哪里。可能有一些方法需要很长时间,或者方法相对较快但被调用的次数比预期的要多得多。

  2. 除非你有一些数字需要衡量,否则我们无法知道你为加快速度而做出的改变是否真的有所作为。如果您可以证明您的代码在一个例行程序中花费了80%的时间并且将其降低到35%,那么您就知道自己正在取得进展。

  3. 因此,打破仪器并开始测量。如果可以,最好在进行每项不同的活动时进行测量,以便加速...在滚动时进行一次性能分析,一次在固定时间内选择尽可能多的不同单元格等等。别忘了保存结果,以便以后进行比较。

答案 2 :(得分:5)

请注意以下几点......

  1. 你是在重复使用这些细胞......是的 一个很好的做法..
  2. 确保你没有做任何事 昂贵的计算 cellForRowAtIndexPath回调,或 在一个名为from的函数中 的cellForRowAtIndexPath ..
  3. 你说有背景 图片。你必须的另一个原因 重用你的细胞。
  4. 有关细胞重用的一些好信息是here ..

    编辑:发现这个页面很晚..

    这个SO question主题可能对你有帮助......特别是接受的答案......

答案 3 :(得分:3)

  1. 使用共享图像实例作为背景(每次创建新单元时都为每个文件分配/初始化/释放一个)。当您的表视图很大时,这意味着内存中的背景X单元占用的内存比它应该多得多。

    而不是
    cell.backgroundView= [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"cell gradient2.png"]];
    只需使用:
    cell.backgroundView= [SomeHelperClass sharedBackgroundUIImageResource];

  2. 如果这没有帮助,请使用CG而不是标签和其他子视图(截图在这里有帮助......知道我们在说什么)。

答案 4 :(得分:2)

表视图的委托实现:

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

如果是这样,您可能希望考虑设置UITableViewCell的rowHeight属性。

答案 5 :(得分:1)

您使用了很多子视图吗?

如果是这样,一个好的技巧是,而不是添加大量的标签和图像,使用CoreGraphics绘制它们。

为此,您必须继承UITableViewCell并实现-(void)drawRect:(CGRect)rect方法。

答案 6 :(得分:1)

两个建议:一个是使用-initWithStyle:reuseIdentifier:表示查看单元格而不是-initWithFrame:。另一种是注释掉将cell.backgroundView设置为带有渐变的图像,看看是否是罪魁祸首。每当我在桌面视图中表现不佳时,都是因为图像。

答案 7 :(得分:0)

这里有点偏离主题(因为所有单元格只有一个背景图像):

我的应用在每个单元格中显示不同的图像。向UITableView添加约5个单元格后,表格大幅减速。每次打开视图控制器时,处理所有图像大约需要1-2秒。

if let image = UIImage(contentsOfFile: photoFile){
    // just set it and let system fit it
    //cell.imageView!.image = image

    // 1 - Calculate sized
    let DEFAULT_THUMBNAIL_WIDTH: CGFloat  = (cellHeight / 4) * 5;
    let DEFAULT_THUMBNAIL_HEIGHT: CGFloat = cellHeight;

    let aspectRatio: CGFloat = image.size.width / image.size.height
    var willBeHeight = DEFAULT_THUMBNAIL_HEIGHT
    var willBeWidth  = DEFAULT_THUMBNAIL_HEIGHT * aspectRatio

    if(willBeWidth > DEFAULT_THUMBNAIL_WIDTH){
        willBeWidth = DEFAULT_THUMBNAIL_WIDTH
        willBeHeight = willBeWidth / aspectRatio
    }

    let eps:CGFloat = 0.000001
    assert((willBeHeight - eps) <= DEFAULT_THUMBNAIL_HEIGHT);
    assert((willBeWidth - eps) <= DEFAULT_THUMBNAIL_WIDTH);

    // 2 - Create context
    var size:CGSize = CGSize(
        width: DEFAULT_THUMBNAIL_WIDTH,
        height: DEFAULT_THUMBNAIL_HEIGHT)
    UIGraphicsBeginImageContext(size)

    // one-to-one rect
    //var imageRect: CGRect = CGRectMake(0.0, 0.0, size.width, size.height)
    var imageRect: CGRect = CGRectMake(0.0, 0.0, willBeWidth, willBeHeight)

    // 3 - Draw image
    image.drawInRect(imageRect)
    var imageResult: UIImage = UIGraphicsGetImageFromCurrentImageContext()
    cell.imageView!.image = imageResult

    UIGraphicsEndImageContext()
}else{
    DDLogError("Can not draw photo: \(photoFile)")
}

所以我最终为我的所有图像生成了小的缩略图。

答案 8 :(得分:-1)

重复使用细胞。对象分配具有性能成本,特别是如果分配必须在短时间内重复发生 - 例如,当用户滚动表视图时。如果重用单元而不是分配新单元,则可以极大地提高表视图性能。 避免重新播放内容。当重复使用具有自定义子视图的单元格时,每次表格视图请求单元格时都不要布置这些子视图。创建单元格时,布置子视图一次。 使用不透明的子视图。自定义表格视图单元格时,使单元格的子视图不透明,不透明。