Jimmypooza
Jimmypooza

Reputation: 97

cell label text changes on selection after scroll

I am having an issue with tableview cells that are created from 2 different prototype cells. I add my own labels and images to them.

They load fine, but after i scroll in the tableview, and then select a cell, the labels from other cells are getting added to the labels already present in the cell.

I've read about similar issues people are having but none address the fact that it only occurs on selection.

I have tried adding if (cell == nil){}; but that has no effect.

My cellForRowAtIndexPath is as follows:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"NDSClassSubMenuViewController cellForRowAtIndexPath: running...");



    NDSClassAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    NSDictionary *childNodes = [appDelegate.children objectAtIndex:0];
    NSString *multicellstat = [childNodes objectForKey:@"@CELLTYPE"];
    NSLog(@"Multicellstat %@", multicellstat);
    NSLog(@"TESTING CHILD %@", childNodes);

    //ONLY LOADING SUB MENU OPTIONS
NSString *cellType;
    if (multicellstat == NULL){
        NSLog(@"cellForRowAtIndexPath: @CellType == multicell. Making multicell.");
        cellType = @"SegueCellSubmenu";
    }else {
        NSLog(@"cellForRowAtIndexPath: children variable is NOT NULL. Making MenuCell.");
        cellType = @"SegueCellMulti";

    }


    UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:cellType forIndexPath:indexPath];

    if (multicellstat == NULL){
        NSLog(@"Not adding picture.");
    }else {


        dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        //this will start the image loading in bg
        dispatch_async(concurrentQueue, ^{

            UIView *blackBG = [[UIView alloc] initWithFrame:CGRectMake(5, 5, 60, 60)];
            blackBG.backgroundColor = [UIColor grayColor];

            NSURL *imageURL = [NSURL URLWithString:[childNodes objectForKey:@"@THUMBNAIL"]];
        NSData *data = [NSData dataWithContentsOfURL:imageURL];
        UIImage *image = [UIImage imageWithData:data];

        UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
        imageView.frame = CGRectMake(0, 0, 60, 60);
            int borderWidth = 1;
            imageView.frame = CGRectMake(borderWidth, borderWidth, blackBG.frame.size.width-borderWidth*2, blackBG.frame.size.height-borderWidth*2);


            dispatch_async(dispatch_get_main_queue(), ^{

                [cell addSubview:blackBG];
                [blackBG addSubview:imageView];


         });
        });


    }


    //NSDictionary *tweet = [[[[appDelegate.menuItems objectForKey:@"Table"] objectForKey:@"Parent"]objectAtIndex:indexPath.row] objectForKey:@"Child"];
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(70, 10, 210, 30)];
    UILabel *detailText = [[UILabel alloc] initWithFrame:CGRectMake(70, 35, 210, 30)];

    [cell.contentView addSubview:label];
    [cell.contentView addSubview:detailText];


    NSString *text = [[appDelegate.children objectAtIndex:indexPath.row] objectForKey:@"@MENUDESC"];
    NSString *name = [[appDelegate.children objectAtIndex:indexPath.row] objectForKey:@"@MENUDESC"];
    //NSLog(@"cellForRowAtIndexPath MENU ITEMS %@", text);

    //Title label
    label.text = NULL;
    label.text = text;
    [label setFont: [UIFont fontWithName:@"Arial" size:16.0]];

//Detail label
    detailText.text = NULL;
    detailText.text = name;
    [detailText setFont: [UIFont fontWithName:@"Arial" size:12.0]];
    //detailText.textColor = [UIColor colorWithRed:186.0/255.0 green:186.0/255.0 blue:186.0/255.0 alpha:1];
    detailText.textColor = [UIColor grayColor];

    //cell.textLabel.text = text;
    //cell.detailTextLabel.text = [NSString stringWithFormat:@"by %@", name];


    NSLog(@"Creating submenu item: %@", text);
     NSLog(@"NDSClassSubMenuViewController: cellForRowAtIndexPath: finished.");



    return cell;

}

Anyone know how I can solve this?

Upvotes: 0

Views: 582

Answers (2)

Jimmypooza
Jimmypooza

Reputation: 97

I solved the problem by creating my own custom cell class and using storyboard to set the layout.

Upvotes: 0

danh
danh

Reputation: 62686

The key issue is that the code running synch should not assume that the cell it's modifying after the image request completes is the same cell (at the same indexPath) as when the image request began.

The correct recipe is like this:

  1. use a cached result if we have one (so we don't do web requests every time we scroll), otherwise make an asynch request.
  2. when the request completes, cache the result
  3. use the original indexPath to determine if the cell is still visible. if it is, reload that indexPath. the newly cached result will now be available in cellForRowAtIndexPath.

Here's a snippet of code that describes how to correctly update at table after an asynch request. In your cellForRowAtIndexPath, check for a cached response before calling this:

// in cellForRowAtIndexPath:
NSString *urlString = [childNodes objectForKey:@"@THUMBNAIL"]
NSURL *imageURL = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:imageURL];
NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
if (!cachedResponse) { 
    // we don't find this image/request in the cache, call asynch load
    [self asynchLoad:urlString forIndexPath:indexPath];
} else {
   // we got this image already, update the cell
    UIImage *image = [UIImage imageWithData:cachedResponse.data];
    // set the cell's UIImageView subview image to image
}

Here we load the image at urlString, cache the result, and then reload the indexPath after the load if it's still visible after the request completes:

- (void)asynchLoad:(NSString *)urlString forIndexPath:(NSIndexPath *)indexPath {

    NSURL *imageURL = [NSURL URLWithString:[childNodes objectForKey:@"@THUMBNAIL"]];
    NSURLRequest *request = [NSURLRequest requestWithURL:imageURL];

    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        if (!error) {

            // cache the response
            NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data];
            [[NSURLCache sharedURLCache] storeCachedResponse:cachedResponse forRequest:self];

            // important part - we make no assumption about the state of the table at this point
            // find out if our original index path is visible, then update it, taking 
            // advantage of the cached image (and a bonus option row animation)

            NSArray *visiblePaths = [self.tableView indexPathsForVisibleRows];
            if ([visiblePaths containsObject:indexPath]) {
                NSArray *indexPaths = [NSArray arrayWithObject:indexPath];
                [self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation: UITableViewRowAnimationFade];
                // because we cached the image, cellForRow... will see it and run fast
            }
        }
    }];
}

Upvotes: 1

Related Questions