【问题标题】:iOS lazy-loading of table imagesiOS 延迟加载表格图像
【发布时间】:2023-04-01 08:15:01
【问题描述】:

我已经使用苹果here 提供的示例在我的应用程序中成功实现了图像的延迟加载。问题是,我希望在滚动时加载图像,但只有在我完成拖动时才将图像加载到单元格中(从屏幕上松开手指。在此之前单元格保持为空)。我修改了代码如下:

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

    //NSLog(@"cell for row at indexpath, size - %f, current - %d", flowTable.contentSize.height, self.current);

    NSString *CellIdentifier = @"FlowCell";

    MyFlowCell *cell = (MyFlowCell *)[self.flowTable dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {

        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"FlowCell" owner:nil options:nil];
       // cell = [nib objectAtIndex:0];

        for(id currentObject in nib)

        {

        if([currentObject isKindOfClass:[MyFlowCell class]])

        {
            cell = (MyFlowCell *)currentObject;

            break;
        }
        }
    }

    ImageDetails *rowItem = [self.imageData objectAtIndex:indexPath.row];

    if (!rowItem.mainImage)
    {  
       // if (self.flowTable.dragging == NO && self.flowTable.decelerating == NO)
       // {

            [self startIconDownload:rowItem forIndexPath:indexPath];

        //}

        cell.mainImage.backgroundColor = [UIColor grayColor];

    }
    else
    {

            cell.mainImage.image = rowItem.mainImage;

    }
    }


    return cell;
}


- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
    if (!decelerate)
    {
      //  [self loadImagesForOnscreenRows];
    }
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    //[self loadImagesForOnscreenRows];
}

此外,我发现在我的 ImageDownloader 类中,NSURLConnection 甚至在我从屏幕上松开手指之前都没有收到响应。我一直试图找出为什么会发生这种情况,但到目前为止我还没有成功。如果我遗漏了什么,请告诉我。

【问题讨论】:

标签: ios objective-c uitableview uiscrollview lazy-loading


【解决方案1】:

更新答案:

标准的 Apple LazyTableImages 存在一些缺陷:

  1. 在滚动完成之前,它不会尝试检索图像。我理解他们为什么选择这样做(可能不想检索可能正在滚动的图像),但这会导致图像出现的速度比必要的慢。

  2. 它不会将并发请求的数量限制为某个合理的数量。是的,NSURLConnection 会自动冻结请求,直到最大请求数下降到某个合理数量,但在最坏的情况下,您实际上可以让请求超时,而不是简单地正确排队。

  3. 一旦开始下载图像,即使单元格随后滚出屏幕,它也不会取消。

简单的解决方法是停用IconDownloader 和所有滚动/减速逻辑,只使用SDWebImageAFNetworking 中的UIImageView 类别。

如果您要自己解决此问题,则需要做一些工作才能解决所有问题。但是here is a rendition of LazyTableImages 使用NSOperationQueue 来确保我们(a)限制并发请求; (b) 允许我们取消请求。我承认我更喜欢上面的 UIImageView 类别实现,更好,但我试图保留 Apple 原始 LazyTableImages 的结构,同时弥补我上面列举的缺陷。

【讨论】:

  • 我的原始答案使用 Grand Central Dispatch 来简化 Apple LazyTableImages 代码。但它并没有解决这个原始实现的许多结构问题,所以我修改后的答案解决了这些问题。
  • 链接的 GitHub 项目没有解决等待结束滚动的问题。 SDWebImage 代替。
【解决方案2】:

您可以按照以下步骤在表格视图中实现延迟加载:

在您的 Class.h 中,输入:

NSMutableDictionary *Dict_name;
BOOL isDragging_msg, isDecliring_msg;

现在在 Class.m 文件中,将这段代码放在视图中确实加载了:

Dict_name = [[NSMutableDictionary alloc] init];

在 cellForRowAtIndexPath 中:

if ([dicImages_msg valueForKey:[[msg_array objectAtIndex:indexPath.row] valueForKey:@"image name or image link"]]) { 
    cell.image_profile.image=[dicImages_msg valueForKey:[[msg_array objectAtIndex:indexPath.row] valueForKey:@"image name or image link"]];
}
else
{
    if (!isDragging_msg && !isDecliring_msg)
    {
        [dicImages_msg setObject:[UIImage imageNamed:@"Placeholder.png"] forKey:[[msg_array objectAtIndex:indexPath.row] valueForKey:@"image name or image link"]];
        [self performSelectorInBackground:@selector(downloadImage_3:) withObject:indexPath];
    }
    else
    {
        cell.image_profile.image=[UIImage imageNamed:@"Placeholder.png"];
    }
}

对于下载图片,函数是:

-(void)downloadImage_3:(NSIndexPath *)path{
    NSAutoreleasePool *pl = [[NSAutoreleasePool alloc] init];

    NSString *str=[here Your image link for download];

    UIImage *img = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:str]]]; 

    [dicImages_msg setObject:img forKey:[[msg_array objectAtIndex:path.row] valueForKey:@"image name or image link same as cell for row"]];

    [tableview performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];

    [pl release];
}

最后把这些方法放到你的类中:

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
    isDragging_msg = FALSE;     
    [tableview reloadData];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
    isDecliring_msg = FALSE;
    [tableview reloadData]; 
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
    isDragging_msg = TRUE;
}
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{
    isDecliring_msg = TRUE; 
}

【讨论】:

  • 是的,这展示了一种可靠的技术(尽管使用reloadData 是更新单元格的低效方式)。
  • 大家好。非常感谢您的所有回答。每个人都以自己的方式帮助我解决问题。不过,我主要使用这篇文章的概念。所以为了它,我会接受这个。 :)
【解决方案3】:

假设您的 UITableViewCell 具有图像的 url 属性和 UIImage 属性(加上队列的属性或静态值:

- (void) setThumbnailUrlString:(NSString *)urlString
{
    thumbnailUrlString = urlString;

    NSURL *url = [NSURL URLWithString:urlString];

    NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
    if ( queue == nil )
    {
        queue = [[NSOperationQueue alloc] init];
    }
    [NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse * resp, NSData     *data, NSError *error) 
    {
        dispatch_async(dispatch_get_main_queue(),^ 
                       {
                            if ( error == nil && data )
                            {
                                UIImage *urlImage = [[UIImage alloc] initWithData:data];
                                thumbnail.image = urlImage;
                            }
                       });
    }];
}

您的实现的问题是您的图像加载方法可能发生在主线程中。需要异步加载图片,然后在主线程中更新图片。

为了正确,如果单元格的图像在单元格被重新用于不同的图像之前加载得太晚,则该块应检查响应的 url 是否与请求的 url 匹配。

【讨论】:

  • 队列是一个 NSOperationQueue 顺便说一句
  • 顺便说一句,你不需要创建这个操作队列。您可以只使用[NSOperationQueue mainQueue] 作为queue 参数到sendAsynchronousRequest。请求仍然异步发生,但完成块将在主队列上调用,从而消除了对主队列的 dispatch_async 的需要。 sendAsynchronousRequestqueue 参数是完成块的队列,而不是请求本身。
【解决方案4】:

我已经完成了这个包含延迟加载控制器的框架。它易于使用且开箱即用。 https://github.com/cloverstudio/CSUtils 教程可以在这里找到: http://www.clover-studio.com/blog/using-csutils-ios-framework-for-lazy-loading-images/

【讨论】:

    【解决方案5】:

    这是一个很晚的答案,但随着 iOS 版本的变化,方法也在不断变化。

    为了延迟加载图片,推荐的方法如下:

    1. 下载所有图像的 URL 并将它们存储到您的容器中(数组/对象等)
    2. 触发 NSURLSessionTask(仅限 iOS 7 后),它在后台队列上异步运行。如果您低于 iOS 7,您可以使用 NSURLConnection SendAsynchronousRequest API - 它在 iOS 9 中已被弃用,因此您最好尽快摆脱它。
    3. 在后台队列中创建图像
    4. 回到主队列,确保你得到了正确的 UITableViewCell,然后更新图像

    这里 - 整个方法是described in my tutorial

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-10-24
      • 2021-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-03
      • 1970-01-01
      • 2014-10-31
      相关资源
      最近更新 更多