What basis UITableViewCell object become nil - ios

I am asking very basic doubt belongs to Tableview, I created tableview programmatically without Storyboard/Xib.
The tableView numberOfRowsInSection it will return 14, the cell view fully dynamic and each cell height will different from one another.
My questaion is in tableview delegate method
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell=(UITableViewCell *)[tableview dequeueReusableCellWithIdentifier:cellIdentifer];
if (cell == nil )
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifer];
// how many time entering this loop
}
// ( adding subview to cell view).
cell==nil means need to enter loop right. Depending on what parameter cell object become nil? how many times it will enter, is it once? not at all.
When I checked, it entered 6 times.
if I use cellIdentifier, it will enter 14 times because Identifier different and every time it will create space for cell, its right because each time name will different and while scrolling it will reused.
NSString *cellIdentifer= [NSString stringWithFormat:#"%ld,%ld",(long)indexPath.section,(long)indexPath.row];
which basis it will enter 6 times. why not one's or 14 times. Please suggest what I did wrong. Because if used #"Cell" identifier, while scrolling repeatedly view will overlap. If I used second one cell view object will not overlaps & looks like perfect, but device memory size will increase
ref by https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableViewCell_Class/index.html

If I right got your question you need register your class in viewDidLoad method or loadView where you created a tableview like that [tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:CellIdentifier]; for right reuse in tableview

Code : 1
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell=(UITableViewCell *)[tableview dequeueReusableCellWithIdentifier:cellIdentifer];
if (cell == nil ){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifer];
// how many time entering this loop
}
In above code the loop will execute as number of visible rows in your tableview at first time. After that it will reuse cell as the cellIdentifier is same Cell and you need to update cell data as per indexpath.
it means if your table display 6 rows then it will execute for 6 times. Change rowheight and you can check.
Code : 2
NSString *cellIdentifer= [NSString stringWithFormat:#"%ld,%ld",(long)indexPath.section,(long)indexPath.row];
for above code the loop will execute total number of rows you have declared, Because it will create new cell for each indexpath.

If we give same Identifier to all cells, Disappearing cell uses the memory of Appearing cell. But, If we give different Identifier then every cell will have its own memory location and shows data perfectly.
Now suppose we have 1000 or more records to load in Table-view. If we will give different Identifiers, there will be lots of allocations in memory. This is the benefit of re-using cells.
Why 6 times ?
Because, if you give same identifiers, table will re-use cells. Maximum number of cells visible at the moment, are allocated at first. Then on scroll, appearing cell uses a memory location of a disappearing cell (cell dequeuing). So, every time you scroll, new cells are not allocated. Instead, already allocated cells are re-used
Why 14 times ?
Because, every cell has different identifier in this case. Every cell will have its own memory location.
Remember
Add subviews inside cell nil condition. Cell specific content should be assigned outside nil condition. Have a look at following code snippet:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellIdentifier = #"MY_CELL";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
/* Everything that is similar in all cells should be defined here
like background colors, label colors, indentation etc. */
}
/* Everything that is row specific should go here
like label text, progress view progress etc. */
return cell;
}

With same identifier:
When table cell is going to disappeared then this cell will be added in stack and these are reusable.
Now when we are going to show a cell then:
If we are using same identifier then controller will check that cell is available in stack with same identifier.
If yes, then we will get a table cell which is already used and UI was already set for this cell. So we need to reset UI before using it.
If not, then it will create new cell and trying to use it.
In your case I think 4-5 table cell is visible at a time so it is creating 6 table cell and reusing those cell.
With different identifier:
Table cell cell will not available in stack at creating cell for different indexpath. So it will create new one. so cell method will be called 14 times.

Related

What is the purpose of an Identifier in UITableView

I'm a little confused to why an identifer (*MyIdentifier) is always required. The code below demonstrates this. I have noticed all tableviews require at least one.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] autorelease];
}
What is the purpose behind having an identifier? Ive have seen a few tutorials where there is more than one. Also, reading the Apple documentation, I was a little confused about why the following is called:
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] autorelease];
}
I would like to know why it takes the input of 'MyIdentifier'? Assuming we had more than one identifier, which one should we choose? To be exact, what if we had a Parent, Child and sub-child etc.
Suppose you have several customized UITableViewCells and each of them have different backgound colors. In your storyboard or xib file, you may name the cell with red background color "redCell" and the one with blue "blueCell". Then you can select what kind of cell to add to a particular row using their identifiers. Let's say, you wanna apply red cells to odd rows and blue ones to even rows then you can use the following code to do this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *redCell = [tableView dequeueReusableCellWithIdentifier:#"redCell"];
UITableViewCell *blueCell = [tableView dequeueReusableCellWithIdentifier:#"blueCell"];
if (indexPath.row % 2 == 0) return redCell;
else return blueCell;
}
Without specifying an identifier, the system wouldn't know which kind of cell to pick.
From the docs:
The identifier is a string identifying the cell object to be reused. This parameter must not be nil.
For performance reasons, a table view’s data source should generally reuse UITableViewCell objects when it assigns cells to rows in its tableView:cellForRowAtIndexPath: method. A table view maintains a queue or list of UITableViewCell objects that the data source has marked for reuse. Call this method from your data source object when asked to provide a new cell for the table view. This method dequeues an existing cell if one is available or creates a new one using the class or nib file you previously registered. If no cell is available for reuse and you did not register a class or nib file, this method returns nil.
Every time you return a cell you have 2 options - you can create new cell and return it, dequeue cell that already exists and reconfigure it.
So the way this works is when you create cell you give it reuse identifier, so when cell goes off screen it can be used instead of creating new cell. After the cell is dequeued though, you might want to change its properties(like text or image)
You can have different reuse identifiers for different cell types(different content views)

dequeueReusableCellWithIdentifier: returns nil even after creating cell instance

I am trying to use a UITableView with a .xib file. I have done it in the past with storyboards, where you declare a reuse id in a dynamic prototype. I have this code inside the - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath function which should work:
NSString *ReuseId = #"DefaultCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ReuseId];
if (cell == nil) {
NSLog(#"cell == nil");
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:ReuseId];
}
However, when I run this, instead of printing cell == nil once it prints it for every row.
Why??
Thanks.
How many rows do you have? It should create up to to 11 or 12 at first on iPhone. Depending on the height of the tableview and the heights of the cells.
So if you have less rows than cells fit on the view, cell reuse will never happen and is not needed.
if (cell == nil) {
NSLog(#"cell == nil");
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:ReuseId];
} else {
NSLog(#"We have a reused cell!");
}
try to repeat your rows several times and scroll slowly. Now every time you are just about to scroll the first pixel of a new cell on the view, it should print "We have a reused cell!".
The first time when you open table view there will be no cells to reuse because all of them will be actually in use. Cells start being reused when you start scrolling the table and some cells start disappearing from the top.
update
Let's say you have a table view that can partially display 6 rows (5.5 by height). When UITableViewController loads the table with the cells, it creates 6 cell instances because all of them must be displayed at the same time.
When you start scrolling and the first cell is hidden, it gets added to reusable cell set. That's when creating 8th cell (because 7th will be created newly, 'cause 1st one will still be visible by half height) the first one will be reused.

Creating a reusable UIView similar to UITableViewCell

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.

Removing old data from reusable cell

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.

UITableview Scroll erases data in text field inside UITableviewcell

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.

Resources