UITableView滚动不流畅

时间:2014-10-11 03:27:04

标签: ios uitableview uiimage xamarin smooth-scrolling

我的UITableView上有平滑的滚动问题,UITableViewCell包含UIImageView。在StrackOverflow上可以找到类似的问题,但是所提出的解决方案都没有帮助我彻底摆脱滞后。

我的情况很常见:

  1. 图像存储在应用程序存储中(在我的应用程序包示例中)
  2. 图片可能有不同的尺寸(500x500,1000x1000,1500x1500)
  3. 我需要在UITableView中显示那些图像,其中UIImageView大小为120x120(视网膜)
  4. 我遵循了多个优化提示,并设法优化滚动。 不幸的是它仍然不完美。这是我的情景:

    1. 首先我将所有图像加载/处理/调整大小逻辑移动到后台线程
    2. 启用UITableViewCell重用
    3. 一旦UITableViewCell在视图中我清除旧值(设置为null)并启动后台线程以加载图像
    4. 此时我们处于后台线程中,并且我添加了500毫秒的延迟以避免经常设置新图像(如果我们快速滚动)(见下面的解释)
    5. 如果UIImage存在于静态图像缓存(带有UIImage实例的常规字典)中 - 获取那个并转到步骤9.
    6. 如果不是 - 使用url到app捆绑包(imageWithName)加载新图像(在现实世界中,场景图像将存储到应用程序存储,而不是捆绑)
    7. 加载图像后,使用图形上下文将其调整为120x120
    8. 将已调整大小的图像保存到静态图像缓存
    9. 此时我们有UIImage实例和进程在后台线程中。从这里我们回到具有给定图像的UI线程
    10. 如果清除了数据上下文(例如UITableViewCell消失或被重用以显示另一个图像),我们将跳过当前可用图像的处理。
    11. 如果数据上下文相同 - 使用alpha动画(UIView.Animate)将UIImage分配给UIImageView
    12. 一旦UITableViewCell不在视图范围内 - 清除数据上下文
    13. 最初在开始新的后台线程之前获取图像(步骤1)是没有后台线程的UIImage缓存检查。在这种情况下,如果我们在缓存中有图像,我们会立即分配它,这会在快速滚动期间引入很大的延迟(只要我们立即获取图像,我们就会经常分配图像)。我在下面的例子中对这些行进行了评论。

      还有两个问题:

      1. 在滚动期间的某个时刻我仍然有一个小的滞后(在 我将新的UIImage分配给UIImageView的时刻。
      2. (这个更明显)当您点击项目并从详细信息返回时,在后退导航动画完成之前有一个延迟。
      3. 有人建议如何处理这两个问题或如何优化我的方案

        请考虑使用Xamarin编写的示例,但我不相信Xamarin是导致问题的原因,只要我对ObjectiveC中编写的应用程序也有同样的问题。

        Smooth Scrolling Test App

6 个答案:

答案 0 :(得分:10)

您是否曾尝试使用保存在TableView中的一张120x120图片填充Bundle?这样,您可以检查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;

}

答案 1 :(得分:3)

尝试使用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库是否容易,但如果它很容易,那么就试一试。

答案 2 :(得分:2)

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

查看标题为FlickrFetcher and Photomania的第14讲中的示例。您还需要在同一链接下载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处理所有内存管理。

祝你好运!

答案 3 :(得分:0)

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

  • 使tableview高度大于可视窗口。细胞将加载"关闭屏幕"并有助于提高滚动平滑度。
  • 使用以下方法处理图像: -(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath

这两个技巧让我的桌子流得非常好。我从API服务获取图像数据,AFNETWORKING有一个非常棒的图像加载器,但由于图像在捆绑中,因此不需要。

答案 4 :(得分:0)

也许你可以试试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

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

希望有所帮助。

答案 5 :(得分:0)

Weel我有类似的问题,我的卷轴不顺畅。我在表中插入一个带有内部labelViews的变量UIImageView。 我所做的是为estimatedHeightforRowAtIndexPath更改方法HeightforRowAtIndexPath,现在滚动是平滑的。