【问题标题】:Sluggish UITableView scrolling even after reload即使在重新加载后 UITableView 滚动也很缓慢
【发布时间】:2023-03-16 15:33:02
【问题描述】:

我有一个 UITableView,它使用了reuseCellIdentifier 并包含图像(使用 UIImageView+AFNetworking.h 异步下载);正如所料,滚动一段时间后,当表格变长时,它会变得迟缓。我还将从 Internet 下载的每个单元格的数据存储在 NSMutableArray *_collection 中,因此我不会在每次单元格重新出现时都重新下载。

我的问题是,我用下面的方法彻底刷新表格后,滚动还是很慢。让它再次顺利加载的唯一方法是退出应用程序并重新打开它。我不明白为什么重新加载后滚动仍然缓慢...我正在使用 ARC 并且我做了泄漏配置文件并且在重新加载表格时看不到任何内容..

-(void)refreshTable:(id)sender{
  [_collection removeAllObjects];
  _collection = nil;

  AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:_baseURL]];
  [httpClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
  NSMutableURLRequest *request = [httpClient requestWithMethod:@"POST" path:_path parameters:nil];
  AFJSONRequestOperation *operation = [AFJSONRequestOperation ...etc...

更进一步,我什至尝试在刷新时删除 tableview,如下所示,但这也无济于事......

-(void)refreshTable:(id)sender{
  [_tableView removeFromSuperView];
  _tableView = nil;
  _tableView = [[UITableView alloc] initWithFrame:self.view.bounds];
  [_tableView setDelegate:self];
  [_tableView setDataSource:self];
  [_tableView setRowHeight:kRowHeight];
  [_tableView setBackgroundColor:[UIColor clearColor]];
  [_tableView setSeparatorColor:[UIColor clearColor]];
  [_tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
  [self.view addSubview:_tableView];

  [_collection removeAllObjects];
  _collection = nil;

  AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:_baseURL]];
  [httpClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
  NSMutableURLRequest *request = [httpClient requestWithMethod:@"POST" path:_path parameters:nil];
  AFJSONRequestOperation *operation = [AFJSONRequestOperation ...etc...

刷新表格后滚动仍然缓慢的原因可能是什么?谢谢!

编辑

- (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CustomCell *cell = (CustomCell*)[theTableView dequeueReusableCellWithIdentifier:kCustomCellId];
    if (cell == nil)
    {
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil];
        cell = (CustomCell*)[topLevelObjects objectAtIndex:0];
    }

    [self populate:cell atIndexPath:indexPath];
    [self loadImageForCell:cell];
    return cell;
}

- (void) populate:(UITableViewCell*)customCell atIndexPath:(NSIndexPath*)indexPath
{
    CustomCell *cell = (CustomCell*)customCell;

    if (indexPath.row % 2) {
        [cell.background setImage:[UIImage imageNamed:@"CellBgYellow"]];
    } else {
        [cell.background setImage:[UIImage imageNamed:@"CellBgOrange"]];
    }

    DataObject* thisDataObject = [_collection objectAtIndex:indexPath.row];
    cell.dataObject = thisDataObject;
    cell.titleLabel.text = [thisDataObject objectForKey:kCellTitle];
    cell.timestampLabel.text = [thisDataObject objectForKey:kCellTimestamp];
}

- (void) loadImageForCell:(CustomCell*)cell
{
    [cell.profilePic setImage:nil];
    NSString* profilePicURL = [cell.dataObject objectForKey:kCellProfilePicURL];    
    NSString* URL = [NSString stringWithFormat:@"%@%@", kBaseURL, profilePicURL];
    NSMutableURLRequest *profilePicRequest = [NSMutableURLRequest requestWithURL:[[NSURL alloc] initWithString:URL] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30.0];
    [profilePicRequest setHTTPShouldHandleCookies:NO];    
    [cell.profilePic setImageWithURLRequest:profilePicRequest
                           placeholderImage:nil
                                    success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image){
                                    }
                                    failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){
                                        NSLog(@"loadimageforcell error (%d): %@",error.code,error.localizedDescription);
                                    }];

    [cell.painting setImage:nil];
    NSString* paintingURL = [cell.dataObject objectForKey:kCellPaintingURL];
    URL = [NSString stringWithFormat:@"%@%@", kBaseURL, paintingURL];
    NSMutableURLRequest *paintingRequest = [NSMutableURLRequest requestWithURL:[[NSURL alloc] initWithString:URL] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30.0];
    [paintingRequest setHTTPShouldHandleCookies:NO];
    [cell.spinner startAnimating];
    [cell.painting setImageWithURLRequest:paintingRequest
                           placeholderImage:nil
                                    success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image){
                                        [cell.spinner stopAnimating];
                                        [cell.spinner removeFromSuperview];
                                    }
                                    failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){
                                        NSLog(@"loadimageforcell error (%d): %@",error.code,error.localizedDescription);
                                        [cell.spinner stopAnimating];
                                        [cell.spinner removeFromSuperview];
                                    }];
}    

【问题讨论】:

  • 请出示您的tableView:cellForRowAtIndexPath:方法。

标签: ios uitableview


【解决方案1】:

问题是2:

  1. 调用图像请求将占用一些 cpu %
  2. 图像解码将占用另一个 cpu %

根据图像大小,您必须执行各种操作。 最简单和更明显的解决方案是在另一个线程中解压缩图像。 这样你的主线程就不会被阻塞,你的滚动性能应该会得到不错的提升。

这里有一段代码可以帮助你:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^(void) {
                    UIImage *cellImage = [UIImage imageWithContentsOfFile:imgFilePath];
                    dispatch_async(dispatch_get_main_queue(),^ {
                        cellImageView.image = cellImage;
                    } );
                });

如您所见,UIImage 是在后台线程中生成的,一旦我们对图像进行解码,我们就会将其分配给单元格。

在上面的例子中,图片是预先缓存的,imgFilePath是图片缓存的文档目录的路径。

您可以通过各种其他方式继续提高性能,例如使用 Loren Brichter 的 ABTableView 单元子类并使用 Core Graphics 绘制图像(也大大提高了性能)。 最后,如果图片很大,可以将整个单元格设置为 CATiledLayer 类的子类,这样图片的绘制也可以由系统在后台线程中完成...

【讨论】:

  • 感谢您。我的图像每张大约 40k,值得做 CATiledLayerClass。我一直在查看 ABTableView 单元格,但由于单元格的不同部分对触摸的响应不同(即点击用户名将调用与点击图片不同的方法),我不确定遵循 drawRect 路线有多容易。
  • 另外,关于您提供的 sn-p 的快速问题,假设图像保存在本地某处,对吗?这不适用于我从互联网上下载图像的情况,或者我错过了什么?再次感谢
  • 回答您的问题: 1. 您可以使用 drawRect 方法并响应单元格中的不同触摸,通过子类化- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 方法根据触摸的位置,您可以捕获被触摸的区域点击(用户名等)。如果这不适合您,您可以定期将按钮添加到单元格的 contentview 中,并在 drawRect 方法中为大图片进行绘图。 2.我提供的sn-p是针对已经从互联网下载图像到设备并缓存它们的类,所以它也适用于你......
  • 谢谢,这是非常有用的信息!只是另一个后续问题。我目前直接在我的dataObjectNSDictionary 的子类,每个 indexPath 一个)中指向下载的 UIImages,然后将其存储在_collection 数组中。如果我像您一样在本地保存图像并在每次看到单元格时加载它们,我会看到更好的性能吗? (即你的dispatch_async 会比[cell.picture setImage:[[_collection objectAtIndex:indexPath.row] objectForKey:@"kUIImage"]]; 快吗?
  • 绝对是的。 AfNetworking 实际上应该具有缓存能力,我很确定,所以一切都应该很容易完成。我在我的应用程序中所做的是,对于每个单元格,我都会保留图像 URL 路径,然后从 ASIHTTP 的 URL 路径中获取缓存的图像。
【解决方案2】:

不要在 cellForRowAtIndexPath 中加载图像。这只会杀死你的滚动。

为了快速解决您的问题,仅在 tableview 没有滚动、完全停止时才加载图像。

更好的方法是重新思考您的设计。通过使用网络队列和延迟加载图像是可行的方法。

查看 Lazy TableView 的苹果演示:http://developer.apple.com/library/ios/#samplecode/LazyTableImages/Introduction/Intro.html

这教会了我很多,通过像你一样使用 ASIHTTPRequest,你甚至可以让这更简单。但是看代码,注释很好,通俗易懂

** 使用 afnetwork 更新

做这个 f.ex。在你的 cellForRowAtIndexPath 里面:

NSURLRequest *imagerequest = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://example.com/image.png"]];
AFImageRequestOperation *op = [AFImageRequestOperation imageRequestOperationWithRequest:imagerequest success:^(UIImage *image) {
    cell.imageview.image = image;
}];

【讨论】:

  • 谢谢,让我试试这个,让你知道结果。也就是说,这并不能真正解释为什么在我重新加载表格后滚动仍然缓慢?难道还有什么问题吗?
  • 他实际上是在使用 AFNetworking 而不是 ASIHTTPRequest :)
  • 当你改变数据的时候,你只需要调用[myTableView reloadData];,其他的都太多了。由于您使用的是 AFNetworking,因此他们有一个很棒的类 AFImageRequestOperation.h,它可以完全解决您的问题。
  • 我已经更新了我的答案。如果我不直接在drawrect中绘制自己的单元格,我就是这样做的,像黄油一样光滑
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-22
  • 2013-05-05
  • 1970-01-01
  • 1970-01-01
  • 2011-10-22
  • 2012-08-17
相关资源
最近更新 更多