I am working for the first time in objective c and have come across an issue that I have not seen an answer for.
I am loading a set of data from a JSON data set and using it to populate a UITableView within a UITableViewController.
First when the view is loaded (viewDidLoad) I populate the array with the JSON data from a URL.
Next the data loads as it should. numberOfRowsInSection shows that there are 30 results in the array which is correct.
However The Iphone loads the entire set three times into the tableview.
Here is some code from the controller for that view:(Twitter is being used as an example and is not the actual data set I use)
- (void)viewDidLoad {
[super viewDidLoad];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
//results = [[NSMutableArray alloc] init];
self.navigationItem.title=#"public_timeline";
self.tableView.allowsSelection = NO;
NSString *url = [[NSString alloc] init];
url = [[NSString alloc] initWithFormat:#"http://twitter.com/statuses/%s.json",#"public_timeline"];
if ([results count] == 0) {
[self parseJSON:url];
}
}
Here is the parseJSON (actual parse is done with the Cocoa JSON framework
-(void) parseJSON:(NSString *)URL{
NSURL *JSONURL = [NSURL URLWithString:URL];
NSData *responseData = [[NSData alloc] initWithContentsOfURL:JSONURL];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
results = [[NSMutableArray alloc] initWithArray:[responseString JSONValue]];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [results count];
}
then the cell's output
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
// Set up the cell...
}
NSDictionary *cdict = [results objectAtIndex:indexPath.row];
cell.textLabel.text = [cdict valueForKey:#"text"];
return cell;
}
I am not sure if what I am doing is the best way to do this, so if someone could help me out that would be great.
Check the numberOfSectionsInTableView Method and change the return Value to 1
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
Related
Hello I am trying to use EGOImageView inside a CustomTableViewCell who i made to customize the cell. This is the code where I used the EGOImageView.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString* simpleTableIdentifier = #"Albums";
CustomTableCell* cell = (CustomTableCell*)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (!cell)
{
cell = [[CustomTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
NSLog(#"Show once or more times");
}
NSDictionary* dictionary = (NSDictionary*)[self.albumCollection objectAtIndex:indexPath.row];
cell.label.text = [dictionary valueForKey:#"name"];
EGOImageView* imageView = [[EGOImageView alloc] initWithPlaceholderImage:[UIImage imageWithContentsOfFile:#""]];
[imageView setImageURL:[NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=small&access_token=%#", (NSString*)[dictionary valueForKey:#"id"], [[FBSession activeSession] accessToken]]]];
[imageView setFrame:CGRectMake(0.0f,0.0f,78.0f,78.0f )];
[cell.iView addSubview:imageView];
[imageView release];
The image on each cell loading the same image. Would it be because it reused the cell while loading the image.
I found a problem I can't think of why the problem happened. I used the graph api to grab the image https://graph.facebook.com/%#/picture?type=small&access_token=%# where the first parameter was the album id.
To make myself easy to see the problem I only used one album on the cell, no matter what album i used the same photo turned up. But when I copy the link to the browser, the actual photo url shown on the address bar with the image shown and it shown the correct photos.
Does anyone know what was wrong.
Here is example. It loads user pics from some server in background and updates cell image. Note that imageView.image is set to nil at the beginning. This is dome for the case of cell reuse so that you will have no image rather than wrong image for the time while its downloading.
One more thing to add is that, would be also good to have a cache so that it does not download images all the time. Another nice thing is to not download images in edge networks.
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"TransactionCell";
NSMutableArray *data = searching ? searchResult : dataSource;
NSDictionary *object = [data objectAtIndex:[indexPath row]];
UITableViewCell *cell = [_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithIdentifier:CellIdentifier] autorelease];
}
cell.imageView.image = nil;
cell.textLabel.text = #"Your cell text";
NSString *contact = #"foo#gmail.com";
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *imgData = [appDelegate addUserPic:contact];
if (imgData == nil && netStatus == ReachableViaWiFi) {
NSString *url = [NSString stringWithFormat:#"http://somehost.com/userpic/%#", contact];
imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
}
dispatch_async(dispatch_get_main_queue(), ^{
UITableViewCell *updateCell = [self.tableView cellForRowAtIndexPath:indexPath];
if (updateCell) {
if (imgData) {
[appDelegate setUserPic:contact imgData:imgData];
updateCell.imageView.image = [UIImage imageWithData:imgData];
} else {
updateCell.imageView.image = nil;
}
/* This forces the cell to show image as now
it has normal bounds */
[updateCell setNeedsLayout];
}
});
});
return cell;
}
I have a dictionary of arrays, and each of these arrays has a number of arrays itself. Its a multidimensional array in a dictionary.
When I click on a UItable row, I push the view to another class, however I want to push along with the view, the array whose object key is the same as the row name that was clicked.
My code is as follows
(The class that does the pushing)
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic -- create and push a new view controller
if(dvController == nil)
dvController = [self.storyboard instantiateViewControllerWithIdentifier:#"FoodCategoryView"];
//NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
//NSMutableDictionary *keysAndObjects = [defaults objectForKey:#"keysAndObjects"];
Food *foodObj;
/*for (id key in [keysAndObjects allKeys]) {
NSArray *foodArray = [keysAndObjects objectForKey:key];
foodObj = [foodArray objectAtIndex:indexPath.row];
}*/
/*for (id key in [keysAndObjects allKeys]) {
NSArray *foodArray = [keysAndObjects objectForKey:key];
for (Food *food in foodArray) {
// do stuff with Food object
//NSLog(#"%#", food.foodName);
}
}*/
NSMutableArray *objects2 = [[NSMutableArray alloc] init];
NSMutableArray *objects1 = [[NSMutableArray alloc] init];
objects1 = [objects objectAtIndex:indexPath.row];
objects2 = [objects1 objectAtIndex:indexPath.row];
foodObj = objects2;
//NSLog(#"Object %#", [objects objectAtIndex:indexPath.row]);
NSLog(#"foodObj %#", foodObj);
//Get the detail view data if it does not exists.
//We only load the data we initially want and keep on loading as we need.
//[foodObj hydrateDetailViewData];
dvController.foodObj = foodObj;
[self.navigationController pushViewController:dvController animated:YES];
}
(the class that the view is pushed to)
- (UITableViewCell *)tableView:(UITableView *)tblView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier];
}
/*for (Food *food in foodObj) {
// do stuff with Food object
NSLog(#"%#", food.foodName);
cell.text = food.foodName;
}*/
//Food *food = [foodObj objectAtIndex:indexPath.row];
//cell.text = [foodObj objectAtIndex:indexPath.row];
cell.text = foodObj.foodName;
NSLog(#"%#", foodObj.foodName);
return cell;
}
As you can see there are lots of comments as I have tried lots of different ways to do it myself.
I was trying to parse a JSON string from USGS. However I get error "-[__NSCFDictionary objectAtIndex:]: unrecognized selector sent to instance 0x68a34c0" Basically, I would like to parse the USGS JSON into the tableview. Can anyone help? Here are my codes.
ViewController.h
#interface EarthquakeViewController : UITableViewController
{
NSArray *json;
NSDictionary *earthquakeReport;
}
ViewController.m
- (void)viewDidLoad
{
//content = [NSMutableArray arrayWithObjects:#"one", #"two", nil];
[super viewDidLoad];
[self fetchReports];
}
- (void)fetchReports
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData* data = [NSData dataWithContentsOfURL:
[NSURL URLWithString: #"http://earthquake.usgs.gov/earthquakes/feed/geojson/all/hour"]];
NSError* error;
json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
NSLog(#"%#", json);
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
});
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return json.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
earthquakeReport = [json objectAtIndex:indexPath.row];
NSString *country = [[[earthquakeReport objectForKey:#"features"] objectForKey:#"properties"] objectForKey:#"place"];
NSString *mag = [[[earthquakeReport objectForKey:#"features"] objectForKey:#"properties"] objectForKey:#"mag"];
cell.textLabel.text = country;
cell.detailTextLabel.text = mag;
return cell;
}
The error is showing at the line earthquakeReport = [json objectAtIndex:indexPath.row];
If you look at your JSON data in a web browser (http://earthquake.usgs.gov/earthquakes/feed/geojson/all/hour) you should notice that the array you are trying to access is not at the root level. The root level is a dictionary, and the array you're looking for is in the "features" key.
To access it properly, first change your json ivar declaration into an NSDictionary:
NSDictionary *json;
Then, in your tableView:cellForRowAtIndexPath: method, access the report thusly:
earthquakeReport = [[json objectForKey:#"features"] objectAtIndex:indexPath.row];
This is my sample twitter program it works but there are several errors
if I scroll up or down top and bottom cellls disappears some times I get an error saying :
BAD ACCESS CODE this happens on this row
NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
Please Advice
#import "TableViewViewController.h"
#interface TableViewViewController ()
#end
#implementation TableViewViewController
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"tweets array count : %d", tweets.count);
return tweets.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"TweetCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSLog(#"ROW : %d", indexPath.row);
NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
NSString *text = [tweet objectForKey:#"text"];
NSString *name = [[tweet objectForKey:#"user"] objectForKey:#"name"];
cell.textLabel.text = text;
cell.detailTextLabel.text = [NSString stringWithFormat:#"by %#", name];
return cell;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self fetchTweets];
}
- (void)fetchTweets
{
NSString *twitterURL = [NSString stringWithFormat:#"https://api.twitter.com/1/statuses/public_timeline.json"];
NSURL *fullURL = [NSURL URLWithString:twitterURL];
NSError *error = nil;
NSData *dataURL = [NSData dataWithContentsOfURL:fullURL options:0 error:&error];
tweets = [NSJSONSerialization JSONObjectWithData:dataURL
options:kNilOptions
error:&error];
}
.....
#end
I see problems in this function:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"tweets array count : %d", tweets.count);
//return tweets.count;
return 11; //default returns 20 but only shows 10 indexpath.Row so 11
}
You probably shouldn't be just returning 11 -- what if tweets has a different length? The table view will try to fetch a cell at an index which doesn't exist in your array. Try returning tweets.count instead.
To elaborate a bit further: The purpose of the tableView:numberOfRowsInSection: method isn't to tell iOS how many rows there are on the screen, it's how many rows there are in the entire tableView. This includes cells which aren't shown on screen.
Found out the answer My app doesn't support ARC because I didn't check if we use a retain at the NSJSONSerialization the error is fixed.
tweets = [[NSJSONSerialization JSONObjectWithData:dataURL
options:kNilOptions
error:&error] retain];
Why is numberOfRowsInSection hardcoded to 11?
Also you should have [self.tableView reloadData] after the tweets array is set up in fetchTweets
Why don't you just uncomment return tweets.count; in your numberOfRowsInSection method?
I have a UITableView which I want to populate with details from an array of objects. The tableview shows the same item on every line (the correct number of lines though!) I know this must be an easy one - but I can't see where I've gone wrong:
Code snippet of view that initializes the table data:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"Show Tank List"])
{
NSURL *myUrl = [[NSURL alloc]initWithString:#"http://localhost/~stephen-hill9/index.php"];
NSData *data = [[NSData alloc] initWithContentsOfURL:myUrl];
NSError *error;
NSArray *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
int i;
NSMutableArray *tanksList;
tank *thisTank = [[tank alloc] init];
tanksList = [[NSMutableArray alloc] init];
for (i=0; i<json.count; i++) {
NSDictionary *bodyDictionary = [json objectAtIndex:i];
thisTank.tankNumber = [bodyDictionary objectForKey:#"ID"];
thisTank.tankProduct = [bodyDictionary objectForKey:#"Product_Desc"];
thisTank.tankPumpableVolume = [bodyDictionary objectForKey:#"Pumpable"];
[tanksList addObject:thisTank];
}
[segue.destinationViewController setTanks:tanksList];
}
}
...and the code that loads the table in the next view...
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;//keep this section in case we do need to add sections in the future.
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [self.tanks count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Tank List Table Cell";
UITableViewCell *cell = [self.tankTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell)
{
cell = [[UITableViewCell alloc] initWithFrame:CGRectZero];
}
tank *thisTank = [self.tanks objectAtIndex:indexPath.row];
cell.textLabel.text = thisTank.tankNumber;
return cell;
}
Move this:
tank *thisTank = [[tank alloc] init];
Inside your for loop. You're updating the same object over and over again.
Also, you're initialising the cell wrong - use the designated initialiser, and pass the reuse identifier in, otherwise you will create new cells all the time:
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
And you really should follow objective-c naming conventions. Classes begin with upper case letters, everything else begins with a lower case letter. It makes your code much easier to read, for other people anyway.
reload the table every time!!!
[self.tableView reloadData];