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?
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
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.