- (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
Related
In my setup, I have 3 tableViews (nested) - tableView inside a tableView cell inside a tableVIew cell.
My goal is to find out touch events on the “mid-level” tableView. When a mid-level tableView is touched, I would like to extend its and its parent tableView height to predetermined values.
I created a custom UITableView with a second tag property (“secondTag”) and inside the tableView methods, I cast tableView to CustomTableView. So, inside tableView didSelectRowAt I'll had two indexes - IndexPath and secondTag. The first one was theIndexPath of the mid-level tableView and the second one was the custom UITableView tag that used for the indexing of the “parent” tableView.
But despite setting the cell identifier properly (inside the storyboard) I got the following error: "unable to dequeue a cell with identifier middleCell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard".
One more note: inside tableView didSelectRowAt I used reloadData() on both the mid-level tableView cell and its parent tableView cell, since I needed both of them (sub/parent tableView cells) to change their height after a touch has occurred.
I think the double reloading may be the cause of my problems but I must reload both the parent and its nested “mid-level” tableView since I need both their heights to change after a touch was made.
*This question comes in to continue the following one: Need help adding new stored property to UITableView
I don't totally understand what registering a class for cell reuse does. I understand how we use reuse identifiers on cells, I just don't understand what calling this method in viewDidLoad does. Looked at a bunch of docs. Not clicking, n00b here. Could someone give me some tips on what it does please?
TableViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
// Register Class for Cell Reuse Identifier
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:CellIdentifier];
}
You have a UITableView. It has a datasource that provides it UITableViewCells. To save memory and processor cycles, it unloads UITableViewCells that are no longer on screen and puts them into a reuse queue. When it loads a new cell, the datasource will typically ask the UITableView for a cell from this reuse queue. If the queue is currently empty, UITableView will construct a new UITableViewCell using the class provided. The reuseIdentifier is used to distinguish this particular cell type queue from another cell type queue within the same UITableView.
Something like this:
UITableView: "Hey, Datasource! Give me the cell for this indexPath."
Datasource: "Alright. That's a 'foo' kind of cell. Got any of those kicking around that you're not using?"
No class registered; reuse cells returned from datasource previously
UITableView: "Yes, I do. Here you go."
No class registered; no cells available
UITableView: "Nope. Hey, I don't have a class registered for that kind of cell. Hmm. Here's nil instead."
Class registered; reuse cells returned from datasource previously
UITableView: "Yes, I do. Here you go."
Class registered; no cells available
UITableView: "Nope. But I have a class registered for that identifier. Here's a new instance."
Registering a class is only needed if you use custom TableViewCells classes If you're using storyboards you can also do this in the Interface Builder.
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
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);
I'm trying to figure out how to have a few static cells in addition to dynamic cells (I think) but when I start a new Master-Detail app and switch the default Dynamic Prototypes to Static Cells, it crashes.
I think, since I'm still new at this, that I don't understand how all of the components (table views, cells, delegates) are wired. After I switched to Static Cells I made three cells but they don't show up. The app just crashes.
I can include code but basically, I create a new Master-Detail, switch to Static Cells, change the name of three cells and it crashes (in the main loop).
Thanks ahead for everything.
If I understand your question, this is the explanation you are looking for:
Prototype (reusability):
When you use Dynamic cells, you use Prototype cells. That means either in IB or in your code, you create an instance of UITableViewCell, and give it an identifier (reuseIdentifier). The UITableView uses this prototype to generate as many cells (rows) as you need. UITableView create (and use memory) for as many cells as it needs to fill the screen. Once some of these cells go off the screen, UITableView reuses them -- recycles them.
You can define more than one prototype cell in a UITableView. The idea is that each prototype serves a different purpose. For example, you define a prototype cell that has only one big UILabel and its purpose is to use static text. You define another prototype that has only one UIImageView. It depends on your design and how you want to display your data.
To use prototype cells, in IB, you use Dynamic Prototypes cells, and set an identifier. Then you have to implement UITableViewDataSource methods, such as:
– tableView:numberOfRowsInSection:
– tableView:cellForRowAtIndexPath:
These methods are delegate methods of UITableView.
If not using IB, you would create cells in – tableView:cellForRowAtIndexPath: method:
UITableViewCell *cell = [[UITableViewCell alloc]
initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString
*)reuseIdentifier];
Static:
The idea of static cells is just simplicity. You can use the prototype cells (e.g. only one prototype) and feed in a static NSArray, for example, as data source. To take the task of implementing UITableViewDataSource methods, Apple introduced static cells, where you would only use the storyboard and IB. In IB, you select the table view, and choose Static Cells instead, and type in your text, or set the image.
You don't want to make any dynamic changes to static cells later when the app is running because it is not meant to, and you would have to implement many more methods that it is not worth it. Although it not much of a work to get the indexPath of the selected row, but the idea is if you need to push a new view from selection of a static cell, you would use segues, instead of implementing any code.
When using static cells, you should not implement UITableViewDataSource method, otherwise your app crashes, and vice versa, if you do not implement UITableViewDataSource methods (required ones) when using prototype cells, your app crashes.