【问题标题】:UITableView scrolling is not smoothUITableView 滚动不流畅
【发布时间】:2014-12-06 07:00:53
【问题描述】:

我的 UITableView 出现平滑滚动问题,其中包含 UIImageView 的 UITableViewCell。在整个 StrackOverflow 中都可以找到类似的问题,但没有一个建议的解决方案可以帮助我完全摆脱滞后。

我的情况很常见:

  1. 图像存储在应用程序存储中(在我的应用程序包示例中)
  2. 图片可以有不同的尺寸(500x500、1000x1000、1500x1500)
  3. 我需要在 UIImageView 大小为 120x120(视网膜)的 UITableView 中显示这些图像

我遵循了多个优化技巧并设法优化了很多滚动。 不幸的是,它仍然不完美。这是我的场景:

  1. 首先我将所有图像加载/处理/调整大小逻辑移至后台线程
  2. UITableViewCell 重用已启用
  3. 一旦 UITableViewCell 在视图中,我清除旧值(设置为 null)并启动后台线程以加载图像
  4. 此时我们处于后台线程中,我添加了 500 毫秒的延迟以避免经常设置新图像(以防我们快速滚动)(见下文说明)
  5. 如果 UIImage 存在于静态图像缓存中(带有 UIImage 实例的常规字典) - 获取该缓存并转到第 9 步。
  6. 如果不是 - 使用 url 从包 (imageWithName) 加载新图像到应用包(在现实世界场景中,图像将存储到应用程序存储,而不是包)
  7. 加载图像后,使用图形上下文将其大小调整为 120x120
  8. 将调整大小的图片保存到静态图片缓存中
  9. 此时我们有 UIImage 的实例,并且进程在后台线程中。从这里我们回到给定图像的 UI 线程
  10. 如果数据上下文被清除(例如 UITableViewCell 消失或被重新用于显示另一个图像),我们将跳过当前可用图像的处理。
  11. 如果数据上下文相同 - 使用 alpha 动画 (UIView.Animate) 将 UIImage 分配给 UIImageView
  12. 一旦 UITableViewCell 不在视野范围内 - 清除数据上下文

最初在启动新的后台线程以在此处获取图像之前(第 1 步)是没有后台线程的 UIImage 缓存检查。在这种情况下,如果我们在缓存中有图像,我们会立即分配它,这会在快速滚动期间引入很大的延迟(只要我们立即获取图像,我们就经常分配图像)。这些行在我下面附加的示例中进行了注释。

还有两个问题:

  1. 在滚动过程中的某个时刻,我仍然有一点延迟(在 当我将新的 UIImage 分配给 UIImageView 时。
  2. (这个更明显)当您点击项目并从详细信息返回时,在返回导航动画完成之前会有一个延迟。

感谢任何建议如何处理这两个问题或如何优化我的方案

请考虑用 Xamarin 编写的示例,但我不认为 Xamarin 是问题的原因,只要我对用 ObjectiveC 编写的应用程序也有同样的问题。

Smooth Scrolling Test App

【问题讨论】:

  • 我很想看看这个项目,但我使用的是 xCode。有什么方法可以转换吗?
  • Instruments(或 Xamarin 等价物)告诉您有关应用程序性能的哪些信息?你在哪里度过你的时间?是 CPU 限制还是 GPU 限制?等
  • 您似乎没有使用 CoreData。可能会测试在 CoreData TableView 中显示缩略图是否不会提高内存管理和缓存性能。
  • @carlodurso 它被一对一映射到objectiveC
  • @DavidRönnqvist 仪器显示活动的正态分布(时间测量)。如何衡量“是受 CPU 限制还是受 GPU 限制?”

标签: ios uitableview uiimage xamarin smooth-scrolling


【解决方案1】:

您是否尝试过仅使用保存在您的Bundle 中的一张 120x120 图像填充您的TableView?这样您就可以检查您的Image rendering是否出现问题

我建议不要将所有图像的大小调整为 120x120 并将它们保存在缓存中,我建议您创建并使用所有图像的缩略图。不知何故,您已经这样做了,但是您正在这样做几次(每次滚动或缓存已满时)。

在我们的上一个项目中,我们有一个带有书籍封面的UICollectionView。大多数封面大小在 400-800kb 之间,滚动时的感觉非常糟糕。所以我们为每张图片创建了一个缩略图(缩略图大约 40-50kb)并使用缩略图而不是真实的封面。奇迹般有效!我附上了缩略图创建功能

- (BOOL) createThumbnailForImageAtFilePath:(NSString *)sourcePath withName:(NSString *)name {

    UIImage* sourceImage = [UIImage imageWithContentsOfFile:sourcePath];
    if (!sourceImage) {
        //...
        return NO;
    }

    CGSize thumbnailSize = CGSizeMake(128,198);

    float imgAspectRatio = sourceImage.size.height / sourceImage.size.width;
    float thumbnailAspectRatio = thumbnailSize.height/thumbnailSize.width;

    CGSize scaledSize = thumbnailSize;

    if(imgAspectRatio >= thumbnailAspectRatio){
         //image is higher than thumbnail
         scaledSize.width = scaledSize.height * thumbnailSize.width / thumbnailSize.height;
    }
    else{
        //image is broader than thumbnail
        scaledSize.height = scaledSize.width * imgAspectRatio;
    }

    UIGraphicsBeginImageContextWithOptions( scaledSize, NO, 0.0 );
    CGRect scaledImageRect = CGRectMake( 0.0, 0.0, scaledSize.width, scaledSize.height );
    [sourceImage drawInRect:scaledImageRect];
    UIImage* destImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    NSString* thumbnailFilePath = [[self SOMEDIRECTORY] stringByAppendingPathComponent:name];

   BOOL success = [UIImageJPEGRepresentation(destImage, 0.9) writeToFile:thumbnailFilePath atomically:NO];

return success;

}

【讨论】:

    【解决方案2】:

    试试 facebook 的异步显示库。

    https://github.com/facebook/AsyncDisplayKit

    真的很容易使用......来自他们的指南:http://asyncdisplaykit.org/guide/

    _imageNode = [[ASImageNode alloc] init];
    _imageNode.backgroundColor = [UIColor lightGrayColor];
    _imageNode.image = [UIImage imageNamed:@"hello"];
    _imageNode.frame = CGRectMake(10.0f, 10.0f, 40.0f, 40.0f);
    [self.view addSubview:_imageNode.view];
    

    这会在后台线程上解码图像。

    我不确定在 Xamarin 上使用 iOS 库是否容易,但如果容易,请试一试。

    【讨论】:

      【解决方案3】:

      我将 Paul Hegarty 的 CoreDataTableViewController 子类化,并在 CoreDataTableView 中使用我的照片缩略图。

      在第 14 讲标题为FlickrFetcher and Photomania 中查找示例。您还需要在同一链接下载 CoreDataTableViewController。

      创建一个具有适当标题的 CoreData 实体并定义您想要的任何属性(数据变量)。您将需要定义两个“可变形”属性,一个用于照片,一个用于缩略图。

      然后在 CoreDataTableView 中加载您的缩略图:

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

      { NSArray *exceptions = [NSArray arrayWithObjects:@"SCR", @"DNS", @"NT", @"ND", @"NH", nil];

      static NSString *CellIdentifier = @"resultsDisplayCell";
      
      UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
      
      if (cell == nil) {
          cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
      }
      
      cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
      
      MarksFromMeets *athleteMarks = [self.fetchedResultsController objectAtIndexPath:indexPath];
      NSString* date = [ITrackHelperMethods dateToAbbreviatedString:athleteMarks.meetDate];
      NSMutableString *title = [NSMutableString stringWithFormat:@"%@", athleteMarks.markInEvent];
      NSMutableString *subTitle = [NSMutableString stringWithFormat:@"%@ - %@",date, athleteMarks.meetName];
      [title replaceOccurrencesOfString:@"(null)"
                             withString:@""
                                options:0
                                  range:NSMakeRange(0, [title length])];
      
      // cell.imageView.image = athleteMarks.photoThumbNail; // Don't like image in front of record.
      
      [cell.textLabel setFont:[UIFont
                               fontWithName:@"Helvetica Neue" size:18]];
      
      [cell.detailTextLabel setFont:[UIFont fontWithName:@"Helvetica Neue" size:16]];
      [cell.detailTextLabel setTextColor:[UIColor grayColor]];
      
      // make selected items orange
      if ([athleteMarks.eventPR integerValue] != 0
          && (![exceptions containsObject:athleteMarks.markInEvent])) {
      
          title = [NSMutableString stringWithFormat:@"%@      (PR)",title];
          [cell.textLabel setTextColor:[UIColor redColor]];
      }
      else if ([athleteMarks.eventSB integerValue] != 0
               && (![exceptions containsObject:athleteMarks.markInEvent])) {
          title = [NSMutableString stringWithFormat:@"%@      (SB)",title];
      
          [cell.textLabel setTextColor:[UIColor orangeColor]];
      } else {
          [cell.textLabel setTextColor:[UIColor grayColor]];
      }
      
      cell.textLabel.text = title;
      cell.detailTextLabel.text = subTitle;
      
      cell.indentationLevel = indentationLevelOne;
      cell.indentationWidth = indentationForCell;
      
      return cell;
      

      }

      如果您愿意,我可以向您发送一个实体的 NSManagedObject 子类的类别示例。此类别将照片和缩略图加载到 CoreData 实体中。第一次会很慢。但是,之后用户应该能够顺利滚动 TableView,然后所有更新的结果将自动加载。告诉我。

      一件好事是CoreData 处理所有的内存管理。

      祝你好运!

      【讨论】:

      • P.S. Tim Roadley 在他名字的 URL 上有一系列关于 CoreData 的精彩教程,以及一些有用的帮助类。
      【解决方案4】:

      我没有足够的代表发表评论,所以这是一个有助于我的 tableview 滚动性能的答案:

      • 使tableview 高度大于可视窗口。单元格将在“屏幕外”加载并有助于提高滚动流畅度。
      • 按以下方法进行图像处理: -(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath

      这两个技巧让我的桌子流动得非常好。我从 API 服务获取图像数据,AFNETWORKING 有一个很棒的图像加载器,但对你来说不是必需的,因为图像包含在包中。

      【讨论】:

        【解决方案5】:

        也许你可以试试SDWebImage。它也是一个 xamarin component 它形成了一个异步图像下载器和异步内存和磁盘图像缓存以及自动缓存过期处理。使用它可能意味着丢弃大量硬编写的代码,但它可能是值得的——而且你的代码会变得更简单。在 iOS 中,您还可以在控制器的 viewDidLoad 中设置 SDWebImageManager

        - (void)viewDidLoad{
        ...
        SDWebImageManager *manager = [SDWebImageManager sharedManager];
        manager.delegate = self;
        ...
        }
        

        并将视图控制器设置为委托。然后,当调用以下委托方法时:

        - (UIImage *)imageManager:(SDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)image withURL:(NSURL *)imageURL
        

        在缓存它们之前,您可以将图像缩放到适当大小的拇指。

        希望对您有所帮助。

        【讨论】:

          【解决方案6】:

          Weel 我遇到了类似的问题,我的滚动不流畅。我在表中插入一个带有内部标签视图的变量 UIImageView。 我所做的是将方法 HeightforRowAtIndexPath 更改为 estimatedHeightforRowAtIndexPath ,现在滚动很流畅。

          【讨论】:

            猜你喜欢
            • 2011-11-06
            • 1970-01-01
            • 2019-07-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-01-30
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多