如何从 API/URL 异步加载图像?

Posted

技术标签:

【中文标题】如何从 API/URL 异步加载图像?【英文标题】:How to load images from API/URL asynchronously? 【发布时间】:2016-02-10 21:45:39 【问题描述】:

我正在从一个存储图像 URL 的数组中同步获取图像,但它的工作速度非常慢。现在我想异步加载它们以便快速工作。

这是代码并提供编码答案。

#import "DetailViewController.h"
#import "FinalViewController.h"

@interface DetailViewController ()

@end

@implementation DetailViewController
@synthesize jsonData;

- (void)viewDidLoad 
    [super viewDidLoad];
    self.title =  @"Select a Photo";

    // Do any additional setup after loading the view.
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;

    NSURL *url = [NSURL URLWithString:@"http://json.code.com/albums/1/photos"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [[NSURLConnection alloc] initWithRequest:request delegate:self];




-(void)connection:(NSURLConnection *)connection didReceiveResponse:(nonnull NSURLResponse *)response

    data1 = [[NSMutableData alloc] init];



-(void)connection:(NSURLConnection *)connection didReceiveData:(nonnull NSData *)theData

    [data1 appendData:theData];


-(void)connectionDidFinishLoading:(NSURLConnection *)connection

    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;

    jsonArray1 = [NSJSONSerialization JSONObjectWithData:data1 options:nil error:nil];
    [mainTableView reloadData];




-(void)connection:(NSURLConnection *)connection didFailWithError:(nonnull NSError *)error

    UIAlertView *errorView = [[UIAlertView alloc]initWithTitle:@"Error" message:@"Please make sure you are connected to either 3G or Wi-Fi." delegate:nil cancelButtonTitle:@"Dismiss" otherButtonTitles:nil, nil];
    [errorView show];
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;




- (void)didReceiveMemoryWarning 
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.


- (int)numberOfSectionInTableView:(UITableView *)tableView

    return 1;


- (int) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

    return  [jsonArray1 count];


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

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    if (cell == nil) 
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"Cell"];
    

    cell.textLabel.text = [[jsonArray1 objectAtIndex:indexPath.row] objectForKey:@"title"];
    cell.detailTextLabel.text = [NSString stringWithFormat:@"URL : %@", [[jsonArray1 objectAtIndex:indexPath.row] objectForKey:@"url"]];

    NSURL *URL = [[NSURL alloc] initWithString:[[jsonArray1 objectAtIndex:indexPath.row] valueForKey:@"thumbnailUrl"]];

    NSData *URLData = [[NSData alloc] initWithContentsOfURL:URL];
    [[cell imageView]setImage:[UIImage imageWithData:URLData]];


    return cell;



-(void)tableView:(UITableView *)tableview didSelectRowAtIndexPath:(NSIndexPath *)indexPath

    FinalViewController *fvc = [[FinalViewController alloc] initWithNibName:@"FinalViewController" bundle:nil];
    fvc.jsonData2 = [jsonArray1 objectAtIndex:indexPath.row];
    [self.navigationController pushViewController:fvc animated:YES];




@end

【问题讨论】:

使用github.com/rs/SDWebImage 【参考方案1】:

你可以这样做:

cell.tag = indexPath.row;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^(void) 

    NSData *imageData = [NSData dataWithContentsOfURL: URL];

    UIImage* image = [[UIImage alloc] initWithData:imageData];
    if (image) 
         dispatch_async(dispatch_get_main_queue(), ^
             if (cell.tag == indexPath.row) 
                 cell.imageView.image = image;
                 [cell setNeedsLayout];
             
         );
     
);

参考:Asynchronous downloading of images for UITableView with GCD

【讨论】:

你能帮我把活动指示器视图放在视图上,直到照片加载。需要编码帮助。谢谢 我在想“第一行不可见,其余的现在都可见”(可能与原点不同)。哈哈。 @NavjotSingh 活动指标,也许你应该发布另一个问题。易于跟踪。 我一天只能提出一个问题。 @NavjotSingh 好吧,我不知道。在我看来,你不应该使用指标。加载时应使用默认图像(如占位符)。【参考方案2】:

我们可以使用dispatch_async来异步运行操作。

试试这个:

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

    myCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    if (cell == nil) 
        cell = [[myCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
    

    cell.poster.image = nil; // or cell.poster.image = [UIImage imageNamed:@"placeholder.png"];

    dispatch_async(kBgQueue, ^
        NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://myurl.com/%@.jpg",[[myJson objectAtIndex:indexPath.row] objectForKey:@"movieId"]]]];
        if (imgData) 
            UIImage *image = [UIImage imageWithData:imgData];
            if (image) 
                dispatch_async(dispatch_get_main_queue(), ^
                    myCell *updateCell = (id)[tableView cellForRowAtIndexPath:indexPath];
                    if (updateCell)
                        updateCell.poster.image = image;
                );
            
        
    );
    return cell;

【讨论】:

您能否编辑它以解释为什么它可能对读者有所帮助?您对这段代码做了哪些更改,或者您会吸引读者注意哪些功能? @halfer 我的目的是让读者了解“dispatch_async”,通过它我们可以实现异步操作。我已经用注意点编辑了答案。谢谢【参考方案3】:

只需简单地设置以下对我来说就可以了。

cell.imageView.image =[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@",[imageArray objectAtIndex:i]]]]];

您可以按照您的要求使用活动指示器。只需将UIActivityIndicatorView 拖放到UITableViewCellUIImageView 上并设置所需的约束。加载图像后,您可以将其设置为 hidden 。

要以编程方式进行,您可以在UITableViewCell 的图像视图中添加一个子视图。加载图像后,您可以删除子视图。

UIActivityIndicatorView* actInd = [[UIActivityIndicatorView alloc]init];
[cell.imageView addSubview:actInd];

【讨论】:

以上是关于如何从 API/URL 异步加载图像?的主要内容,如果未能解决你的问题,请参考以下文章

iCarousel + Parse:从 PFObject 加载异步图像

如何将 UIImage 异步加载到 SwiftUI Image 中?

从firebase异步加载图像时出错

使用 NSOperationQueue 异步从 Web 加载多个图像

android从网络上异步加载图像

将图像异步加载到自定义 UITableViewCell 部分工作