ios - Async image loading from url inside a UITableView cell - image changes to wrong image while scrolling -


i've written 2 ways async load pictures inside uitableview cell. in both cases image load fine when i'll scroll table images change few times until scroll end , image go right image. have no idea why happening.

#define kbgqueue dispatch_get_global_queue(dispatch_queue_priority_default, 0)  - (void)viewdidload {     [super viewdidload];     dispatch_async(kbgqueue, ^{         nsdata* data = [nsdata datawithcontentsofurl: [nsurl urlwithstring:                                                        @"http://myurl.com/getmovies.php"]];         [self performselectoronmainthread:@selector(fetcheddata:)                                withobject:data waituntildone:yes];     }); }  -(void)fetcheddata:(nsdata *)data {     nserror* error;     myjson = [nsjsonserialization               jsonobjectwithdata:data               options:kniloptions               error:&error];     [_mytableview reloaddata]; }      - (nsinteger)numberofsectionsintableview:(uitableview *)tableview {     // return number of sections.     return 1; }  - (nsinteger)tableview:(uitableview *)tableview numberofrowsinsection:(nsinteger)section{     // return number of rows in section.     // number of items in array (the 1 holds list)     nslog(@"myjson count: %d",[myjson count]);     return [myjson count]; }     - (uitableviewcell *)tableview:(uitableview *)tableview cellforrowatindexpath:(nsindexpath *)indexpath{          mycell *cell = [tableview dequeuereusablecellwithidentifier:@"cell"];         if (cell == nil) {             cell = [[mycell alloc] initwithstyle:uitableviewcellstyledefault reuseidentifier:@"cell"];         }          dispatch_async(kbgqueue, ^{         nsdata *imgdata = [nsdata datawithcontentsofurl:[nsurl urlwithstring:[nsstring stringwithformat:@"http://myurl.com/%@.jpg",[[myjson objectatindex:indexpath.row] objectforkey:@"movieid"]]]];              dispatch_async(dispatch_get_main_queue(), ^{         cell.poster.image = [uiimage imagewithdata:imgdata];             });         });          return cell; } 

... ...

- (uitableviewcell *)tableview:(uitableview *)tableview cellforrowatindexpath:(nsindexpath *)indexpath{              mycell *cell = [tableview dequeuereusablecellwithidentifier:@"cell"];             if (cell == nil) {                 cell = [[mycell alloc] initwithstyle:uitableviewcellstyledefault reuseidentifier:@"cell"];             }     nsurl* url = [nsurl urlwithstring:[nsstring stringwithformat:@"http://myurl.com/%@.jpg",[[myjson objectatindex:indexpath.row] objectforkey:@"movieid"]]];     nsurlrequest* request = [nsurlrequest requestwithurl:url];       [nsurlconnection sendasynchronousrequest:request                                        queue:[nsoperationqueue mainqueue]                            completionhandler:^(nsurlresponse * response,                                                nsdata * data,                                                nserror * error) {                                if (!error){                                    cell.poster.image = [uiimage imagewithdata:data];                                    // whatever want image                                }                             }];      return cell; } 

assuming you're looking quick tactical fix, need make sure cell image initialized , cell's row still visible, e.g:

- (uitableviewcell *)tableview:(uitableview *)tableview cellforrowatindexpath:(nsindexpath *)indexpath {     mycell *cell = [tableview dequeuereusablecellwithidentifier:@"cell" forindexpath:indexpath];      cell.poster.image = nil; // or cell.poster.image = [uiimage imagenamed:@"placeholder.png"];      nsurl *url = [nsurl urlwithstring:[nsstring stringwithformat:@"http://myurl.com/%@.jpg", self.myjson[indexpath.row][@"movieid"]]];      nsurlsessiontask *task = [[nsurlsession sharedsession] datataskwithurl:url completionhandler:^(nsdata * _nullable data, nsurlresponse * _nullable response, nserror * _nullable error) {         if (data) {             uiimage *image = [uiimage imagewithdata:data];             if (image) {                 dispatch_async(dispatch_get_main_queue(), ^{                     mycell *updatecell = (id)[tableview cellforrowatindexpath:indexpath];                     if (updatecell)                         updatecell.poster.image = image;                 });             }         }     }];     [task resume];      return cell; } 

the above code addresses few problems stemming fact cell reused:

  1. you're not initializing cell image before initiating background request (meaning last image dequeued cell still visible while new image downloading). make sure nil image property of image views or else you'll see flickering of images.

  2. a more subtle issue on slow network, asynchronous request might not finish before cell scrolls off screen. can use uitableview method cellforrowatindexpath: (not confused named uitableviewdatasource method tableview:cellforrowatindexpath:) see if cell row still visible. method return nil if cell not visible.

    the issue cell has scrolled off time async method has completed, and, worse, cell has been reused row of table. checking see if row still visible, you'll ensure don't accidentally update image image row has since scrolled off screen.

  3. somewhat unrelated question @ hand, still felt compelled update leverage modern conventions , api, notably:

    • use nsurlsession rather dispatching -[nsdata contentsofurl:] background queue;

    • use dequeuereusablecellwithidentifier:forindexpath: rather dequeuereusablecellwithidentifier: (but make sure use cell prototype or register class or nib identifier); and

    • i used class name conforms cocoa naming conventions (i.e. start uppercase letter).

even these corrections, there issues:

  1. the above code not caching downloaded images. means if scroll image off screen , on screen, app may try retrieve image again. perhaps you'll lucky enough server response headers permit transparent caching offered nsurlsession , nsurlcache, if not, you'll making unnecessary server requests , offering slower ux.

  2. we're not canceling requests cells scroll off screen. thus, if rapidly scroll 100th row, image row backlogged behind requests previous 99 rows aren't visible anymore. want make sure prioritize requests visible cells best ux.

the simplest fix addresses these issues use uiimageview category, such provided sdwebimage or afnetworking. if want, can write own code deal above issues, it's lot of work, , above uiimageview categories have done you.


Comments

Popular posts from this blog

blackberry 10 - how to add multiple markers on the google map just by url? -

php - guestbook returning database data to flash -

delphi - Dynamic file type icon -