1

I am fetching images synchronously from an array which stores URLs of images but it work very slowly. Now i want to load them asynchronously for fast working.

Heres the code and provide answer with coding.

#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
1

3 Answers 3

1

We can use dispatch_async to run the operation asynchronously.

Try this:

- (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;
}
Sign up to request clarification or add additional context in comments.

2 Comments

Would you be able to edit this to explain why it might help the reader? What change did you make in this code, or what feature would you draw readers' attention to?
@halfer My intention was to make the reader aware of "dispatch_async" by using which we can achieve asynchronous operation. I have edited the answer with attention point. Thanks
0

You can do like this:

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

Ref: Asynchronous downloading of images for UITableView with GCD

5 Comments

Can you please help me in putting activity indicator view on the view till photos are loading. need coded help. thanks
I'm thinking about the "the first row is not visible, all the rest are visible now" (maybe not same as origin). Haha.
@NavjotSingh activity indicator, maybe you should post another question. Easy for following.
i can't put more then one question in one day .
@NavjotSingh well, I don't know that. In my opinion, you shouldn't use indicator. You should use a default image(like placeholder) when loading.
0

Just by simply setting the following works fine for me .

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

You can use the activity indicator as you have asked . Just drag and drop UIActivityIndicatorView onto the UIImageView of the UITableViewCell and set the needed constraints . Once the image gets loaded you can set it as hidden .

To do it programmatically , you can add a subview to the Image view in the UITableViewCell. Once the Image gets loaded you can remove the sub view .

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.