iOS Refresh button in View Controller Nav: reloading all tableViewCells created from parsed JSON when clicked

Pat picture Pat · Dec 22, 2011 · Viewed 10.4k times · Source

I've got a fairly important conceptual issue that many people have asked about, but there isn't a readily available clear answer to be found by searching.

My application is simple: Several rows of TableViewCells populated with data from a parsed JSON feed. When a cell is clicked on, that cell's info is passed to a SecondViewController and displayed. The JSON feed is also stored to a .plist and in the case that the internet is not available, the TableViewCells are populated from the .plist.

This is all working great.

However, the last thing I need is a refresh button at the top of my FirstViewController to refresh the JSON feed, and all of the cells in the table with the new data from the new variables. However, I've encountered an issue with implementing this:

My original JSON call, and variables to populate the cells are located in the ViewDidLoad method. When the view loads, these variables are "set" and don't refresh. Further, I can move the JSON call and variables into viewWillLoad - which will refresh the table each time after clicking on a cell, and then clicking "back" to the firstViewController -- this will update the JSON and cells successfully, however it does impact the speed and makes the view controller "pause" when going back to the MainViewController, which makes calling my original JSON and setting my variables in viewWillLoad an unviable option.

I have created a reload button in ViewDidLoad, which is linked to an IBAction method "refresh":

Create Button Programitically in ViewDidLoad:

// Reload issues button
UIBarButtonItem *button = [[UIBarButtonItem alloc]
                           initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh
                           target:self
                           action:@selector(refresh:)];
self.navigationItem.rightBarButtonItem = button;
[button release];

Action Method it's linked to:

- (IBAction)refresh:(id)sender {

    myRawJson = [[NSString alloc] initWithContentsOfURL:[NSURL  
                                  URLWithString:@"http://www.yoursite.com/json.JSON"]  
                                  encoding:NSUTF8StringEncoding 
                                  error:nil];

    SBJsonParser *parser = [[SBJsonParser alloc] init];
    NSDictionary * myParsedJson = [parser objectWithString:myRawJson error:NULL];

// New updated dictionary built from refreshed JSON
    allLetterContents = [myParsedJson objectForKey:@"nodes"];

// Log the new refreshed JSON
    NSLog(@"You clicked refresh. Your new JSON is %@", myRawJson);

    //Maybe use the notification center?? But don't know how to implement.
    //[[NSNotificationCenter defaultCenter] addObserver:self 
                                            selector:@selector(refreshView:) 
                                            name:@"refreshView" object:nil];
    //[[NSNotificationCenter defaultCenter] postNotificationName:@"refreshView" 
                                            object:nil];

    }

    [self.tableView reloadRowsAtIndexPaths:[self.tableView indexPathsForVisibleRows] 
                     withRowAnimation:UITableViewRowAnimationNone];

    [myRawJson release];
}

In the code above you can see that I'm re-calling the JSON each time the button is clicked and logging a message to console with the new JSON. This is working. I've even re-built a dictionary which is successfully adding the new content.

My question is: How can I make the tableViewCells "refresh" with this new data as well? Can I just make the button re-load the entire view controller - so it would call ViewDidLoad again? Do I need to re-think my apps structure, or move my original variables out of viewDidLoad?

I've been reading some posts on the NSNotificationCenter, but the implementation of this still baffles me, as I'm fairly new to iOS development.

Thanks~


Update:


It's still not updating. Here is my full refresh button code with [self.tableView reloadData]; called at the end of my IBAction.

    - (IBAction)refresh:(id)sender {
        [DSBezelActivityView newActivityViewForView:        
                         self.navigationController.navigationBar.superview     
                         withLabel:@"Loading Feed..." width:160];

        myRawJson = [[NSString alloc] initWithContentsOfURL:[NSURL 
                     URLWithString:@"http://site.com/mobile.JSON"] 
                     encoding:NSUTF8StringEncoding 
                     error:nil];

        SBJsonParser *parser = [[SBJsonParser alloc] init];
        NSDictionary * myParsedJson = [parser objectWithString:myRawJson error:NULL];
        allLetterContents = [myParsedJson objectForKey:@"nodes"];

        BOOL isEmpty = ([myParsedJson count] == 0);

        if (isEmpty) {
            NSString *refreshErrorMessage = [NSString 
stringWithFormat:@"An internet or network connection is required."];
            UIAlertView *alert = [[UIAlertView alloc] 
                                 initWithTitle:@"Alert" 
                                 message: refreshErrorMessage 
                                 delegate:self 
                                 cancelButtonTitle:@"Close" 
                                 otherButtonTitles:nil];
            [alert show];
            [alert release];

            allLetterContents = [NSMutableDictionary 
                                dictionaryWithContentsOfFile:[self saveFilePath]];
            //NSLog(@"allLetterContents from file: %@", allLetterContents);

        } else {

        NSLog(@"Your new allLetterContents is %@",  allLetterContents);

          // Fast enumeration through the allLetterContents NSMutableDictionary
          for (NSMutableDictionary * key in allLetterContents) {
            NSDictionary *node = [key objectForKey:@"node"];
            NSMutableString *contentTitle = [node objectForKey:@"title"];        
            NSMutableString *contentNid = [node objectForKey:@"nid"];
            NSMutableString *contentBody = [node objectForKey:@"body"];
            // Add each Title and Nid to specific arrays
            //[self.contentTitleArray addObject:contentTitle];
            [self.contentTitleArray addObject:[[contentTitle 
                                     stringByReplacingOccurrencesOfString:@"&" 
                                     withString:@"&"] mutableCopy]];
            [self.contentNidArray addObject:contentNid];
            [self.contentBodyArray addObject:contentBody];
          }

        }

        [self.tableView reloadData];

        [DSBezelActivityView removeViewAnimated:YES];
        [myRawJson release];
    }

I'm configuring the cell at cellForRowAtIndexPath (Updated: Posted entire method):

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
        if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
            cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
        }
    }

    // Configure the cell.
    cell.textLabel.text = [self.contentTitleArray objectAtIndex: [indexPath row]];
    cell.detailTextLabel.text = [self.contentNidArray objectAtIndex: [indexPath row]];
    return cell;
}

Setting it on didSelectRowAtIndexPath:

self.detailViewController.currentNodeTitle = [contentTitleArray 
                                             objectAtIndex:indexPath.row];
self.detailViewController.currentNodeNid= [contentNidArray 
                                          objectAtIndex:indexPath.row];
self.detailViewController.currentNodeBody = [contentBodyArray 
                                            objectAtIndex:indexPath.row];

So when clicking my refresh button the table should* refresh with the new json, but does not.. Am I missing a step?

enter image description here

Additionally this may not be important, but I'm changing the colors for every other row with:

// Customize the appearance of table view cells.
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.row % 2)
    {
        [cell setBackgroundColor:[UIColor colorWithRed:221.0/255.0 green:238.0/255.0 blue:255.0/255.0 alpha:1]];
        cell.textLabel.textColor = [UIColor colorWithRed:2.0/255.0 green:41.0/255.0 blue:117.0/255.0 alpha:1];
        cell.detailTextLabel.textColor = [UIColor colorWithRed:2.0/255.0 green:41.0/255.0 blue:117.0/255.0 alpha:1];

    }    else [cell setBackgroundColor:[UIColor clearColor]];
}

Update

enter image description here

Answer

Gustavo Barrientos picture Gustavo Barrientos · Dec 22, 2011

You need to call the reload method.

[self.tableView reloadData];

This will fire the dataSource and delegate events an will refresh the UITableView.

You can find more info in the UITableView Class Reference:

Call this method to reload all the data that is used to construct the table, including cells, section headers and footers, index arrays, and so on. For efficiency, the table view redisplays only those rows that are visible.