0

I have a UITableView, each row contains four UIButtons to represent users. I am using Parse.com as my data base. My code for - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath is below:

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

    FriendViewCell *cell = (FriendViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];


    for(int i =0; i<4; i++) {


        if((int)indexPath.row*4+i<[self.currentUser.friends count]) {


            FriendButton *button = cell.buttons[i];
            UILabel *label = cell.labels[i];


            [button setTag:(int)indexPath.row*4+i];
            PFObject *user =self.currentUser.friends[(int)indexPath.row*4+i];
            label.text=user[@"username"];

            UIImage *cachedImage = self.images[user[@"username"]];
            if (cachedImage) {
                //use cached image
                [button setBackgroundImage:cachedImage forState:UIControlStateNormal];
            }

            else {

                //download image
                PFFile *userImageFile = user[@"profilePic"];
                [userImageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
                    if (!error) {
                        UIImage *image = [UIImage imageWithData:imageData];
                        [self.images setObject:image forKey:user[@"username"]];
                        //this is the line that may be confusing it
                        [button setBackgroundImage:image forState:UIControlStateNormal];
                        //check we have the right number

                        NSLog(@"Number of friends %lu, number of cached images: %lu",(unsigned long)[self.currentUser.friends count], (unsigned long)[self.images count]);

                    }
                }];

            }



        }
}

return cell;

}

I am using a NSMutableDictionary to store the images. If the image doesn't exit in the dictionary the placeholder image will be used. I'n my custom FriendViewCell code I also have this method:

-(void)prepareForReuse {

    //this is called each time we reuse a cell
    UIImage *placeHolder = [UIImage imageNamed:@"placeHolder"];

    //reset placeholder incase we download images
    //Double check this that no images are being double used while loading..
    [self.button1 setBackgroundImage:placeHolder forState:UIControlStateNormal];
    [self.button2 setBackgroundImage:placeHolder forState:UIControlStateNormal];
    [self.button3 setBackgroundImage:placeHolder forState:UIControlStateNormal];
    [self.button4 setBackgroundImage:placeHolder forState:UIControlStateNormal];

}

To initially set back to the placeholder before checking if the image is in the dictionary. It all works but I've just noticed that if I do a clean install of the app and there are no cached files and then try to load the table with say 100 friends i.e. many images each buttons image will keep changing until they are all downloaded. I'm not really sure why this is happening. I fell like it might be because of [button setBackgroundImage:image forState:UIControlStateNormal]; which is in the download image block so it will mix things up as I scroll. Can anyone give me some pointers on this please?

KeXMeX
  • 53
  • 7

1 Answers1

0

so I solved the problem. This maybe useful for someone else. My new download block is:

//download image
                PFFile *userImageFile = user[@"profilePic"];
                [userImageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
                    if (!error) {
                        //put the image in the dictionary
                        UIImage *image = [UIImage imageWithData:imageData];
                        [self.images setObject:image forKey:user[@"username"]];
                        NSLog(@"Number of friends %lu, number of cached images: %lu",(unsigned long)[self.currentUser.friends count], (unsigned long)[self.images count]);
                        //fetch the cell again because reuse may have confused it
                        FriendViewCell *originalCell = (FriendViewCell *)[tableView cellForRowAtIndexPath:indexPath];
                        if (originalCell) {
                            FriendButton *button2 = originalCell.buttons[i];
                            [button2 setBackgroundImage:image forState:UIControlStateNormal];
                        }




                    }
                }];

I think if my understanding is correct after the download you have to go back and fetch the cell again from the original indexPath, if you don't do that it might send the image to the wrong cell due to the cell reuse. So create a new cell FriendViewCell *originalCell = (FriendViewCell *)[tableView cellForRowAtIndexPath:indexPath], and a new UIImageView (UIButton in my case) and then set that image. Seems to be working perfectly now, very simple fix too.

KeXMeX
  • 53
  • 7
  • You're right about the cell potentially being reused. A more elegant way to handle this is `[tableView reloadItemsAtIndexPaths:@[indexPath]];`. Then you're done. You can delete all of the logic that updates the cell, because your existing datasource method (cellForRowAtIndexPath) handles that, including handling the case where we have an image cached in self.images. Complete discussion in my answer here http://stackoverflow.com/questions/15799432/poor-uicollectionview-scrolling-performance-with-uiimage/15799771#15799771 – danh Mar 07 '15 at 20:26