UITableView - cellForRowAtIndexPath takes more than half a second - ios

I've built a UITableView with reusable cells. At some point in my code I need access to a cell at a certain indexPath, so I call
MyCell *cell = (MyCell *) [tableView cellForRowAtIndexPath:indexPath];
Sometimes (usually the first time) this is called, it may take from 0.5 to 1 second.
What can cause this and how can I preven this?
I checked if
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
gets called as a reaction to cellForRowAtIndexpath: but it doesn't.
EDIT:
I just did another test and it also takes long if I call
[tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionMiddle];
Whatever gets called first, does have a lag of half a second

call [tableView reloadData] first, that may force the tableview to load its data. but still, if a cell is not visible on screen, it might not be loaded into memory.

Related

didDeselectRowAtIndexPath issue

I have a tableview in a scrollview in a popover. When the view is presented, the bottom cell in tableview is not visible to the user. If I select all of the cells then deselect the fist cell, the out of view cell is deselected too. Has anyone come across this behaviour before? If so, how to approach it?
Now your job is to find all the visible cells in the tableview and then apply select/deselect to it.
UITableView *tableView = self.tableView;
// Or however you get your table view
NSArray *paths = [tableView indexPathsForVisibleRows];
// For getting the cells themselves
NSMutableSet *visibleCells = [[NSMutableSet alloc] init];
for (NSIndexPath *path in paths)
{
[visibleCells addObject:[tableView cellForRowAtIndexPath:path]];
}
// Now visibleCells contains all of the cells you care about.
-(void) tableView:(UITableView *)tableView didDeselectRowAtIndexPath:
(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:
(NSIndexPath *)indexPath {
//stuff
//as last line:
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
For that matter, deselectRowAtIndexPath can be called from anywhere at any time you want the row to be deselected.
[self.myTableView deselectRowAtIndexPath:[self.myTableView
indexPathForSelectedRow] animated: YES];
If you are using dequeueReusableCellWithIdentifier: change your cellForRowAtIndexPath: to use dequeueReusableCellWithIdentifier:forIndexPath:
In a UITableView cells get reused. That means it only produces as many as absolutely needed. As soon as a new one is coming onto the screen, the last one is "recycled" instead of initialising a whole new instance.
This makes your application run faster. It also means that you have to undo any changes you made, when recycling.
Selection status is one of them. The UITableView should manage this automatically for you, if it is dequeued with the relevant indexPath. If not, it wouldn't know whether that specific cell should be selected.

Why is awakeFromNib called twice from a Cell in a TableView?

I'm trying to understand why awakeFromNib is being called twice in my code. I currently have a tableview that has a special compressible cell that appears once at the end of the table. The first awakeFromNib is being called when the tableview is scrolled to the special cell at the end (which is fine I believe,as the tableview is reusing cells). However, whenever I tap the cell to expand the cell, the awakeFromNib is being called again.
Could anyone explain to me why awakeFromNib is being called twice? And how I could only make it only be called once?
Thanks
EDIT** Code people have requested
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section >= (NSInteger)[self.trip.destinations count]) {
GuestCell *cell = (GuestCell*)[tableView dequeueReusableCellWithIdentifier:GuestCellIdentifier forIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
[cell setupCellForGuests:self.trip.guests];
cell.guestExpanded = NO;
NSLog(#"RETURNING CELL");
return cell;
}
// For all other sections
return [self prepareCardCellForIndexPath:indexPath forHeightCalc:NO];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section >= (NSInteger)[self.trip.destinations count]) {
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
You're animating the reload of the expanding row. The table view implements this by creating another cell for the same index path, and animating a transition from the old cell to the new cell. It creates a second instance of your cell prototype, so the second instance also receives the awakeFromNib message. If you log self, you'll see that the address is different the second time.
I don't think you can avoid the creation of a second cell instance (and thus a second awakeFromNib) unless you get rid of the animation. Even then I'm not sure it will reuse the old cell.
If the cell with that nib is only one in the table, then my guess is that it has something to do with animations. I didn't check how tableview handles cells during animation, but for tableview header it asks for another instance and then performs animation (for example fade) - so the old instance is faded out and the new is faded in. At least that's what I think has the highest probability, if you are handling cells correctly.

IOS-TableView Cell: dequeueReusableCellWithIdentifier issue

In my project, I create everything by code, like tableviews and cells. I have function to add cells.
The situation is: I have 10 object in my array, when program runs, the function (- (TodoTableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath) runs through all 10 cells as I scroll, it does run 10 times of init... function in cell class. BUT, when I add a new cell in the program, then I found that this new cell never calls the init function for some reason. (The cell is being successfully created though... weird)
I use this part to add cell:
[self.tableView beginUpdates];
[self.todoItemList insertObject:newItem atIndex:toBeAddedIndex];
[self.tableView insertSections: [NSIndexSet indexSetWithIndex:toBeAddedIndex] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
and in this function:
- (TodoTableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"Scroll to %ld", indexPath.section);
static NSString *cellIdentifier = #"TodoCell";
TodoTableViewCell *cell = (TodoTableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
//If the current cell is dummycell, make it transparent
cell.todoItem = (TodoItem *)(self.todoItemList[indexPath.section]);
I don't know why, anyone please kindly help
----------UPDATE-----------------
NSString *cellIdentifier = [NSString stringWithFormat:#"TodoCell%ld",indexPath.section];
I found that if I change to this line. every time a add a new cell, it just calls the init function! but... if I do delete first and then add, problem still there, because this identifier has been created and used and stored I guess.
try to call:
[self.tableView insertRowsAtIndexPaths:<#(NSArray *)#> withRowAnimation:<#(UITableViewRowAnimation)#>]

Is it possible to create UITableViewCells inside a for/in loop?

I'm calling a function in which i retrieve a NSDictionary with data that needs to be displayed in tableviewCells. Is it possible to call the method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
in another method?
It is not possible to call that one specific method inside another method. However, you can call [self.tableView reloadData] inside any methods. This call all the UITableView delegate methods which includes - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
You can explicitly call any methods of UITableViewDataSource and UITableViewDelegate, but should only be done in cases of dire need. In a project of mine, I get specific (subclassed) cells which draw elements not in the drawRect: method, but in a drawCell method I have defined, for performance gains (preventing offscreen rendering, etc).
Unless your cells have special drawing needs, (resizing subviews, etc) you should let the cellForRowAtIndexPath: method do this work for you.
Still, if you want to explicitly get cells, you may do with:
[yourTableView cellForRowAtIndexPath:indexPath];
This will return you your cell. Mind you, it will execute all relevant code inside the cellForRowAtIndexPath: including dequeueing, creation, and any other calls you have made in it.
indexPath passed here is an object of type NSIndexPath, and you may create one like this:
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:ROW_NUM inSection:SECTION_NUM];
Where, ROW_NUM is the is the row number inside the section, which is SECTION_NUM. For the first cell in the third section, the indexPath is:
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:0 inSection:2];//Since indices start at 0

no visible #interface for "UITableView" declares the selector "cellForRowAtIndexPath:"

I am new in IOS6 dev. I got a problem with UITableView. My code below is to display Check Mark at the end of the row selected. But I received an error like "no visible #interface for UITableView declares the selector cellForRowAtIndexPath:". UITableView has cellForRowAtIndexPath method, but tableView cannot
show it. I don't know why. Please help.
Below is the code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; -----error line
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
The problem is "tableView" cannot recognize all the methods under UITableView. Some it knows such as "numberOfRowsInSection". I cannot figure out why.
The TableView itself does not implement this selector.
The full selector for this method is
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
and is from the protocol for the delegate. Your delegate (e.g. the viewController) has to implement this method. It is not recommended (and not easily possible) to get the cell object from the table.
Instead, change the underlying data and redraw your table with
[tableView reloadData];
Your problem is not in the code sample you include. Your problem rests elsewhere. We can't diagnose the problem on the basis of this one snippet. You'll have to share a more complete code sample with us.
Unrelated to your question, there is a subtle issue in your didSelectRowAtIndexPath. You should not just be updating the cellAccessoryType here. You really should be updating your model that backs your UI. This would be critical if the table had more rows than were visible at any given moment in time.
To illustrate the idea, let's assume your model was an array of objects with two properties, the title of the cell and whether the cell was selected or not.
Thus, your cellForRowAtIndexPath might look like:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
RowData *rowObject = self.objects[indexPath.row];
cell.textLabel.text = rowObject.title;
if (rowObject.isSelected)
cell.accessoryType = UITableViewCellAccessoryCheckmark;
else
cell.accessoryType = UITableViewCellAccessoryNone;
return cell;
}
And your didSelectRowAtIndexPath might look like:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
RowData *rowObject = self.objects[indexPath.row];
rowObject.selected = !rowObject.isSelected;
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
Again, your compiler warning/error is undoubtedly stems from some other problem in your source code, as your original code snippet is syntactically correct. I'm just trying to correct a different flaw in your didSelectRowAtIndexPath. In MVC programming, you really want to make sure you're updating your model (and then updating your view), not just updating the view.
But, to be clear, if you don't correct the error that is causing your current compiler warning/error, you'll probably just get another warning regardless of what you put into didSelectRowAtIndexPath. You have to identify why the compiler is balking at your current code.

Resources