Why UICollectionView.cellForItemAtIndexPath always calls UICollectionViewDatasource.cellForItemAtIndexPath? - ios

Scenario: I want to check selected state of specific visible UICollectionViewCells with indexPath for which I am calling cellForItemAtIndexPath on the UICollectionView reference.
Problem: Calling UICollectionView.cellForItemAtIndexPath always calls UICollectionViewDatasource.cellForItemAtIndexPath which returns a new cell without the selection state.
Question: Why UICollectionView.cellForItemAtIndexPath always calls UICollectionViewDatasource.cellForItemAtIndexPath ?
Apple documentation says the return value is "The cell object at the corresponding index path or nil if the cell is not visible or indexPath is out of range."
Am I missing something or is my implementation of cellForItemAtIndexPath in datasource incorrect ?
- (UICollectionViewCell*) collectionView: collView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
SudoCell *cell = [collView dequeueReusableCellWithReuseIdentifier:CELL_ID forIndexPath:indexPath];
[cell setValuesWithSection:indexPath.section item:indexPath.item modelObject:_model];
cell.backgroundColor = [UIColor whiteColor];
return cell;
}
As a work around currently I am setting storing the section and item values as instance values of the cell. Looping through all visible cells to find matching cells with the section and item values and checking visible state. This becomes tedious when number of cells are huge.
Please advice.

You're misunderstanding how protocols and delegates work. cellForItemAtIndexPath: is a delegate method that UICollectionView and UITableView call on its datasource in order to populate the Collection or Table View.
So lets say you have a CollectionView and you gave it a datasource. At some point when you run your application that CollectionView is going to call a method on the datasource numberOfSectionsInCollectionView: in order to get how many sections are needed for the CollectionView
Then it calls collectionView:numberOfItemsInSection in order to get the items for each section for the collection. This method is called for each individual section defined in the Collection View.
Finally it calls collectionView:cellForItemAtIndexPath: in order to get the Cells for each item of the collection. This method is called for each individual item defined in the Collection View. This would be where you could programmatically configure the cell to display the information you want, for instance, if you wanted to give the collection a Cell that had an image attached to it, this is where you would do it.
https://developer.apple.com/library/ios/documentation/uikit/reference/UICollectionViewDataSource_protocol/Reference/Reference.html
These are all datasource methods with the sole responsibility of providing data for the Collection View. If you want to respond to user interactions you need to use UICollectionViewDelegate Protocol and implement the methods
collectionView:didSelectItemAtIndexPath:
collectionView:didDeselectItemAtIndexPath:
As the signature implies, these methods are called in response to actions performed on the collection, along with an IndexPath describing the section and item number of the cell the action as performed on.
https://developer.apple.com/library/ios/documentation/uikit/reference/UICollectionViewDelegate_protocol/Reference/Reference.html
UICollectionDelegate Protocol reference above you can use to respond to different events that occur in that view
But none of that information above is any use to you if you don't have a basic understanding of protocols and delegates. I would recommend spending time reinforcing that understanding first before continuing

Related

What is wrong if cellForItemAtIndexPath: is called in didSelectItemAtIndexPath:

I called cellForItemAtIndexPath: in the didSelectItemAtIndexPath: method. The result was that didSelectItemAtIndexPath: was only called one time per cell. So I can tap the first time on a cell, then I can tap on another cell, but if I tap on the first cell again the didSelectItemAtIndexPath is not called anymore (but all other "new" cells).
Now I'm not calling cellForItemAtIndexPath: anymore and now didSelectItemAtIndexPath: works as expected. But why I'm not allowed to do this? Any reasons for this?
The collection view is caching instances of cells for you and you dequeue them when you create the cell at an index path. The collection view is making some assumptions about what's going on when you do this, so if you start making multiple requests for cells at the same index path strange things start to happen. Sometimes this results in visual issues, sometimes interaction issues.
If you want to get the cell at a specified index path you should ask the collection view directly instead of using your own delegate method, this will give you the existing cell on screen rather than a new cell.

Check if a UITableViewCell is selected, and the cell went off screen

I have a custom UITableViewCell that dequeueReusableCells. I have an int called selectedRow which gets the selected rows number in the method of didSelectRowAtIndexPath. I then pass selectedRow to an int called rowNumber which is in the class of my customCell.
In customCell.m, I have the method prepareForReuse. In that I made an NSLog of rowNumber.
What I want to do is: if a row is selected and that row went off screen, then perform some code. I would probably have to use prepareForReuse, but I don't know what to do in it.
I know it's a bit complicated, but if you have any questions, then I'd be happy to answer
Actually, you don't need to call prepareForReuse directly as it would be called automatically:
this method is invoked just before the object is returned from the
UITableView method dequeueReusableCellWithIdentifier:.
and as you don't know what to do in it, note:
For performance reasons, you should only reset attributes of the cell
that are not related to content, for example, alpha, editing, and
selection state
UITableViewCell Class Reference
You can use - (void)tableView:tableView didEndDisplayingCell:cell forRowAtIndexPath:indexPath; in UITableViewDelegate to know which cell is scrolled off screen.
However, this method is iOS6+ only.
You're over complicating things. You don't have to do prepareForReuse the in the custom cell.
Take a look at this.
http://www.icodeblog.com/2009/05/24/custom-uitableviewcell-using-interface-builder/
Its pretty similar for storyboards.

Alternative method for getting reference to a UITableviewCell?

The design of the app I am working on, specifically the tableview part is quite complicated.
There are around 5-6 methods in which I need to get a reference to particular cell from the table view.
What I do not like is that I have to use the
-tableview:cellForRowAtIndexPath
This is a datasource method and does a lot of heavy lifting. Custom cell configuration, dequeueing, getting data for particular cell..etc.. The point is all this code is executed again,even if it does not make any sense at all. The cell object is completely loaded already.
Am I right in my observation and if yes, is there a working tested and lightweight solution?
Perhaps there could be an index of references to all cells. Asking it, I would immediately get the reference without the datasource code.
UITableView has cellForRowAtIndexPath: method.
From UITableView reference:
Returns the table cell at the specified index path.
- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath
Parameters
indexPath The index path locating the row in the receiver.
Return Value An object representing a cell of the table or nil if the cell is not visible or indexPath is out of range.

dequeueReusableCellWithIdentifier returns nil in didSelectRowAtIndexPath method

I'm using CustomTableCell in my project. I can see the "dequeueReusableCellWithIdentifier" is returning a valid cell in the method "cellForRowAtIndexPath" as it should eg whenever the table is reloaded.
In the CustomTableCell I have some images that I want to reuse, without downloading them again. However everytime i get a "nil" with the "dequeueReusableCellWithIdentifier" when used inside "didSelectRowAtIndexPath" delegate method.
The original table was not destroyed. The table is class object and I can see it is the same as the tableView received in the "didSelectRowAtIndexPath" parameter.
This behavior is consistent with out viewcontrollers in my project where I have used tableview.
I'm using ARC. Any idea what could be wrong ?
Thank you!
The question is a little vague, but I believe you're simply trying to access your custom cell from within "didSelectRowAtIndexPath". If this is the case, then you should utilize:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomTableCell *myCell = (CustomTableCell*)[tableView cellForRowAtIndexPath:indexPath];
}
Then you're free to access any part of your custom cell that you wish.
In case your curious:
"dequeueReusableCellWithIdentifier" is utilized to reduce the amount of object allocations & deallocations by reusing table cells that have "moved" beyond the visible range. i.e If you have a table with 1000 cells, but only 10 are visible at any given time. ~10 cells will be created and reused over and over again. Thus, when called dequeueReusableCellWithIdentifier will pull one of these cells off the stack of cells that aren't currently being utilized, or create one if the stack is empty.

iOS callback when uitableviewcell gets destroyed

As a user scrolls up and down in an uitableview cells get destroyed and created.
Is there a way to detect when a cell is going to be or has been destroyed?
Assuming that by "getting destroyed" you actually are referring to a cell getting reused, simply implement prepareForReuse within your UITableViewCell derived class.
prepareForReuse
Prepares a reusable cell for reuse by the table view's delegate.
- (void)prepareForReuse
Discussion
If a UITableViewCell object is reusable—that is, it has a reuse
identifier—this method is invoked just before the object is returned
from the UITableView method dequeueReusableCellWithIdentifier:. For
performance reasons, you should only reset attributes of the cell that
are not related to content, for example, alpha, editing, and selection
state. The table view's delegate in tableView:cellForRowAtIndexPath:
should always reset all content when reusing a cell. If the cell
object does not have an associated reuse identifier, this method is
not called. If you override this method, you must be sure to invoke
the superclass implementation.
Availability
Available in iOS 2.0 and later.
See Also
– initWithFrame:reuseIdentifier:
#property reuseIdentifier
Declared In
UITableViewCell.h
Without going into the implications of suitability or performance, another option might be to periodically check what cells remain visible, using the visibleCells method of the UITableView class:
- (NSArray *)visibleCells
As per the documentation:
Returns an array containing UITableViewCell objects, each representing a visible cell in the receiving table view.
You can subclass UITableViewCell and override it's dealloc method.
Any good reason to do it assuming you are reusing the cells to save the resources ?
What you're attempting to intercept is part of the internal implementation of UITableView and how it manages its cells. While there are ways in which you can attempt to intercept such behavior, I would suggest that you avoid using them, as there is no guarantee that future implementations of UITableView will maintain this behavior.
It would be better in cases such as this to consider a different approach: be it design and implement your own table class, or change your code logic.
As stated above, cells aren't destroyed when the leave the screen. However there are some things you can do, to track related actions, depending on what you are trying to do.
First there is a delegate message:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
This is called before a cell enters the screen. Another possibility is the already stated prepareForReuse method of a cell.
Another approach would be: Try and override willMoveToSuperview: or any other of the related methods. I am not sure if this is fired after the cell becomes invisible, but it might work.
Best regards,
Michael

Resources