This has been driving me mad, I have look at about every post on around in regard to this.
I am using custom tableview cell classes and XIB files. It all works fine, until I scroll the table view off screen (that old chestnut). I know it has to do with the dequeueReusableCellWithIdentifier method and not clearing the old data. However I have no idea how to implement this.
Once the cell scrolls off screen., it causes an overlap and continues to add images to the cell.
let cell: DetailHeaderTableViewCell = tableView.dequeueReusableCellWithIdentifier("detailHeaderTableViewCell") as DetailHeaderTableViewCell
From what I have found, I need to implement something like this:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:nil];
UIImageView *defaultImageView;
UILabel *customLabel;
if (cell == nil) {
// create the cell and empty views ready to take the content.
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
} else { ... }
However I cannot work out how to achieve this in swift.
Many thanks in advance.
What you're looking for is UITableViewCell's prepareForReuse method.
If you're using custom UITableView cells, you need to implment this method to clear out any old data from your cells before they are reused.
Related
I have a UITableView that has different types of cells in it.The common approach is to have different UITableViewCells and reuse them depending on the data object.
Now, I have 9 different types of data objects. But my view is similar to the Facebook feed view with like button, comments button, user image and user name. Only the centre view changes based on the data object.
My question is, should I use 9 different type of cells with a common class for these elements or should I use one cell and add the centre view as and when the cell is created?
Currently my approach is to use one cell and add the centre view. Will the UITableViewCell be re-used if we follow that approach?
The table view cells will always be reused if you initialize the cell with reuseIdentifier: and use the dequeueReusableCellWithIdentifier: method on the table view.
As far whether to use a single UITableViewCell subclass or several, it depends on how much is different between each of your 9 content types. If they all contain the same UI elements, using 1 subclass makes sense. Otherwise, you can create multiple subclasses and still reuse the cells with dequeueReusableCellWithIdentifier: as long as you pass in a different unique identifier for each subclass. Each subclass would be independently reused.
Here's what your cellForRowAtIndexPath: could look like if you're using multiple cell classes:
NSString *primaryCellID = #"PrimaryCellID";
NSString *secondaryCellID = #"SecondaryCellID";
if (someCondition) {
CustomTableViewCell1 *cell = [tableView dequeueReusableCellWithIdentifier:primaryCellID];
if (!cell) {
cell = [[CustomTableViewCell1 alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:primaryCellID];
}
return cell;
}
else {
CustomTableViewCell2 *cell = [tableView dequeueReusableCellWithIdentifier:secondaryCellID];
if (!cell) {
cell = [[CustomTableViewCell2 alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:secondaryCellID];
}
return cell;
}
I've got multiple (n) tableviews I need to display horizontally. I am using a UICollectionView where each collection view cell has a UITableView. Each tableview takes up most of the screen, there are three tableviews (three cells) displayed at a time, with only a few pixels of the left and right tableviews showing when there is no scrolling going on. All the table views are objects of the same class, and all use only one type of cell.
My problem is that every time I scroll to display the next tableview, all of the cells in the tableview have to be allocated and init'ed. This is very expensive, and there is an obvious split second freeze if the new tableview has a full set of cells to display. I had the idea of making a UITableView in my parent class (the one holding the UICollectionView) calling it a prototypeTableView and when I create the tableview cell I set that prototypeTableView as a property of that cell (the tableview).
Here is the code in my Parent class that holds the UICollectionView. Where I set the prototypeTableView to the
CollViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CollViewReuse forIndexPath:indexPath];
cell.theTableView.prototypeTableView = self.prototypeTableView;
Here's the code in my UITableView child class
MyTableViewCell *cell = [self.prototypeTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[MyTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
My problem is that I get a nil cell back every time I dequeue a cell. If there is a better way to do this please let me know. I just wanted to test this and I can't completely call this a bad way to do it until I can get the table view to actually dequeue my cells!
Thanks!!
Ahoy!
I'm trying to create a reusable UIView (for various reasons) similar to the UITableViewCell implementation used in UITableViewController. I'd like to use the reusable view in a UIScrollView so I know i'm not trying to achieve something that's entirely unattainable.
The default implementation of this is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//declare cell identifier
static NSString *cellIdentifier = #"cell_identifier";
//dequeue cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
//check cell is valid
if(cell == nil)
{
//create a new cell
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
}
//
//return cell
return cell;
}
From this, it's worth noting that the cell is dequeued from the UITableView. If the cell is invalid, a new cell is created. My question is, how does this cell then become "queued" for reuse later?
My current attempted implementation looks like this:
- (TestScrollViewCell *)scrollView:(TestScrollView *)_scrollView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//declare cell identifier
static NSString *cellIdentifier = #"cell_identifier";
//dequeue cell
TestScrollViewCell *cell = (TestScrollViewCell *)[scrollView dequeueReusableCellWithIdentifier:cellIdentifier];
//check cell is valid
if(cell == nil)
{
//create a new cell
cell = [[TestScrollViewCell alloc] initWithFrame:CGRectZero];
}
//
//return cell
return cell;
}
I'm thinking that adding a NSMutableDictionary to my TestScrollView to store the cellIdentifier and the TestScrollViewCell (UIView) and then plucking them back out based on the dictionary key would be a good start but is this really a true implementation of "reusable" cells?
The issue I can see is that I would then be adding the UIView to the ScrollView which is positioned based on the frame. Dequeing a view in this sense wouldn't allow me to then add the view to the scroll view without affecting the first view (by modifying the frame) but surely this is how UITableViewCells work, as well as section headers/footers?
I've been looking at this implementation which seems to be following the same route I was intending on implementing but i'm not 100% sold that this is a true implementation of reusable cells.
Has anyone had any luck with this previously? I'm trying to take Apple's lead on this one but other than UITableViewCell and MKAnnotationView (MapKit) there aren't any accessible implementations of this for me to glean from.
Any help would be greatly appreciated.
It's not just the view, it's the whole UITableViewController you'll need to recreate. The reuse flow goes like this:
dequeueReusableCell gets empty reused cell from some storage, I guess, from NSMutableArray (grab first object from array, then delete it from array and return it). If array is empty, method returns nil. You check for cell value, if it's nil, you create a new instance of cell class. If it's not nil, you fill it with your data.
This goes for every visible cell, that is, every cell that can fit on screen. Any non-visible cells are not initialized. When user scrolls the table, cell that are gone completely off-screen (not a single pixel visible) sent to reuseQueue – all their subviews and values return to default values or just nilled, and then cell gets added to the end of our NSMutableArray that is the queue.
I hope I explained well enough.
EDIT: Oh, and one more thing - you'll need different reuse queues for each reuse identifier.
I have a uitableview with each cell having a scroll view as the subview.
the scrollview has a bunch of images in it.
so when i change the data in the data source and after calling the reload table
the images doesn't change but when i remove the dequeue the new data is reloaded.
is there any method to remove the contents in the dequeue so that i don't get the old data
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"looser"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
scrollview=[[myscrollView alloc]initwitharray:imagearray];
[cell.contentView addSubview:scrollview];
}
}
A tableview works as follows:
It has room for a certain amount of cells on the screen, let's say 7 as an example. The tableview will ask you for the 7 cells of indexes 0 through 6.
If the top cell leaves the screen by scrolling, it will be placed in the reusable cell queue. There are now 6 cells on the tableview.
A new one comes up at the bottom now, the tableview asks for the cell at index 7. You call dequeueReusableCell, and you get the one that was at the top earlier.
The tableView has no idea what your cell is like, as it can be subclassed, so it will not make any changes to it. It is up to you to use your knowledge of how the tablecell is constructed to empty it, then fill it with the correct new data.
The reason tableview works like this is for performance. In stead of having maybe 100 views that would have to be checked (or mostly, ignored, which also costs time) for every scroll movement, it has a maximum of 7.
So in short, no. There are no default methods to remove data from reusable cells in UITableView, since UITableView can not and should not know what kind of cells they are. It is up to you to clear the cells when the tableview gives them to you.
Create a custom cell and it generates a method
- (void) prepareForReuse{}
Which do you cleanse all data from a cell and the output will be an empty cell.
No, not while the cell is in the cache. When you dequeue a reusable cell you should clear out the old data first before using it again.
Maybe you should just remove the stuff you don't want.
I have a UITableViewController with UITextfield inside the tableview cells. If I scroll the table view, the user entered data in the textfields disappears. I tried to add the textfield data to a NSMutableArray but it still didn't work. Any help please.
When cellForRowAtIndexPath: is called, the cell you return has to be completely filled in with whatever data you want to show. So, if the cell includes a UITextfield, you'll need to set it's text property to the right value for that row in your data.
When a table cell disappears off the top or bottom of the screen, the UITableViewCell itself becomes available for re-use. (As you scroll, cells disappear, and new cells appear, but the UITableView class is re-using the UITableViewCell objects.) In cellForRowAtIndexPath: when you get a cached cell to use, you have to be sure to setup everything you want it to show for the row in question, otherwise you might see some odd behavior in your table.
Does this help?
EDIT:
Here's an example of the typical pattern used in cellForRowAtIndexPath:. Notice the use of dequeueReusableCellWithIdentifier:. That method returns a previously allocated but not in use UITableViewCell, if there is one. Notice further that if no cached cell is returned, the code creates a new one, and sets it up (with stuff that is independent of anything that might be row specific). Following that, you'd setup the cell as you need it for the row in question.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *SearchResultsCellIdentifier = #"SearchResultsCellIdentifer";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:SearchResultsCellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:SearchResultsCellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
// Your row-specific setup of the cell here
// ...
return cell;
}
Check the docs for specifics about these methods. There are LOTS of examples from Apple and elsewhere about how to implement tableViews.