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.
Related
I'm trying to have a dynamic table which expands on click to reveal some other content. I've got to the point where the table is populated with information from an NSMutableArray. I can also press each cell and it expands to double the size. Now, the next step, which is proving to be somewhat troublesome is to get it to display new/alternative text when they click on the cell. First of all, here's my setup 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];
NSString *cellValue = [cellContent objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
}
return cell;
}
and after this, I have the method where the cell expands on press:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
// If our cell is selected, return double height
if([self cellIsSelected:indexPath]) {
return kCellHeight * 2.0;
[cellContent replaceObjectAtIndex:[self cellIsSelected:indexPath] withObject:#"NEW STUFF HERE"];
}
I must be going about this in the wrong way as nothing changes when I touch the cell. How do I get it to display new/alternative text on touch? Any help would be super, I think it's likely to be something quite easy, but I can't see it at the moment.
Thanks!
Your second method is only triggered when the cell is built or rebuilt. You need to explicitly ask it to refresh itself in the following method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
And then within that method, you can call
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
and now your redraw methods will be triggered, so you can handle whatever you want there.
How do I read intValue of an array of NSIndexpaths through indexPathsForVisibleRows?
By the way, why do visibleCells and indexPathsForVisibleRows not work before if (cell == nil) function?
Here is my code:
In the cellForRowAtIndexPath method:
static NSString *identifierString;
UITableViewCell *cell = [tableView1 dequeueReusableCellWithIdentifier:identifierString];
// when I use visibleCells and indexPathsForVisibleRows here, the app crashes
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifierString] autorelease];
cell.accessoryType = UITableViewCellAccessoryNone;
}
// when I use visibleCells and indexPathsForVisibleRows here, the app works
//cell implementation here
return cell;
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath is where the tableview gets populated with cells. If you try to reference visible cells before they have been created, which is done within the if-statement, then the app crashes.
The alloc command allocated memory for the cell to be created and then it gets initialized with certain parameters. This method gets called as many times as you designate in numberOfRowsInSection.
So that you don't recreate all the cells again and again the if-statement checks if the cell does exist and only if it is nil creates a new one to take the place.
To get the int row value of an IndexPath you can use it's row property. For example:
NSArray indexArray = [self.tableView indexPathsForVisibleRows];
int i=0;
while(i!=indexArray.count){
//Log out the int value for the row
NSLog(#"%d", indexArray[i].row);
i++;
}
Hope this helps
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;
}
It seems to work much faster to do this:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
JHomeViewCell *cell = (JHomeViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[JHomeViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
cell.cellContent.thumbnailCache = self.thumbnailCache;
}
Entry *entry = [self.resultsController objectAtIndexPath:indexPath];
if (cell.cellContent.entry != entry) {
[cell.cellContent setNeedsDisplay];
}
}
The problem is when an entry is edited, the cell doesn't change. I could check for all elements of the cell, to see if they're different, but is there a better way of doing this? Calling drawrect every time the cell appears slows the app down and seems unnecessary.
If you want to do custom drawing for your tableview cell you have to subclass UITableViewCell (as it looks you have done with JHomeViewCell).
Put drawRect inside of your implementation of JHomeViewCell
#implementation JHomeviewCell
- (void)drawRect:(CGRect)rect
{
[super drawRect];
// Insert drawing code here
}
#end
Also, you shouldn't call drawRect directly. You should call setNeedsDisplay instead, and probably need to set the cellContent.entry value to the entry you have from your results controller.
Entry *entry = [self.resultsController objectAtIndexPath:indexPath];
if (cell.cellContent.entry != entry) {
cell.cellContent.entry = entry;
[cell setNeedsDisplay];
}
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.