I want to put new values in a table view.
My tableView:cellForRowAtIndexPath: has the following
if (cell == nil) { // do something }
When I remove the if statement on top and use reloadData the app crashes. Do I need to clear the old values in the table before I use reloadData? If so how?
Thanks
Edit:
I'm sorry i didnt mean that i put reloadData inside tableView:cellForRowAtIndexPath:. i put it inside another method and removed if (cell == nil) { // do something } from inside tableView:cellForRowAtIndexPath:
You should read the TableView Programming Guide to familiarize yourself with the concepts. reloadData is a method that eventually calls tableView:cellForRowAtIndexPath:.
You are generating infinite loop by calling reloadData inside
tableView:cellForRowAtIndexPath:
There is no need to clear data in a table view. You may want to ensure you properly implement -prepareForReuse in your UITableViewCell subclasses though, so that reused cells don't show old data.
It's BTW unclear why you want to remove the if (cell == nil) bit. If there is no cell to reuse (dequeue) then you need to create a new one. If you don't you'll return nil, which will cause an exception in UITableView.
You (99% of the time) can't call reloadData from tableView:cellForRowAtIndexPath:.
This is because reloadData eventually calls tableView:cellForRowAtIndexPath, so you get a never ending loop.
You need to explain more what you trying to do (and some extra code would be nice) in order to answer the question further.
reloadData calls all tableView data source , so you cann't call it in cellForRowAtIndexPath it will make an infinite loop.
Related
My app allows user to reorder rows in a table view, but I want to reloadData after moving is done to change something (add some text to the first row).
I tried to reloadData in moveRowAtIndexPath: method, but it makes the app hang because this method is called many time (according number of rows need to be moved) and the table view is reload many time.
So, I just want to know when the moving behavior is done then I reloadData in just one time. Does anyone know about this? Please help me. Thanks you so much!
- (void)doReload
{
[myTable reloadData];
}
You can do [self performSelector:selector(doReload) withObject:nil afterDelay:0.1] in the moveRowAtIndexPath method but you can't call [myTable reloadData] directly because as you found it causes a loop. Basically you need to allow the table manipulation to finish and the run loop to get around to calling your method which then causes a reload of your table. This is a bit of a hack but it works well. Ordinarily you don't need reloadData at this point but you may be trying to do something out of the ordinary.
-(int)table:(UITable*)table movedRow: (int)row toRow: (int)dest
Is there a way to tell a UITableView to preload all rows?
The tableView is supposed to show several comments (up to 80 comments).
So my CommentCell uses a Setter to adapt the cell to a specific comment.
-(void)setComment:(Comment *)newComment {
if (newComment != comment) {
[comment release];
comment = [newComment retain];
/*
* set the cells view variables here
*/
}
}
This specific setter takes quite a bunch of processing resources and scrolling gets kinda laggy.
I am using a comment-specific reuseIdentifier instead of a static cellIdentifier when calling
dequeueReusableCellWithIdentifier:
in order to assure, that "newComment" equals the old "comment".
And in fact this does work great when scrolling over cells which have already been loaded.
But when scrolling through the comments for the first time, it still lags like hell.
Which leads me to my question:
Is there a way to tell the tableview to preload all cells? (which I doubt)
or
Do I have to implement my own cache instead of relying on "dequeueReusableCellWithIdentifier:"?
Keep in mind that your comment specific reuseIdentifier could be what is causing everything to go slow (or at least, it isn't helping). The reason we use reuseIdentifier for UITableViewCells is because if you try to allocate a new cell every time you need one it isn't as performant as if you can just reuse one that was already made.
I'd recommend pre-computing your comments so you can just set properties of your cells and reusing cells after they scroll off the tableview.
No, you have to preload your data in your dataSource. Just put everything you need in an array and fill the table's cells from that array of preloaded objects.
In my test on the device and in simulator, dequeueReusableCellWithIdentifier: returns nil more than once (about 5 times for a table with 15 rows). Is this normal? I was under impression it should return nil only the very first time?
The UITableView class implements the behaviour of dequeueReusableCellWithIdentifier:. As long as you're calling it in the right place (i.e. inside of tableView:cellForRowAtIndexPath:) then there's not much you can do about it returning or not returning reusable cells.
It's not something I'd worry about unless I could prove that specific behaviour was giving me crashes or performance issues.
The table view handles cells using the fly weight pattern. If you familiarize yourself with that you will have a better grasp of what is happening and why.
Both tableView and collectionView have limits. If you scroll fast enough, or there are a lot of elements on that cell (takes longer to prepare), iOS may not prepare the reusable cell as fast as the cellForIndexPath is getting called.
So in the case of tableView, dequeueReusableCellWithIdentifier: returns nil.
And in the case of CollectionView dequeueReusableCellWithReuseIdentifier:forIndexPath: will create a new object for you.
From the official documentation:
The reuse identifier is associated with a UITableViewCell object that the table-view’s delegate creates with the intent to reuse it as the basis (for performance reasons) for multiple rows of a table view. It is assigned to the cell object in initWithFrame:reuseIdentifier: and cannot be changed thereafter. A UITableView object maintains a queue (or list) of the currently reusable cells, each with its own reuse identifier, and makes them available to the delegate in the dequeueReusableCellWithIdentifier: method.
http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UITableViewCell_Class/Reference/Reference.html#//apple_ref/occ/instp/UITableViewCell/reuseIdentifier
I don't understand this. Well, I understand the basic idea, I think, that you create UITableViewCells, and try to reuse as many as you can instead of making new ones (or something like that). But what exactly decides whether or not a cell is reusable? If I've got two identical (visually) cells, but with different texts (well I suppose they aren't entirely identical), can they both have the same identifier? Or should they have different ones? Or in what situation are you supposed to use different identifiers?
Can anyone clarify or link to a place where it is?
Ok, this is how I believe it works:
Using dequeueReusableCellWithIdentifier for the tableView, you can greatly speed things up. Instead of instantiating a lot of cells, you just instantiate as many as needed, i.e. as many that are visible (this is handled automatically). If scrolling to an area in the list where there are "cells" that haven't got their visual representation yet, instead of instantiating new ones, you reuse already existing ones.
You can try this yourself by doing this:
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
NSLog(#"new one");
}
else
{
NSLog(#"old one");
}
Remember, you only want dequeueReusableCellWithIdentifier to return a cell if it is applicable. So if a cell is going to be reused, make sure it is correct for the situation. That's what reuseIdentifiers are for. Usually, you will only need one. But there might be a list that uses several different kinds of cells, and in that case, you'd have to keep them separate by providing different reuseIdentifiers. Otherwise you might end up getting a cell that you treat as some other kind of cell (for example, UITableView cell instead of the custom one you wanted).
So basically, as I understand it, use different reuseIdentifiers for different kinds of cells, where kind means class. If you only use standard cells, you probably only need one reuseIdentifier.
This design pattern is known as object pooling.
Just to add some things to quano's otherwise very good answer: (I tried to add this as a comment, but it was too long!)
Even reuse identifiers can be omitted in developing, although this must be done in very specific circumstances. If you have a table view of 6-7 cells, and each one is different, you may find that creating a new cell with nil as the identifier may be preferable.
Having a reusable cell means that in each time the cellForRowAtIndexPath is called, you must check the cell, initialize it if there is no reusable cell, and outside of the init scope you must explicitly iterate through all possible indexpaths and set the values for each label explicitly depending on what kind of cell you have! So, in a table view with 10 dinstinct cells, you will have to take care of creating the cell if nil, and filling it up depending on what you created.
Therefore, in this case, it's preferable in terms of code maintenance to initialize each cell with nil identifier (since it's not going to be reused anyway) and fill each cell's info appropriately without worrying about reusing it.
UITableView is like having a cell pool for each reuseIdentifier, so that it recycle the cell
I like this video from http://oleb.net/blog/2014/05/scrollviews-inside-scrollviews/
http://im.ezgif.com/tmp/ezgif-3302899694.gif
I have a UITableView with style "Grouped" which I use to set some options in my App. I'd like for one of the cells of this UITableView to only show up depending on whether another of this UITableView's cells is activated or not. If it's not, the first cell should show up (preferably with a smooth animation), if it is, the first cell should hide.
I tried returning nil in the appropriate -tableView:cellForRowAtIndexPath: to hide the cell, but that doesn't work and instead throws an exception.
I'm currently stuck and out of ideas how to solve this, so I hope some of you can point me in the right direction.
You should remove the data behind the hidden cells from the table view's data source.
For example, if you are using an array, when an action occurs that causes a cell to be hidden, you would remove the object for that row from the array. Then, as the table view's data source, the array will return one less total count and only return valid cells for every row in that count (no nil).
This approach may require maintaining a second array with all of the objects (including hidden).
To update the view, check out reloadRowsAtIndexPaths:withRowAnimation:.
Here's a handy post in which the author provides some source code for performing animations on the currently selected cell:
http://iphonedevelopment.blogspot.com/2010/01/navigation-based-core-data-application.html
He's using this in a NSFetchedResultsController context, but you can see how he's using various calls to add/remove cells & sections.
Now, in your case, you'll need to modify whatever array you're using to host the data used to generate the rows in your tableView when you "activate" your cell, then selectively use:
tableView:insertRowsAtIndexPaths:withRowAnimation:
tableView:deleteRowsAtIndexPaths:withRowAnimation:
tableView:insertSections:withRowAnimation:
tableView:deleteSections:withRowAnimation:
to adjust things accordingly (you can start with tableView:reloadData:, but it's inefficient).
I realize that the API can be a bit daunting, but take the time to read through it and understand what the various calls do. Understanding how the UITableView uses its datasource and delegate, as well as the chain of events that occur when cells are selected/deleted/etc., is important if you want to get things just right (and crash-free).
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:withRowAnimation:]; // or insertRowsAtIndexPaths:withAnimation:
[tableView endUpdates];
Before cellForRowAtIndexPath is called, numberOfRowsInSection is called. You should return the appropriate value of cells in the section there, so if you only want to show 1 cell, return one. The logic what cells are shown has to be implemented partially in both methods