iOS 延迟加载表格图片

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14400378/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 21:54:18  来源:igfitidea点击:

iOS lazy-loading of table images

iosobjective-cuitableviewuiscrollviewlazy-loading

提问by Rameez Hussain

I have successfully implemented lazy-loading of images in my app using the example provided by apple here. The thing is that, I want the image to load as I am scrolling, but the image loads into the cell only when I finish dragging (Release my finger from the screen. Until then the cell remains empty). I have modified the code as follows:

我已经使用 apple 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];
}

Moreover, I found out that in my ImageDownloader class, the NSURLConnection doesn't even receive the response until I release my finger from the screen. I have been trying to find out why this is happening, but I haven't been successful so far. Please tell me if I've been missing something.

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

采纳答案by Wolverine

You can achieve lazy loading in your tableview by following these steps :

您可以按照以下步骤在 tableview 中实现延迟加载:

In your Class.h, put:

在你的 Class.h 中,输入:

NSMutableDictionary *Dict_name;
BOOL isDragging_msg, isDecliring_msg;

Now In Class.m file, put this code in view did load:

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

Dict_name = [[NSMutableDictionary alloc] init];

In cellForRowAtIndexPath:

在 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"];
    }
}

And for the download image the function is:

对于下载图像,函数是:

-(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];
}

And at last put these methods in your class:

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

- (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; 
}

回答by Rob

Updated answer:

更新的答案:

The standard Apple LazyTableImagessuffers from a few flaws:

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

  1. It won't try to retrieve the images until the scrolling finished. I understand why they chose to do that (probably don't want to retrieve images that might be scrolling away), but this causes the images to appear more slowly than necessary.

  2. It does not constrain the number of concurrent requests to some reasonable number. Yes, NSURLConnectionwill automatically freeze requests until the maximum number of requests falls to some reasonable number, but in a worst case scenario, you can actually have requests time out, as opposed to simply queueing properly.

  3. Once it starts to download the image, it won't cancel it even if the cell subsequently scrolls off of the screen.

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

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

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

The simple fix is to retire IconDownloaderand all of the scrolling/decelerating logic and just use the UIImageViewcategory from either SDWebImageor AFNetworking.

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

If you're going to fix this yourself, it takes a little work to get all of this right. But here is a rendition of LazyTableImagesthat uses NSOperationQueueto ensure that we (a) limit concurrent requests; and (b) allows us to cancel requests. I confess I prefer the above UIImageViewcategory implementations, better, but I tried to stay with the structure of Apple's original LazyTableImages, while remedying the flaws I've enumerated above.

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

回答by ahwulf

Assuming your UITableViewCell has an url property and a UIImage property for the image (plus a property or static value for the queue:

假设您的 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;
                            }
                       });
    }];
}

The issue with your implementation is that your image loading method is probably occurring in the main thread. You need to load the image asynchronously and then update the image in the main thread.

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

To be correct the block should check to see if the response's url matches the requested url if the cell's image loaded too late before the cell was reused for a different image.

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

回答by Josip B.

I've done this framework which contains lazy load controller. It's easy to use and works out of the box. https://github.com/cloverstudio/CSUtilsTutorial can be found here: http://www.clover-studio.com/blog/using-csutils-ios-framework-for-lazy-loading-images/

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

回答by Nirav Bhatt

This is quite late answer, but with iOS version changes the approach keeps changing.

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

In order to lazily load the images, here is the recommended approach:

为了延迟加载图像,这里是推荐的方法:

  1. Download URLs of all images and store them into your container (array / objects etc)
  2. Fire a NSURLSessionTask(post iOS 7 only) which runs async on background queue. If you are below iOS 7, you can use NSURLConnection SendAsynchronousRequest API- it's deprecated in iOS 9 so you better get rid of that soon.
  3. Create your images while on background queue
  4. Come back to main queue, ensure that you got the right UITableViewCell, then update the image
  1. 下载所有图像的 URL 并将它们存储到您的容器中(数组/对象等)
  2. 触发在后台队列上异步运行的NSURLSessionTask(仅适用于 iOS 7 之后)。如果你低于 iOS 7,你可以使用NSURLConnection SendAsynchronousRequest API- 它在 iOS 9 中被弃用,所以你最好尽快摆脱它。
  3. 在后台队列中创建图像
  4. 回到主队列,确保你得到了正确的 UITableViewCell,然后更新图像

Here - the entire approach is described in my tutorial.

这里 - 整个方法在我的教程中进行了描述