简体   繁体   中英

UITableView image loading with json is not loading properly

I am using this to display some labels and images by JSON parsing in to tableView but images are not coming at the first launch when scrolling the tableview images are coming and dancing i mean not coming in order format help me with this

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
        self.customCellClass = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
        if (self.customCellClass == nil)
        {
            self.customCellClass = [[CellCustom alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
        }
        self.customCellClass.nameLabel.text = [[arrayData objectAtIndex:indexPath.row] objectForKey:@"name"]; // label
        self.customCellClass.cityLabel.text = [[arrayData objectAtIndex:indexPath.row] objectForKey:@"region"]; // label

        NSString * stripped = [[[arrayData objectAtIndex:indexPath.row] objectForKey:@"summary"] stripHtml]; //label
        self.customCellClass.detailLabel.text = stripped;
        self.customCellClass.mainImage.image = nil;


        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void) {
            NSData *data0 = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[[arrayData objectAtIndex:indexPath.row]objectForKey:@"images"]objectForKey:@"logo"]]];
            UIImage *image = [UIImage imageWithData:data0];

            dispatch_sync(dispatch_get_main_queue(), ^(void) {
                self.customCellClass.mainImage.image = image;
            });
        });
        return self.customCellClass;

}

Replace below code in UITableView's cellForRowAtIndexPath method :

//Check if cell has image or not
if(!self.customCellClass.mainImage.image)
{
   dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
   dispatch_async(q, ^{

      NSData *data0 = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[[arrayData objectAtIndex:indexPath.row]objectForKey:@"images"]objectForKey:@"logo"]]];
      UIImage *image = [UIImage imageWithData:data0];

      //Get main queue
      dispatch_async(dispatch_get_main_queue(), ^{

       /* This is the main thread again, where we set the tableView's image to be what we just fetched. */

       self.customCellClass.mainImage.image = image;

      });

   });
}

Good solution is if you are using AFNetworking then use UIImageView+AFNetworking category

NSURL *imageURL = [NSURL URLWithString:[[[arrayData objectAtIndex:indexPath.row]objectForKey:@"images"]objectForKey:@"logo"]]
[self.customCellClass.mainImage setImageWithURL:imageURL placeholderImage:nil];

You are assigning image=nil every time when cell is loading and downloading the same image.

You can user following class to do this seamlessly.

AsyncImageView.h

#import <UIKit/UIKit.h>


@interface AsyncImageView : UIView {
    NSURLConnection *connection;
    NSMutableData *data;
    NSString *urlString; // key for image cache dictionary
}

-(void)loadImageFromURL:(NSURL*)url;
-(void)loadBackgroundImage:(UIImage *)image;
-(UIImage*)loadImageFromURLForGetIamge:(NSURL*)url;
-(BOOL)getCachedImageWithUrl:(NSString*)url;

@end

and AsyncImageView.m

#import "AsyncImageView.h"
#import "ImageCacheObject.h"
#import "ImageCache.h"

//
// Key's are URL strings.
// Value's are ImageCacheObject's
//
static ImageCache *imageCache = nil;

#define SPINNY_TAG 5555   

@implementation AsyncImageView


- (id)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
    }
    return self;
}


- (void)drawRect:(CGRect)rect {
    // Drawing code
}


- (void)dealloc {
    [connection cancel];

}

-(void)loadBackgroundImage:(UIImage *)image
{
    UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
    imageView.contentMode = UIViewContentModeScaleAspectFill;
    imageView.layer.masksToBounds=YES;

    //imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    [self addSubview:imageView];
    imageView.frame = self.bounds;
    [imageView setNeedsLayout]; 
    [self setNeedsLayout];

    imageView=nil;

}

-(void)loadImageFromURL:(NSURL*)url
{
    if (connection != nil) {
        [connection cancel];
        connection = nil;
    }
    if (data != nil) {
        data = nil;
    }

    if (imageCache == nil) // lazily create image cache
        imageCache = [[ImageCache alloc] initWithMaxSize:2*1024*1024];  // 2 MB Image cache
  //  NSLog(@"Value of the url here = %@",url);
    urlString = [[url absoluteString] copy];
    UIImage *cachedImage = [imageCache imageForKey:urlString];
    if (cachedImage != nil)
    {
        if ([[self subviews] count] > 0)
        {
            [[[self subviews] objectAtIndex:0] removeFromSuperview];
        }
        UIImageView *imageView = [[UIImageView alloc] initWithImage:cachedImage];
        imageView.contentMode = UIViewContentModeScaleAspectFill;
        imageView.layer.masksToBounds=YES;

        [self addSubview:imageView];
        imageView.frame = self.bounds;
        [imageView setNeedsLayout]; // is this necessary if superview gets setNeedsLayout?
        [self setNeedsLayout];

        imageView=nil;
        return;
    }


    UIActivityIndicatorView *spinny = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    spinny.tag = SPINNY_TAG;
    spinny.frame=CGRectMake(self.frame.size.width/2-10, self.frame.size.height/2-10, 20, 20);
    spinny.center = self.center;
    [spinny startAnimating];
    [self addSubview:spinny];
    [self bringSubviewToFront:spinny];

    spinny=nil;

    NSURLRequest *request = [NSURLRequest requestWithURL:url 
                                             cachePolicy:NSURLRequestUseProtocolCachePolicy 
                                         timeoutInterval:60.0];
    connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}


-(BOOL)getCachedImageWithUrl:(NSString*)url
{
    BOOL isIamgeCached=NO;

    if (imageCache == nil)
        imageCache = [[ImageCache alloc] initWithMaxSize:2*1024*1024];  // 2 MB Image cache

    UIImage *cachedImage = [imageCache imageForKey:url];
    if (cachedImage != nil)
    {
        isIamgeCached=YES;
    }
    return isIamgeCached;
}


-(UIImage*)loadImageFromURLForGetIamge:(NSURL*)url
{
    if (connection != nil)
    {
        [connection cancel];
        connection = nil;
    }
    if (data != nil)
    {
        data = nil;
    }

    if (imageCache == nil) // lazily create image cache
        imageCache = [[ImageCache alloc] initWithMaxSize:2*1024*1024];  // 2 MB Image cache
    //  NSLog(@"Value of the url here = %@",url);
    urlString = [[url absoluteString] copy];
    UIImage *cachedImage = [imageCache imageForKey:urlString];
    if (cachedImage != nil)
    {
        if ([[self subviews] count] > 0)
        {
            [[[self subviews] objectAtIndex:0] removeFromSuperview];
        }
        UIImageView *imageView = [[UIImageView alloc] initWithImage:cachedImage];
        imageView.contentMode = UIViewContentModeScaleToFill;
        imageView.autoresizingMask =
        UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        [self addSubview:imageView];
        imageView.frame = self.bounds;
        [imageView setNeedsLayout]; // is this necessary if superview gets setNeedsLayout?
        [self setNeedsLayout];

        imageView=nil;
        return cachedImage;
    }


    UIActivityIndicatorView *spinny = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    spinny.tag = SPINNY_TAG;
    spinny.frame=CGRectMake(self.frame.size.width/2-10, self.frame.size.height/2-10, 20, 20);
    //spinny.center = self.center;
    [spinny startAnimating];
    [self addSubview:spinny];
    [self bringSubviewToFront:spinny];

    spinny=nil;

    NSURLRequest *request = [NSURLRequest requestWithURL:url
                                             cachePolicy:NSURLRequestUseProtocolCachePolicy
                                         timeoutInterval:20.0];
    connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];


    return cachedImage;
}


- (void)connection:(NSURLConnection *)connection 
    didReceiveData:(NSData *)incrementalData {
    if (data==nil)
    {
        data = [[NSMutableData alloc] initWithCapacity:2048];
    }
    [data appendData:incrementalData];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection
{
    connection = nil;

    UIView *spinny = [self viewWithTag:SPINNY_TAG];
    [spinny removeFromSuperview];

    if ([[self subviews] count] > 0)
    {
        [[[self subviews] objectAtIndex:0] removeFromSuperview];
    }

    UIImage *image = [UIImage imageWithData:data];

    [imageCache insertImage:image withSize:[data length] forKey:urlString];

    UIImageView *imageView = [[UIImageView alloc] 
                               initWithImage:image];
    imageView.contentMode = UIViewContentModeScaleToFill;
    imageView.autoresizingMask = 
        UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    [self addSubview:imageView];
    imageView.frame = self.bounds;
    [imageView setNeedsLayout]; // is this necessary if superview gets setNeedsLayout?
    [self setNeedsLayout];

    imageView=nil;
    data = nil;
}

@end

You use dispatch_async to load image from remote, that means the images to be displayed in tableview cells are loaded asynchronously. And you use a instance variable to record current cell, which cause the problem you met.

After a image being loaded finished (may take few minutes), it want to be displayed in a cell (written in this code),

dispatch_sync(dispatch_get_main_queue(), ^(void) {
    self.customCellClass.mainImage.image = image;
});

only to find that the self.customCellClass points to a wrong cell (the - (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath was called several times during the image being loaded, and each calling changes the pointing of self.customCellClass to other cell).

So, the order of images is wrong.

Try this:

Use a locality variable to keep the cell get from dequeueReusableCellWithIdentifier . Like this:

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:     (NSIndexPath *)indexPath
{
    CellCustom *customCellClass = (CellCustom *)[tableView dequeueReusableCellWithIdentifier:@"Cell"];
    if (customCellClass == nil)
    {
        customCellClass = [[CellCustom alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
    }
    customCellClass.nameLabel.text = [[arrayData objectAtIndex:indexPath.row] objectForKey:@"name"]; // label
    customCellClass.cityLabel.text = [[arrayData objectAtIndex:indexPath.row] objectForKey:@"region"]; // label

    NSString * stripped = [[[arrayData objectAtIndex:indexPath.row] objectForKey:@"summary"] stripHtml]; //label
    customCellClass.detailLabel.text = stripped;
    customCellClass.mainImage.image = nil;


    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void) {
        NSData *data0 = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[[arrayData objectAtIndex:indexPath.row]objectForKey:@"images"]objectForKey:@"logo"]]];
        UIImage *image = [UIImage imageWithData:data0];

        dispatch_sync(dispatch_get_main_queue(), ^(void) {
            customCellClass.mainImage.image = image;
        });
    });
    return customCellClass;
}

Besides, take a look at the difference between - (id)dequeueReusableCellWithIdentifier:(NSString *)identifier and - (id)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM