Getting previous UITableViewCell in loading of next cell - ios

So, this is weird.
I have a UITableView that loads cells in from cellForRowAtIndexPath. I'd like to know the state of the previous cell before properly loading the current cell, so I load in the previously created cell with CustomCell *cell = (CustomCell *)[tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row-1 inSection:indexPath.section]]; but I am always getting nil.
I'm not sure what's going on as this process seems pretty simple - does the table view not save the cell on each run of cellForRowAtIndexPath?

Related

What basis UITableViewCell object become nil

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.

How to create object in Objective-C?

I am new in iOS development. Currently I am reading this tutorial http://www.appcoda.com/how-to-handle-row-selection-in-uitableview/ . I am facing problem when I am reading this line
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
I know object in objective-c is created by following way
classname *objecname = [[classname alloc]init];
My confusion point is here UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
How cell object is created here? Please tell details.
Based on the tutorial you linked to, I'm assuming you are dealing with selecting a row. When you select a row, you have access to the NSIndexPath of that row, which contains two parts:
The section of the cell
The row of the cell
With that information, let's break down the confusing code: UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UITableViewCell *cell
This part declares your variable. You've chosen to name it cell. You could just as easily have named it theCell, like so:
UITableViewCell *theCell
The * means that you're declaring a pointer, which is just a reference to an actual object, or the table view cell.
[tableView cellForRowAtIndexPath:indexPath]
The tableView refers to the UITableView that was just selected. UITableView has a method called cellForRowAtIndexPath, and what that method does is retrieve the cell at the specified section and row of the UITableView. In your case, it retrieves the row that you've just selected and stores the reference to it in your cell variable.
When declaring a new object, yes, it would take on the following syntax:
classname *objecname = [[classname alloc] init];
The key word here is new. When dealing with selecting rows in a UITableView, you don't want to create a new cell because you can't select a cell that doesn't exist. You want to get the cell that the user has just selected.
I assume you have this code in your didSelectRowAtIndexPath: method, is that right?
If so, you're tapping on a cell that is on the screen, so the cell object already exists. Where and how was it created? Inside cellForRowAtIndexPath:, which you have already written.
So when you make this call
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
it just gives you the cell object to work with, but it doesn't create a new one.
The cell is not created with that line. That line gets a reference to a cell that is already in the table view at that indexPath.

Make custom dynamic cells NOT reusable in UITableView

I'm using a custom UITableViewCell subclass, called MessageCell.
I initialise the cell like so:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MessageCell *cell = [tableView dequeueReusableCellWithIdentifier:#"messageCell"];
// more code
return cell;
}
In each custom cell I have a checkbox used to mark this cell. When a cell is marked it is being added to an array, and vice versa.
While this works in terms of data, the UI is not reflecting it as it should. What happens is that in addition to the marked cell, other cells are being marked.
I assume this behaviour stems from cell reusability, i.e when I mark a cell as selected it has a common pointer with all future cells in this screen position.
For this reason, I want to use "normal" non-reusable cells.
So I tried:
MessageCell *cell = [[MessageCell alloc] init];
OR:
MessageCell *cell = [[MessageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
But both of them resulted in displaying empty cells.
You still want to reuse cells! Just check your data and set the checkbox to checked or unchecked in your cellForRowAtIndexPath:
MessageCell *cell = [tableView dequeueReusableCellWithIdentifier: #"messageCell"];
if (condition cell should be checked) //set the cells checkbox to checked
else //set the cells checkbox to unchecked
Or some approach like that.
PS on re-reading your question:
do you really need a reference to a cell in your array? Can't you for example store the indexPath and use cellForRowAtIndexPath: if you need to make a cell out of it?

When creating my UITableViewCells, why is the init never called? (Resulting in blank cells?)

In my UITableView, I recently changed the structure of the cell from formerly just putting UILabels in the contentView of the cell, to adding two UIViews (CellFront and CellBack, on top of one another) into the contentView (so I can achieve a sliding effect by sliding the top one off and revealing the lower one) and adding the UILabels to the top UIView.
However, now, for whatever reason, the cells never get init'd and as a result my UITableView is full of blank cells.
My cell gets created as follows (ArticleCell is a subclass of UITableViewCell):
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = nil;
ArticleInfo *articleInfo = [self.fetchedResultsController objectAtIndexPath:indexPath];
// Checks if user simply added a body of text (not from a source or URL)
if ([articleInfo.isUserAddedText isEqualToNumber:#(YES)]) {
CellIdentifier = #"BasicArticleCell";
}
else {
CellIdentifier = #"FullArticleCell";
}
ArticleCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[ArticleCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// If the user simply added a body of text, only articlePreview and progress has to be set
cell.preview = articleInfo.preview;
// If it's from a URL or a source, set title and URL as well
if ([articleInfo.isUserAddedText isEqualToNumber:#(NO)]) {
cell.title = articleInfo.title;
cell.URL = articleInfo.url;
}
return cell;
}
But I set a breakpoint on the initWithStyle method above within the if statement and it never gets called:
What would cause this? I'm deleting the app and building it from scratch every time, so data is definitely being added to the UITableView, but all the cells are blank. And I can tell a bunch of cells are being added as I have disclosure indicators on all of them, and the table view just gets filled with empty cells with the indicators only.
What am I doing wrong?
try
ArticleCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
instead of
ArticleCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
the first one is the old standard way. It will not create a cell for you. While with the second a cell will be created form the storyboard. So if you use storyboards you should use indeed the method you are using now, but it will never go info the if branch, as the cell will never be nil.
when instantiating form storyboard, initWithStyle:reuseIdentifier: is never called. Either set everything up in -initWithCoder: or -layoutSubviews

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.

Resources