When scrolling in my UITableView cells are disappearing - objective-c

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?

Related

Displaying data retrieved from Parse in UITableView

After all progress i made with your answers, my issue changed. So i am changing my question with clearer way. I have an UITableView which is showing my retrieved data from Parse.com. So i made a NSMutableArray for adding objects to that array when they are retrieved. But my problem is even i add objects to NSMutableArray, my table does not show anything but default screen of UITableView. I thing the issue is UITableView is formed before my NSMutableArray got its objects. Here is my code:
Note: The PropertyClass is the class which has the properties of my objects.
At MyTableViewController.h
#interface MyTableViewController : UITableViewController <CLLocationManagerDelegate> {
PFObject *object;
}
#property (strong, nonatomic) IBOutlet UITableView *MyTableView;
#end
At UITableViewController.m
#interface MyTableViewController ()
#property(strong)NSMutableArray *myNSMutableArray;
#end
#implementation MyTableViewController
#synthesize myNSMutableArray,MyTableView;
-(void) retrievingDataFromParse{
PFQuery *query = [PFQuery queryWithClassName:#"MyObjectsClass"];
[query whereKey:#"ObjectsNumber" lessThanOrEqualTo:10];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d scores.", objects.count);
if (objects.count==0) {
NSString *objectError = #"There no object retrieved from Parse";
PropertiesClass *PC = [[PropertiesClass alloc]initWithPropert1:objectError Propert2:nil Propert3:nil Propert4:nil];
[myNSMutableArray addObject:PC];
}
for (int i = 0; i < objects.count; i++) {
object = [objects objectAtIndex:i];
NSString *Propert1 = [object objectForKey:#"Propert1"];
NSNumber *Propert2 = [object objectForKey:#"Propert2"];
NSNumber *Propert3 = [object objectForKey:#"Propert3"];
NSString *Propert4 = [object objectForKey:#"Propert4"];
PropertiesClass *PC = [[PropertiesClass alloc]initWithPropert1:Propert1 Propert2:Propert2 Propert3:Propert3 Propert4:Propert4];
[myNSMutableArray addObject:PC];
};
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.myNSMutableArray = [NSMutableArray array];
[self retrievingDataFromParse];
[MyTableView 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 [myNSMutableArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
PropertiesClass *PC= [myNSMutableArray objectAtIndex:indexPath.row];
cell.textLabel.text=PC.Propert1;
return cell;
}
Looking at your code i see that you never create a UITableViewCell, you should change this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
PropertyClass *PC = [myMutableArray objectAtIndex:indexPath.row];
cell.textLabel.text = PC.x;
return cell;
}
with this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (nil == cell){
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
PropertyClass *PC = [myMutableArray objectAtIndex:indexPath.row];
cell.textLabel.text = PC.x;
return cell;
}
the method dequeueReusableCellWithIdentifier:forIndexPath: return a UITableViewCell only if there are unused, but already allocated, cells in your table view. otherwise it returns nil.
Also when you update the mutable array containing all your data you should call [yourTableView reloadData] to force the table view to reload its content.
Your code is quite cryptic. Few suggestions here.
First, rename variables and methods with camelCaseNotation (camel case notation). For example, MyMutableArray should be myMutableArray. RetrievingDataFromParse should be retrievingDataFromParse (and so on). Start upper case letter are for classes.
Second, what does this code mean (I put comment on your code)?
for (int i = 0; i < objects.count; i++) {
// where do you have defined object?
object = [objects objectAtIndex:i];
NSString *x = [object objectForKey:#"x"];
NSNumber *y = [object objectForKey:#"y"];
NSNumber *z = [object objectForKey:#"z"];
NSString *t = [object objectForKey:#"t"];
// is Mekan a subclass of PropertiyClass or what else?
PropertiyClass *Properties = [[Mekan alloc]initWithx:x y:y z:z t:t]
// what's MekanKalibi? Maybe you need to add Properties
[MyMutableArray addObject:MekanKalibi];
}
Edit
If you don't use iOS6 - (void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifier you should alloc-init cells.
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if(!cell) {
// alloc-init a new cell here...
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
// or if you don't use ARC
// cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
PropertyClass *PC = [myMutableArray objectAtIndex:indexPath.row];
cell.textLabel.text = PC.x;
return cell;
Edit 2
I don't know how parse works but I suppose it manages async requests. So, at the end of your for loop, just call reload data in the table.
Parse states:
The InBackground methods are asynchronous, so any code after this will run immediately. Any code that depends on the query result should be moved inside the completion block above.
I had the same problem. When you reload the table, you need to move it so it is inside the block. Worked for me.
I'm not 100% sure how the asynchronous parts affect it so. I know that the start of my viewDidload and the end occured then this block, hence the problem.
People should probably up this as this solves the issue.
Cheers.
All you have to do is reload tableView in the block... this will show data.
for (int i = 0; i < objects.count; i++) {
object = [objects objectAtIndex:i];
NSString *Propert1 = [object objectForKey:#"Propert1"];
NSNumber *Propert2 = [object objectForKey:#"Propert2"];
NSNumber *Propert3 = [object objectForKey:#"Propert3"];
NSString *Propert4 = [object objectForKey:#"Propert4"];
PropertiesClass *PC = [[PropertiesClass alloc]initWithPropert1:Propert1 Propert2:Propert2 Propert3:Propert3 Propert4:Propert4];
[myNSMutableArray addObject:PC];
};
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
**[MyTableView reloadData];**
}];

iOS 5 JSON to tableView error

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];

Custom cell not showing up in tableView

I have a custom cell with identifyer 'tweetCell' I have imported this into my tableviewcontroller.h file. I have linked the class to the prototype cell in storyboard. All the UILabels are wired up to the cell but I cannot get the tableview to display the custom cell. It did work? Then overnight stopped?!! I understand that class files need to start with an upper case letter, I have changed this but to no avail. Can anyone spot my error here? Thanks in advance......
- (void)fetchTweets
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData* data = [NSData dataWithContentsOfURL:
[NSURL URLWithString: #"http://search.twitter.com/search.json?q=%23mysearchhashtag"]];
NSError* error;
tweets = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:&error];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
});
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return tweets.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"tweetCell";
customCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[customCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
NSString *tweetText = [tweet valueForKey:#"text"];
NSArray *tweetComponents = [tweetText componentsSeparatedByString:#":"];
cell.firstDetail.text = [tweetComponents objectAtIndex:0];
cell.secondDetail.text = [tweetComponents objectAtIndex:1];
cell.thirdDetail.text = [tweetComponents objectAtIndex:2];
return cell;
}
Did you write this method - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView? Try like this.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}

NSMutableDictionary and NSArray

I am splitting a String:
#"Sam|26|Developer,Hannah|22|Team Leader,Max|1|Dog"
and using NSMutableDictionary to display in a TableViewCell with 3 labels. Code:
- (void)viewDidLoad {
[super viewDidLoad];
NSString *test = #"Sam Parrish|26|Developer,Hannah Rajamets|22|Team Leader,Max Parrish|1|Dog";
testArray = [[NSArray alloc] init];
testArray = [test componentsSeparatedByString:#","];
dict = [NSMutableDictionary dictionary];
for (NSString *s in testArray) {
testArrayNew = [s componentsSeparatedByString:#"|"];
[dict setObject:[testArrayNew objectAtIndex:1] forKey:[testArrayNew objectAtIndex:0]];
[dict setObject:[testArrayNew objectAtIndex:2] forKey:[testArrayNew objectAtIndex:1]];
NSLog(#"Dictionary: %#", [dict description]);
}
[dict retain];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[dict allKeys] count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CustomCell";
untitled *cell = (untitled *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"untitled" owner:nil options:nil];
for (id currentObject in topLevelObjects) {
if ([currentObject isKindOfClass:[UITableViewCell class]]) {
cell = (untitled *) currentObject;
break;
}
}
}
// Configure the cell.
cell.nameLabel.text = [[dict allKeys] objectAtIndex:[indexPath row]];
cell.ageLabel.text = [dict objectForKey:cell.nameLabel.text];
cell.jobLabel.text = [dict objectForKey:cell.ageLabel.text];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
this is displaying 6 TableViewCells when I run, 3 of which are perfect, the other 3 are an assortment of the data. i realise this is to do with the setObject: forKey: but can't seem to find the solution for this to work correctly.
any help much appreciated..
sam
I would store the data as an NSArray holding NSArrays....
- (void)viewDidLoad {
[super viewDidLoad];
NSString *test = #"Sam Parrish|26|Developer,Hannah Rajamets|22|Team Leader,Max Parrish|1|Dog";
data = [[NSMutableArray alloc] init];
for (NSString *s in testArray) {
[data addObject:[s componentsSeparatedByString:#"|"]];
}
}
Then in
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
use
// Configure the cell.
NSArray *cellData = [data objectAtIndex:indexPath.row];
cell.nameLabel.text = [cellData objectAtIndex:0];
cell.ageLabel.text = [cellData objectAtIndex:1];
cell.jobLabel.text = [cellData objectAtIndex:2];
If I understand you correctly, you're saying that the first three cells are correct and the other three shouldn't be shown at all? If that's the case, the problem is this:
return [[dict allKeys] count];
Shouldn't it be...
return [testArray count];
...in which case, you need to retain testArray.
In addition, you have a memory leak. You create an instance of NSArray, then you immediately leak it by assigning the return value of componentsSeparatedByString: to the same variable. Have you run the static analyser over your code?

Cocoa Touch UITableView Data

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;
}