How to make a UITableViewCell with different subviews reusable?

Instead of adding subviews to cells I'd suggest that you create for every kind of cell your own class. If you have the kinds: train, car, bike, boat and airplane I would create five subclasses.

Instead of adding subviews to cells I'd suggest that you create for every kind of cell your own class. If you have the kinds: train, car, bike, boat and airplane I would create five subclasses. As I understand Apple the reuse mechanism with the identifier is just for that case: different types of cells get their own identifier, not every single cell a special one.

Just to point how I interprete the whole thing. In Apple's Table View Programming Guide for iOS / Characteristics of Cell Objects, the 3rd paragrpah delivers some insight into the meaning of the reuse identifier. I've written myself a small TableViewCellFactory class which makes my life easier to create cells with the interface builder and have those in my app within minutes.

First of all a small example on how to use cellForRowAtIndexPath and the factory as well as setting content for a cell. I create a fresh cell with the factory which needs the tableView so it can handle the reuse logic. Next thing is to let a method fill in the content for the cell.In this case it's a cell which shows a video clip with some text.

Data Source delegate method and helper - (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)anIndexPath { VideoClipTableViewCell *cell = TableViewCellFactory videoClipTableViewCellWithTableView:aTableView; self configureVideoClipCellWithCell:cell andIndexPath:anIndexPath; // code to decide what kind of cell not shown, but it could be here, just move the model // access code from the configure cell up here and decide on what you get return cell; } Next comes the data source helper to put content into the cell. Get the content from my model array and set the properties. Note, this does everything by reference, nothing is returned.

- (void)configureVideoClipCellWithCell:(VideoClipTableViewCell *)aCell andIndexPath:(NSIndexPath *)anIndexPath { VideoClip *videoClip = videoClips objectAtIndex:anIndexPath. Row; aCell.videoTitleLabel. Text = videoClip.

Title; aCell.dateLabel. Text = videoClip. Date; // more data setting ... } TableViewFactory This class consists mainly of convenience methods and some boilerplate methods to do the real work.

// Convenience static method to create a VideoClipTableViewCell + (VideoClipTableViewCell *)videoClipTableViewCellWithTableView:(UITableView *)aTableView { return self loadCellWithName:@"VideoClipTableViewCell" tableView:aTableView; } // method to simplify cell loading + (id)loadCellWithName:(NSString *)aName tableView:(UITableView *)aTableView { return self loadCellWithName:aName className:aName identifier:aName tableView:aTableView; } // method with actually tries to create the cell + (id)loadCellWithName:(NSString *)aName className:(NSString *)aClassName identifier:(NSString *)anIdentifier tableView:(UITableView *)aTableView { UITableViewCell *cell = aTableView dequeueReusableCellWithIdentifier:anIdentifier; if (cell == nil) { UINib * nib = UINib nibWithNibName:aName bundle:nil; NSArray * nibContent = nil; nibContent = nib instantiateWithOwner:nil options:nil; for (id item in nibContent) { if (item isKindOfClass:NSClassFromString(aClassName)) { cell = item; } } } return cell; } I've thrown out the whole error and exception handling just to keep the example short. If someone is interested I'd add the code. Some important things about the usage is: The connected class name, the reuse identifier and the nib name are all the same so a cell can be created with only one string constant, else the long loadCellWithName has to be used.

Don't forget to set the reuse identifier in interface builder. The nib should contain only one TableViewCell (can be changed with some coding though) Don't set outlets of the File's Owner, use those of the tableViewCell Set the class identity of the cell to a corresponding class which should be created foremost Look at the screenshot Thoughts on subclassing own custom cells It's actually easy to subclass your own cell, add a few properties to it, make them available in IB with outlets, choose the new extended class in IB for your nib file. The main problem is interface itself.

It's not easily done to have different kinds of cells based on a custom cell in interface builder. The first approach would be to copy the nib file, rename it and use it with all the existing references and link the new ones to differing outlets. But what happens if the base cell has to be changed?

Going through all kinds of inheriting cells could be a tedious task. I just stumbled across Custom views in Interface Builder using IBPlugins on Cocoa with Love.It's a nice tutorial how to extend the components Library in IB. Our custom base cell could become an item in the library and become the template we've been looking for.

I think this approach is the right way to choose. Still, looking at necessary steps, it's not just done within 5 minutes. Interface builder is a helpful tool, allowing us to rapidly create views, but when it comes to reusability through subclassing, there are big steps necessary to create maintainable views.

A pity. Creating the views with code only I think one is better off with subclassing if it comes to more than one level of inheritance or many ancestor classes for just one base view. EDIT On the other hand, Apple warns about excessive use of subviews in a cell: However, if the content of a cell is composed of more than three or four subviews, scrolling performance might suffer.

In this case (and especially if the cell is not editable), consider drawing directly in one subview of the cell’s content view. The gist of this guideline is that, when implementing custom table-view cells, be aware that there is a tradeoff between optimal scrolling performance and optimal editing or reordering performance. Right now any approach has its drawbacks and advantages: Too man subviews will hit performance, easily done with IB Drawing with code will result in a hard to maintain code base but will perform better Skipping IB makes subclasssing of template cell classes easier erarchy through subclassing difficult to achieve with IB with nib files.

Thanks Nick, this is quite an impressive answer. Still, I see one issue: The Cell I called "MyCell" contains quite a few views (besides the changing one) and also quite a lot of code. I would have to copy/paste the views into every different cell (boat, train...) since there is no way of inheriting the elements of my "MyCell" or am I mistaking?

– Phlibbo Apr 21 at 23:09 @Phlibbo I did some testing and reading on it so I've added what I have found out. – Nick Weaver Apr 22 at 0:15.

There are a couple of different ways to do this. You need a way to access that subview and reset or change it on reuse. You could subclass UITableViewCell with your own class of cell that has a property for the train or car view.

That way you could access and change that view when the cell is reused. Assign a different identifier to each type of cell: ` - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CarCellIdentifier = @"CarCell"; static NSString *TrainCellIdentifier = @"TrainCell"; if(indexPath == carCellNeeded) { UITableViewCell *cell = tableView dequeueReusableCellWithIdentifier:CarCellIdentifier; if (cell == nil) { cell = UITableViewCell alloc initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CarCellIdentifier autorelease; cell addSubview:carView; } } else if(indexPath == trainCellNeeded){ UITableViewCell *cell = tableView dequeueReusableCellWithIdentifier:TrainCellIdentifier; if (cell == nil) { cell = UITableViewCell alloc initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TrainCellIdentifier autorelease; cell addSubview:trainView; } } return cell; } Or assign a special tag to that sub view you are adding and when the cell comes back around again to be reused you can access that specific subview by its tag.

Thanks for your response. One question: Would your way of assigning different identifiers still work if I have my own Subclass ob UiTableViewCell with it's own xib? I'm not sure about that because I defined the Identifier in the xib file.

– Phlibbo Apr 21 at 23:16 You would just use an initWithNib instead of initWithStyle. You would still have the dequeueReusableCellWithIdentifier line. Just make sure that the identifier you specify in the nib and in the code are the same.

– Dancreek Apr 23 at 16:22.

I would add both custom subviews to the nib and connect them to an outlet. And depending on the content I would hide one of them when you configure the content of your cell. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"CellIdentifier"; UITableViewCell *cell = tableView dequeueReusableCellWithIdentifier:CellIdentifier; if (!cell) { cell = /* load from nib */ } if (/*indexPath conditionForTrainCell*/) { cell.trainSubview.carSubview.

// configure train cell } else { cell.trainSubview. CarSubview. // configure car cell } return cell; }.

Thank you, this in fact would be a solution. Unfortunately, some subviews are backed by quite a lot of code which I would prefer to keep separated. Could I somehow do that while using your solution?

– Phlibbo Apr 21 at 16:42 Depends how you do the separation right now. But I don't see a problem. In theory you could just replace the adding and removal of the subview with the unhiding and hiding of it.

– fluchtpunkt Apr 21 at 16:51 I know it is a stupid question, but if I have a . Xib with a customized UITableViewCell, how do I insert a custom view for which I have a . Xib and code files?

I thought it's possible but just can't find it. Thanks! – Phlibbo Apr 21 at 23:20 Add an UIView object in the cell nib and change it's class (In the identity tab of the inspector) to your custom class.

Then you'll need a IBOutlet in the cell subclass so you can connect them. – fluchtpunkt Apr 210 at 1:18.

I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.

Related Questions