UITableView showing (null) for first row - objective-c

I am updating a UITableView with CoreData. On startup, the first row is showing as (null). If I scroll down/up, it loads though. It just doesnt show initially.
- (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];
[cell setAccessoryType: UITableViewCellAccessoryDisclosureIndicator];
}
// set text for each cell
[cell.textLabel setText: [dataManager getProjectValueForKey: #"Title" atIndex: [indexPath row]]];
[cell.detailTextLabel setText: [[[dataManager getProjectValueForKey: #"pid" atIndex:[indexPath row]] stringByAppendingString: #": "] stringByAppendingString: [dataManager getProjectValueForKey: #"Sponsor" atIndex:[indexPath row]]]];
return cell;
}
dataManager is what is talking to Core Data. I feel like it might be lagging behind or something on startup so the cell is trying to show the data before it is ready. But I don't know.

Actually, there is a great "dataManager" already built into Core Data. It is called NSFetchedResultsController. You can always retrieve the correct data object for your table cell with
[[self.fetchedResultsController fetchedObjects] objectAtIndex:indexPath.row];
This is typically done inside tableView:cellForRowAtIndexPath:, and not in some other helper function. The fetched results controller will take care of everything that is necessary to retrieve the data that is needed in order to display it.
See also the numerous Core Data code examples from Apple for this common design pattern.

Related

Reloading searchResultsTableView

I have, in my main tableView, some custom cells (cells with an imageView, basically).
The imageView is set only if a value on my plist is false. Then, when it's true, the imageView is nil.
Basically when the user enters the detailView, the value is set to YES and the cell.imageView is nil.
And it's okay, it works
I'm using a searchDisplayController, when i search for something that has a cell.imageView, going into the detailView and then coming back to the searchResultsTable, the cell has still the image, while it shouldn't, but the main tableView has the correct cell (so, with no image).
I thought that it could depend on searchResultsTableView, but i'm not sure.
I tried with
[self.searchDisplayController.searchResultsTableView reloadData];
with no effect.
How could i reload the searchResultsTableView so that it shows the right cells, those with the image and those that don't have the image anymore?
Any help appreciated!
EDIT
This is my cellForRowAtIndexPath method:
- (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];
}
NSArray *rows;
if (tableView == self.searchDisplayController.searchResultsTableView) {
rows = filteredList; //for search
} else {
NSDictionary *section = [localSortedTips objectAtIndex:indexPath.section];
rows = [section objectForKey:#"Rows"];
}
NSDictionary *item = [rows objectAtIndex:indexPath.row];
cell.textLabel.text = [item objectForKey:#"name"];
if ([[item valueForKey:#"isRead"] boolValue] == NO) {
cell.imageView.image = [UIImage imageNamed:#"unread.png"];
} else {
cell.imageView.image = nil;
}
cell.textLabel.font = [UIFont boldSystemFontOfSize:15.0];
cell.textLabel.adjustsFontSizeToFitWidth = YES;
return cell;
}
If I understood you right, then you can have a workaround but searching again with the same search string:
if (self.searchDisplayController.active) {
self.searchDisplayController.searchBar.text = self.searchDisplayController.searchBar.text;
}
put it in viewWillAppear: or viewDidAppear: which will be called each time the view is shown up (eg. you go back from the detail view to your search view). And reloading the data in this place would be nice too, to get the right data (for example if you marked the cell as read like in your sample code)
Just [self.tableView reloadData]; and not the searchResultsTableView (it will be automatically use the updated data after the new search)
It sounds as if perhaps your cells are being recycled and not reset properly. UITableView uses view recycling, so it's important that if you do something such as set an image you make sure it is explicitly set even when their isn't an image to display.
If you share your cellsForRowAtIndexPath code you might be able to get some more help.

Why is my UITableView not being updated?

Why does my UITableView not update? Here is how I am attempting to update it.
- (void)viewWillAppear:(BOOL)animated
{
NSArray* arrValues = [self.defaults objectForKey:#"values"];
[self.tableScores insertRowsAtIndexPaths:arrValues withRowAnimation:UITableViewRowAnimationNone];
}
arrValues is now an array of NSNumbers. I am sure that it is not empty.
Call [tableScores reloadData]; in - (void)viewWillAppear:(BOOL)animated
Update 1
Also, you need to define arrValues in your header. Each time the viewWillAppear, you are creating a new instance, but you won't be able to use it throughout the rest of your controller. This is the main reason you aren't seeing anything besides at your breakpoint.
Update 2
According to your comment below, you have not implemented cellForRowAtIndexPath: which is how the cell is created. Below is an example, but you may want to search around the net for example projects because this UITableView's 101. There is still a lot more you need to learn when it comes to arrays and tableViews.
Example cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"FriendCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [arrValues objectAtIndex:indexPath.row];
return cell;
}

issue customizing UITableViewCell

I'm using the following code to just make a single UILabel on my cell. (I know this example is contrived; I can't even get this to work much less my ultimate design goal.)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Photo";
UILabel *username;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
username = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, 120.0, 20.0)];
username.tag = USERNAME_TAG;
username.font = [UIFont systemFontOfSize:14.0];
username.textAlignment = UITextAlignmentLeft;
username.textColor = [UIColor blackColor];
username.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
[cell.contentView addSubview:username];
} else {
username = (UILabel *)[cell.contentView viewWithTag:USERNAME_TAG];
}
//NSDictionary *photos = [self.photos objectAtIndex:indexPath.row];
username.text = #"Testing!";
return cell;
}
I got this template code from here
However, when I run the code, there is no text label in the cell, but the accessory does show up.
I find it much easier to use a custom xib for table cells that don't lend themselves easily to one of the normal types. The basic steps are: create a xib with your row, create an outlet property, then in your cellForRowAtIndexPath method, call loadNibNamed and assign it to a local variable (that you return), and set the outlet back to nil.
The secret is that loading a nib with a nil outlet causes the nil outlet to be init'd and inflated with the contents of the nib.
Described here (scroll to Loading Custom Table-View Cells From Nib Files):
http://developer.apple.com/library/ios/#documentation/UserExperience/Conceptual/TableView_iPhone/TableViewCells/TableViewCells.html#//apple_ref/doc/uid/TP40007451-CH7
If you need a lot of user interaction, you are probably better off sub-classing UITableViewCell and supplying it with its own xib. If you have a lot of controls on a cell, you will end up using tags to determine the source of events, which gets messy with more than a few tags.
Apple changed the behavior of dequeueReusableCellWithIdentifier when they released 5.0.
In 5.0+ it is guaranteed to always return a copy of your cell (if it exists in your xib/storyboard), even if you haven't even loaded a cell yet. Prior to this, it would return a cell only if there was one that was ready to be recycled.
Because of this, some older code which assumes that you will receive nil unless you have already initialized it will break.
So, get rid of the identifier (or the entire cell) in your xib/storyboard or modify your logic so that it initializes it when needed (perhaps based on whether or not there is a view with the specified tag).
It looks like you're just trying to add a "standard" UILabel to the UITableViewCell.
Here's what you do: every UITableViewCell of the UITableViewCellStyleDefault has a property called textLabel. In your code, do the following:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Photo";
UILabel *username;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
}
cell.textLabel.text = #"Testing!";
return cell;
}
PS: Looks like you're trying to add an image at some point? There's a property for that as well. Take a look at the documentation for the imageView property.

UITableView not selecting properly

I am working on an iPhone app, where I have a UITableView which is being populated with an XML feed through a URL.
Say for instance three of the cells are populated.
If I tap on the first cell nothing happens, however if I tap on the second or third cell, it takes me to the second screen related to cell one, and the same happens with the other cells - tap on it nothing, tap on another and it takes me to the second screen of the previous one selected.
I have never had this happen before and am rather confused.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"UITableViewCell"];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:#"UITableViewCell"];
}
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
LocationsItem *atm = [[locations atms] objectAtIndex:[indexPath row]];
[[cell textLabel] setText:[atm atmName]];
float distance = [[atm atmDistance] floatValue];
NSString *distanceString = [NSString stringWithFormat:#"%0.2f miles from current location", distance];
[[cell detailTextLabel] setText:distanceString];
return cell;
}
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
LocationsItem *atm = [[locations atms] objectAtIndex:[indexPath row]];
ATMDetailsController *detailsController = [[ATMDetailsController alloc] init];
[detailsController setCurrentATM: atm];
[[self navigationController] pushViewController:detailsController animated:YES];
}
Thanks,
Nick
You answered your own question. The issue is that you used tableView:deselectRowAtIndexPath: instead of tableView:didSelectRowAtIndexPath:
What is noteworthy is that this is from the unfortunate fact that deselect comes before did in the dictionary, and therefore, xcode's normally awesome code completion hinks you!
Now to go get those hours of debugging back!

how is the function called in xcode

im new to xcode and i am doing this code to fill up the table view with annotation titles but the function gets called more than once and the table cells are filled with all repeated values , how is the function called in xcode , how can i stop this function from getting called more than once
- (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];
}
NSLog(#"this is a test text ");
NSMutableArray *annotations = [[NSMutableArray alloc] init];
int i=0;
if(indexPath.section == 0)
{
for(iCodeBlogAnnotation *annotation in [map annotations])
{
i++;
NSLog(#"this is the no %d",i);
[annotations addObject:annotation];
}
cell.textLabel.text = [[annotations objectAtIndex:indexPath.row] title];
}
return cell;
}
Any help would be deeply appreciated ,
Thank you for your help in advance
You can't really control when it's called. It's called each time your tableview wants to display a new cell. You use the indexPath to determine what to put in that cell. It's called at least once per cell that's on the screen (sometimes more if the table is scrolled up and down).
You don't need to create the temporary array each time this function is called, just use [map annotations] directly :
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// There will be one row per annotation
return [[map annotations] 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] autorelease];
}
// Put the text from this annotation into this cell
cell.textLabel.text = [[[map annotations] objectAtIndex:indexPath.row] title];
return cell;
}
I hope I've understood your question. If not, please tell me in the comments below!
It isn't a function, it is a method.
It is called by a table view as the table view draws cells. It will be called once per cell and, sometimes, more than once per cell depending on what the user is doing.
You don't push data into a table view, it asks you for cell contents.
Asking "how can i stop this function from getting called more than once?" indicates that you don't understand table views (it is confusing if you've come from the "push" model of UI programming). Start with the TableView programming guide.
The function is called whenever the UITableView does not already have a UITableViewCell for a particular index path and needs one. Note that it may be called multiple times for an index path, as a result of user scrolling (to save memory, cells that are offscreen may be reused or released) or calls to reloadData and related functions or insertRowsAtIndexPaths:withRowAnimation: and related functions. You cannot (and really do not want to) prevent it from being called more than once.
That said, assuming that [map annotations] returns an ordered collection of some sort that is ordered the same way each time, your code should be doing what you want (even though it is very inefficient). More detail on the problem would be helpful.