16

我有一个 UITableView,它在 200 个部分中有大约 400 个单元格,它在响应用户交互(滚动、选择单元格)方面有点迟缓。我确保检索单元格和标题视图的方法在运行时做到了最低限度,并且我不认为我正在做任何不寻常的事情来让它变慢。单元格和标题只有背景图像和文本。有没有其他人遇到过这种问题,你知道有什么方法可以让它运行得更快一点吗?

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

在模拟器和我的设备 iPhone 4 上都观察到了迟缓。这是我的viewForHeaderInSection和的实现cellForRowAtIndexPath,它们是唯一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;
}
4

9 回答 9

50

即使您的单元格实际上是那么简单(背景图像和标签),也有一些事情需要考虑

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

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

图像中的 Alpha 通道 绘制时代价高昂的是透明度。由于您的单元格背景后面没有任何内容,因此请确保您保存的图像没有 Alpha 通道。这节省了大量的处理。

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

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

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

于 2011-06-23T09:42:39.293 回答
9

如果您希望加快代码速度,最好的办法就是对其进行分析。有两个原因:

  1. 您可以阅读一些总体上可以提高表格性能的内容,例如使用固定高度的单元格和重复使用单元格,这可能有助于实现这些内容(看起来您已经这样做了)。但是当谈到加速你的代码时,你真的需要知道你的应用程序大部分时间都花在了哪里。可能有一些方法需要很长时间,或者一种方法相对较快但调用频率比您预期的要多得多。

  2. 除非您有一些数字可以衡量,否则不可能知道您为加快速度所做的更改是否真正产生影响。如果您可以证明您的代码在一个例程中花费了 80% 的时间,并且您将其减少到 35%,那么您就知道您正在取得进展。

所以,打破仪器并开始测量。如果可以的话,最好在执行您想要加速的每项不同活动时进行测量...在滚动时进行一次分析会话,在固定期间选择尽可能多的不同单元格时进行一次分析等。不要忘记保存结果,以便以后进行比较。

于 2011-06-21T04:51:58.197 回答
5

请注意这几点..

  1. 你在重复使用细胞吗?这是一个很好的做法。
  2. 确保您没有在 cellForRowAtIndexPath 回调中或在从 CellForRowAtIndexPath 调用的函数中进行任何昂贵的计算。
  3. 你说有背景图片。您必须重复使用您的细胞的另一个原因。

一些关于单元重用的好信息在这里..

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

这个SO question thread 可能会帮助你......尤其是接受的答案......

于 2011-05-30T04:22:18.400 回答
3
  1. 使用共享图像实例作为背景(每次创建新单元时分配/初始化/释放一个)。当您的表格视图很大时,这意味着内存中的背景 X 单元格占用的内存比应有的要多得多。


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

  2. 如果这没有帮助,请使用 CG 而不是标签和其他子视图(此处的屏幕截图会有所帮助......了解我们在说什么)。

于 2011-06-22T05:50:56.567 回答
2

表视图的委托是否实现:

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

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

于 2011-05-30T04:29:36.313 回答
1

您是否使用了很多子视图?

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

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

于 2011-06-21T04:33:46.693 回答
1

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

于 2011-06-21T04:48:18.880 回答
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)")
}

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

于 2015-06-27T13:44:03.327 回答
-1

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

于 2011-07-22T09:34:15.040 回答