【问题标题】:improving ViewController load speed with Custom UITableViewCells (a broken record)使用自定义 UITableViewCells 提高 ViewController 加载速度(破纪录)
【发布时间】:2012-09-11 00:26:06
【问题描述】:

我有一个带有 UITableView 的 UIViewController,它的加载速度非常慢(也许像糖蜜一样慢)我可以说这是由于在 ViewController 拥有的分组 UITableview 中为 UITableViewCells 设置了自定义背景图像。

我认为我遵循的建议与其他 SO 问题中提到的建议一样多: Tricks for improving iPhone UITableView scrolling performance

我还关注了这篇文章,以确保我没有做一些愚蠢的事情: Cocoa With Love Custom UITableView Drawing

只要我不调用背景样式代码,性能就会提高。

按照建议,我正在执行所有这些步骤:

  • 由于设计的方式,我有 2 种不同的单元格变体,并且不能有统一的行高。无论单元格变化如何,行背景始终是 3 个图像中的 1 个,并且基于行索引。 0:top_row_image,n-1:bottom_row_image,所有其他:middle_row_image
  • 我正在 ViewController 的 viewDidLoad 中加载用于背景的图像。
  • 我没有任何 drawRect 代码,因为我让单元格内的 UITextField 处理
  • 单元格的图层设置为不透明
  • 小区标识符正在被重复使用
  • 不从 NIB 文件加载单元格

基本上,我想用不同的顶部、中间和底部行背景图像来设置表格的每个部分行的样式,具体取决于它是哪种类型的行类型。谁能建议一种更好的方法来在 UITableView 上自定义背景?

这是我的代码:

ViewController:
@property (nonatomic, weak) IBOutlet                            *tableview;
@property (nonatomic, strong, readwrite) UIImage                *topRowImage;
@property (nonatomic, strong, readwrite) UIImage                *middleRowImage;
@property (nonatomic, strong, readwrite) UIImage                *bottomRowImage;

@synthesize tableview = _tableview;    
@synthesize topRowImage = _topRowImage;         
@synthesize middleRowImage = _middleRowImage;
@synthesize bottomRowImage = _bottomRowImage;

// Load the images that will be used for the cell background ahead of time
- (void)viewDidLoad
{
    [super viewDidLoad];        

    // All of the images are 60x20 but my cells are 300x44 or 300x56
    UIEdgeInsets edgeInsets = UIEdgeInsetsMake(2, 4, 2, 4);        
    self.topRowImage = [[UIImage imageNamed:@"top_row.png"] resizableImageWithCapInsets:edgeInsets];        

    UIEdgeInsets edgeInsets = UIEdgeInsetsMake(0, 4, 0, 4);
    self.middleRowImage = [[UIImage imageNamed:@"middle_row.png"] resizableImageWithCapInsets:edgeInsets];            

    edgeInsets = UIEdgeInsetsMake(2, 4, 2, 4);
    self.bottomRowImage = [[UIImage imageNamed:@"bottom_row.png"] resizableImageWithCapInsets:edgeInsets];    
}

- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellId = [self getCellIdAt:indexPath];

    BaseCustomTableViewCell *cell = (BaseCustomTableViewCell *)[aTableView dequeueReusableCellWithIdentifier:cellId];

    if (cell == nil) 
    {
        if ([cellId isEqualToString:@"CellId1"])
        {
           cell = [[CustomTableViewCell1 alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];     
        }
        else
        { 
           cell = [[CustomTableViewCell2 alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];     
        }
    }

    // the following line seems to be the bottleneck
    [self styleBackground:cell indexPath:indexPath totalRows:totalRows];

    [cell configureData:myRowData];
}

- (void)styleCellBackground:(BaseCustomTableViewCell *)cell 
                  indexPath:(NSIndexPath *)indexPath
                  totalRows:(NSInteger)totalRows
{
    UIImage *backgroundImage = nil;    

    if (indexPath.row == 0)
    {
        // Top row of this section        
        backgroundImage = self.topRowImage; // ivar loaded during 'viewDidLoad'
    }
    else if (indexPath.row == totalRows - 1)
    {
        // Bottom row of this section        
        backgroundImage = self.bottomRowImage;
    }
    else {
        // Middle row of this section        
        backgroundImage = self.middleRowImage;
    }

    [cell updateRowBackground:backgroundImage];
}

@implementation CustomTableViewCell

-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])
    {        
        // Create my text field
        _textField = [[UITextField alloc] initWithFrame:CGRectZero];        
        _textField.backgroundColor          = [UIColor whiteColor];    

        self.backgroundColor                = [UIColor whiteColor];            
        self.contentView.backgroundColor    = [UIColor whiteColor];

        self.backgroundView = [[UIImageView alloc] init];        

        // not sure which of these should be set to opaque to ensure to meet criteria #4
        // 4. Make your UITableViewCell's layer opaque (same goes for the content view if you have one)
        self.backgroundView.opaque = YES;
        self.layer.opaque = YES;
        self.opaque = YES;        
        self.contentView.opaque = YES;        

        [self.contentView addSubview:_textField];
    }
}

- (void)layoutSubviews
{    
    CGRect textFieldFrame = CGRectMake(10, 2, 278, 40);
    self.textField.frame = textFieldFrame;

    [super layoutSubviews];
}

- (void)updateRowBackground:(UIImage *)rowBackground
{
    ((UIImageView *)self.backgroundView).image = rowBackground;        
}

【问题讨论】:

  • 我很困惑,但每个单元格都有不同的背景?你大约有多少个细胞?如果您有许多具有不同背景的单元格,那么它们应该被动态加载。通过在 viewdidload 中加载所有内容,您正在使界面等待一切准备就绪。此外,如果您正在调试并且您有很多单元格,请确保您没有 NSLOG 内容会增加加载时间。
  • 我有 3 种不同的背景类型。每个部分的顶行单元格应该有一种类型(它有一个向下的曲线),中间行有另一个(标准图像),最后,每个部分的底行有一个第三种类型(这个图像有一个向上的曲线)。
  • 我一共15行,分成4个部分。

标签: ios ios5 uitableview


【解决方案1】:

我认为,如果您在每次调用 cellForRowAtIndexPath 时停止切换每个单元格的背景图像,您会看到显着的性能提升。

听起来您有三种单元格(“顶部”、“中间”和“底部”)。您可以停止交替重复使用它们,因此每次使用时都必须重置它们的背景图像。

相反,您可以根据它们在表格中的位置使用不同的标识符来初始化表格单元格的实例。这样,您将创建一个带有“顶部”标识符的单元格、一个带有“底部”标识符的单元格,以及许多带有“中间”标识符的单元格。然后,您只能在初始化单元格时设置它们的背景图像。只要您尝试重用具有适当标识符的单元格,您就不再需要每次都更改其背景图像。

【讨论】:

  • 由于我有 2 种不同的单元格类型,因此我将有 6 个单元格 id 变体。我的印象是拥有这么多不同的细胞类型对性能也不是很好。在 CocoaWithLove 网站上提供的代码示例中,我只看到了 1 个 cellId 类型,但基于 indexPath.row 的背景不同。
  • 更多单元格标识符可能会导致表格视图在内存中保留更多单元格实例,但这不一定会影响性能。我的印象是,Matt 在 CocoaWithLove 上的示例演示了如何自定义单元格外观,不一定是高性能单元格创建的指南。由于您现在似乎遇到了真正的性能问题并将其归因于更新这些背景图像的成本,我认为这是一个尝试消除问题的合理实验。
  • 只是想确保我理解这一点:当您有自定义的顶行、中间行和底行背景时,每个单元格类型都有 3 个不同的单元格标识符来出列。如果您的单元格包含异构数据或 UI 元素,那么您将拥有每个单元格类型的其他唯一单元格 ID 类型。
  • @HowardSpear 这似乎很可能,但取决于您的应用程序的具体情况以及配置单元以供重用的成本。如果重新配置单元格(在单元格的-prepareForReuse 和/或数据源的-cellForRowAtIndexPath: 中)很便宜,则无需引入更多标识符。我会尽可能使用一些标识符,只需测量性能以查看何时可能需要引入新标识符,以便在重用单元时用一些额外的内存使用来降低 CPU 负载。从您不必更改背景开始,看看是否有帮助
  • Jonah 说的是正确的,但除此之外,我们刚刚观察到 resizableImageWithCapInsetsstretchableImageWithLeftCapWidth: topCapHeight: 花费更多时间并且滞后于表格视图滚动。我知道它已被弃用,但...
猜你喜欢
  • 2010-10-04
  • 2016-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-02
  • 1970-01-01
  • 2014-12-03
相关资源
最近更新 更多