As I understood it, its valid to create a UITableViewCell with a reuseIdentifier of nil, if there's no expectation for that cell to be reused, for example when there will only be one or two rows for that type of cell.
Someone suggested the other day that this was incorrect. Is it?
No, i think this is perfectly fine, according to Apple's official documentation:
reuseIdentifier
A string used to identify the cell object if it is to be reused for drawing multiple rows of a table view. Pass nil if the cell object is not to be reused. You should use the same reuse identifier for all cells of the same form.
https://developer.apple.com/library/IOs/documentation/UIKit/Reference/UITableViewCell_Class/index.html
Related
The way my current design works is I dequeue a cell and give it a story id. In the cell initWithStyle I create mysubView (UILabel), set the autolayout constraints and fire a API request that loads the story title async. Then I set the text for the UILabel.
The problem is - at this point the UITableViewController already calculated the wrong height for the cell because initially the UILabel has no text. The only way how to update it now is by doing
parentTableView.beginUpdates
parentTableView.endUpdates
But it feels wrong because I need to reference the parent UITableView from within the cell and there has to be a better way. Am I missing something? I'm using UITableViewAutomaticDimension by the way for the row height.
I suggest you watch Session 211 of WWDC 2012, Building Concurrent User Interfaces and apply the concepts there. This features cells whose contents are independently queried and rendered.
The basic concept is as follows:
1. In tableView:cellForRowAtIndexPath, a cell is instantiated.
2. In the same method, an operation for retrieving the data to populate the cell is created and stored into a dictionary. A reference to the cell is passed to the operation. The operation has a completion handler that populates the cell and removes the operation from the dictionary.
3. Before the cell is returned from the method, the operation is added to an operation queue.
4. In tableView:didEndDisplayingCell:forRowAtIndexPath, operations for cells that have moved off-screen are cancelled and removed from the dictionary.
add following code in "cellForRowAtIndexPath" method before return cell.
cell.setNeedsUpdateConstraints()
cell.updateConstraintsIfNeeded()
also You can use only "updateConstraintsIfneeded"
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
What is the purpose of the reuseIdentifier in above constructor.
The reuseIdentifier is used to group together similar rows in an UITableView.
A UITableView will normally allocate just enough UITableViewCell objects to display the content visible in the table.
If reuseIdentifier has not been set, the UITableView will be forced to allocate new UITableViewCell objects for each new item that scrolls into view, potentially leading to laggy animations.
The doc says:
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.
reuseidentifier is an id from which you can get cell from it.
As a cell scrolls out of the viewable area of the screen, the object representing it gets reused for cells scrolling on to the screen. The reuse identifier tells the system that an object can be reused for a cell entering the screen for which you request the same identifier.
Reuse identifiers are required by UITableViewCell in order to support the dequeueing of reusable cells by uniquely identifying cell types. Normally you create a unique string reuse identifier for each kind of cell you have use.
refer this https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableViewCell_Class/#//apple_ref/occ/instp/UITableViewCell/reuseIdentifier
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.
I have a custom UITableViewCell that has data that I get from the server. I set the reuseIdentifier of the cell when the data comes in. Everything works fine, until I do a pull down to refresh and get new/updated data. The identifier from the server is the same, but the data may be different (which is an expected result in this case). When this happens I need to re-create the cells, and keep the same reuseIdentifier. I know that not setting the reuseIdentifier is one way around this, but that's a very bad idea, that hurts performance.
I've looked at plenty of question here involving reuseIdentifier's, but none of them seam to answer my question.
Thank you for any insight you have!
There's no need to clear the reuseIdentifier. Simply reload the table view after setting up your new data. All visible cells will be reloaded. Using the same reuseIdentifier is fine. As long as your cellForRowAtIndexPath method is using the new data to populate each cell, you will get the desired results.
Update - The comment by mkral is a good clarification. The reuseIdentifier represents the type of cell, not the data. So the reuseIdentifier should have nothing to do at all with the identifier from the server unless the server's identifier affects the type of cells being shown.
I'm having trouble understanding how this works. I've read many threads on SO about it - such as UITableView dequeueReusableCellWithIdentifier Theory and How does dequeueReusableCellWithIdentifier: work?.
However, my UITableView succesfully dequeues a cell each time (it's never nil), even when it first loads. I was under the impression that similar cells should use the same identifier, so you only have to change what's necessary.
Because
if (!cell) {
NSLog(#"New cell");
cell = [[UITableViewCell alloc] initWithStyle:someStyle reuseIdentifier:someIdentifier];
}
Never gets called, I'm not sure how I'm supposed to handle cells in the same table with different styles, because the style can only be set in the initializer.
I also tried using different cell identifiers, to make sure it wasn't reusing cells from a different table or something. I am registering these identifiers with [tableView registerClass: forCellReuseIdentifier:]
If I understand, this method should only return previously created cells that have been moved off the screen (hidden, i.e. can be reused). Then how come it returns a cell the first time it's called?
Edit: So the confusion was using [tableView dequeueReusableCellWithIdentifier: forIndexPath:] instead of [tableView dequeueReusableCellWithIdentifier:] (the first requires registering the identifier, the second will return nil if none is available - the behavior I was expecting above).
However, I noticed that when I changed my code to use [tableView dequeueReusableCellWithIdentifier:], it creates a new cell, and its contentView.frame has a width of 320 (full width). Before, when I did dequeue...forIndexPath it would give a width of 302, or the visual/"real" width of the cell. Why is this?
Also, is there a way to specify the style of the UITableViewCells regstiered for reuse?
Solution: So I found this thread UITableView cell.contentView.bounds.size.width Changes With Cell Reuse, which says when you set the autoresizingmask to UIViewAutoresizingFlexibleLeftMargin, it's fixed when you try to do relative positioning (the contentView width is initially the fully width, but when you present it it's shrunk, so if you do your calculations right it'll still show up properly).
I was positioning a UISwitch on the right - and when I set the autoresizing mask it works when it's first displayed but shifted over another ~20 pixels when I switched it. I don't know what caused that extra shift, but I ended up solving it by simply setting the UISwitch as the cell's accessoryView.
(This is partially off topic from the original question, but if someone stumbles on this maybe it'd be useful). For anyone wondering specifically about the original question, the answer is under the first edit.
When you call [tableView registerClass: forCellReuseIdentifier:], you're teaching the table view what to do when you later use the specified ReuseIdentifier. So, when you later call [tableView dequeueReusableCellWithIdentifier:] it will either:
A. Get a cell that has previously been created and isn't currently being used
OR
B. Create a new cell of the class you specified
So, when you dequeue, you will always get an instance. If you want to create new cell instances yourself with initWithStyle:reuseIdentifier: then you shouldn't register a class with the table view. Alternatively, leave the registration and add logic to specify everything that needs to be configured (and consider using multiple different cell classes and reuse identifiers).
because the first time the cell is nil that is why this gets called:
if (!cell) {
NSLog(#"New cell");
cell = [[UITableViewCell alloc] initWithStyle:someStyle reuseIdentifier:someIdentifier];
}
but then if the cell is already ready for reuse and basically its not nil - it returns the cell and it does not hit the above if statement
From the apple docs at https://developer.apple.com/library/ios/#documentation/UIKit/Reference/UITableView_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006943
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.
The dequeue method will
Return a recycled cell if one is available
Create a new cell if you registered one (you mentioned you did this)
If none of these are true, it returns nil
I'm guessing if you remove the registration (which may be hidden in a xib) then you will get the nil result.
if you see UITableView.h
Beginning in iOS 6, clients can register a nib or class for each cell.
If all reuse identifiers are registered, use the newer -dequeueReusableCellWithIdentifier:forIndexPath: to guarantee that a cell instance is returned.
Instances returned from the new dequeue method will also be properly sized when they are returned.
(void)registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(5_0);
(void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0);