Setting style of UITableViewCell when using iOS 6 UITableView dequeueReusableCellWithIdentifier:forIndexPath:

UitableviewIos6

Uitableview Problem Overview


I'm trying to work out how to set the UITableViewCellStyle when using the new methods in iOS 6 for UITableView.

Previously, when creating a UITableViewCell I would change the UITableViewCellStyle enum to create different types of default cells when calling initWithStyle: but from what I can gather, this is no longer the case.

The Apple documentation for UITableView states:

> Return Value: A UITableViewCell object with the associated reuse identifier. This method always returns a valid cell.

> Discussion: For performance reasons, a table view's data source should generally reuse UITableViewCell objects when it assigns cells to rows in its tableView:cellForRowAtIndexPath: method. A table view maintains a queue or list of UITableViewCell objects that the data source has marked for reuse. 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 based on the class or nib file you previously registered.

> Important: You must register a class or nib file using the registerNib:forCellReuseIdentifier: or registerClass:forCellReuseIdentifier: method before calling this method.

> If you registered a class for the specified identifier and a new cell must be created, this method initializes the cell by calling its initWithStyle:reuseIdentifier: method. For nib-based cells, this method loads the cell object from the provided nib file. If an existing cell was available for reuse, this method calls the cell’s prepareForReuse method instead.

This is how my new cellForRowAtIndexPath looks after implementing the new methods:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
	static NSString *cellIdentifier = @"cell_identifier";
	
	[tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:cellIdentifier];
	
	UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
	
	return cell;
}

The code I have so far works fine but always returns the default style. How can I change this so I can create cells with the other styles such as UITableViewCellStyleDefault, UITableViewCellStyleValue1, UITableViewCellStyleValue2 and UITableViewCellStyleSubtitle?

I don't want to subclass UITableViewCell, I just want to change the default type as I could do prior to iOS 6. It seems odd that Apple would provide enhanced methods but with minimal documentation to support their implementation.

Has anyone mastered this, or run in to a similar problem? I'm struggling to find any reasonable information at all.

Uitableview Solutions


Solution 1 - Uitableview

I know you said you didn't want to create a subclass, but it looks inevitable. Based on the assembly code while testing in the iOS 6.0 simulator, UITableView creates new instances of UITableViewCell (or its subclasses) by performing

[[<RegisteredClass> alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:<ReuseIdentifier>]

In other words, the style sent (UITableViewCellStyleDefault) appears to be hard-coded. To get around this, you will need to create a subclass that overrides the default initializer initWithStyle:reuseIdentifier: and passes the style you wish to use:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    // ignore the style argument, use our own to override
    self = [super initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuseIdentifier];
    if (self) {
        // If you need any further customization
    }
    return self;
}

Also, it might be better to send registerClass:forCellReuseIdentifier: in viewDidLoad, instead of doing it every time a cell is requested:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.tableView registerClass:<RegisteredClass> forCellReuseIdentifier:<ReuseIdentifier>];
}

Solution 2 - Uitableview

dequeueReusableCellWithIdentifier isn't deprecated so you aren't required to use the new dequeueReusableCellWithIdentifier:forIndexPath:.

Use the new way along with the appropriate register method (in viewDidLoad) if you are using a custom cell class but use the old way if you want to use one of the UITableViewCellStyle enums.

Solution 3 - Uitableview

You can avoid an extraneous subclass by using the storyboard interface builder:

  1. In the Storyboard view, select the table view cell prototype cell (on the table view)
  2. In the Utilities view, in the Attributes inspector, modify the Style value
  3. (Optionally) Modify other values such as Selection and Accessory

The new iOS 6.0 dequeueReusableCellWithIdentifier:forIndexPath: does use those values when allocating new cells and returning them. (Tested on an iOS 6.0 compilation using Xcode 4.5.2)

Solution 4 - Uitableview

Another alternative that saves one file is to create a Nib and use registerNib:forCellReuseIdentifier: instead.

Making the Nib is easy: Create a new .xib file in Interface Builder. Delete the default view. Add a Table View Cell object. Using the Attributes Inspector, change the style for the cell. (Here you also have the opportunity to customize the cell further by adjusting other attributes.)

Then in your table view controller's viewDidLoad method call something like:

[self.tableView registerNib:[UINib nibWithNibName:@"StyleSubtitleTableCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"Cell"];

Solution 5 - Uitableview

Bolot's answer is the correct. Simple and you don't need to create any XIB file.

I just wanted to update his answer for whoever is doing it using Swift instead of Objective-C:

override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: .value1, reuseIdentifier: reuseIdentifier)
}
    
required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

Solution 6 - Uitableview

My solution to this is to call initWithStyle: reuseIdentifier: after I've obtained it using [self.tableView dequeueReusableCellWithIdentifier:@"cellId" forIndexPath:indexPath]. After all, init is just another selector, and the compiler makes no restrictions on calling it on an already initialised object. It will however complain about not using the result of calling init, so I do:

UITableViewCell* cell = [self.tableView dequeueReusableCellWithIdentifier:@"cellId" forIndexPath:indexPath];
cell = [cell initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cellId"];

I imagine this won't work in Swift...

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionZack BrownView Question on Stackoverflow
Solution 1 - UitableviewbolotView Answer on Stackoverflow
Solution 2 - UitableviewMurray SagalView Answer on Stackoverflow
Solution 3 - UitableviewColliertonView Answer on Stackoverflow
Solution 4 - UitableviewMr. BernaView Answer on Stackoverflow
Solution 5 - UitableviewGabriel OlivaView Answer on Stackoverflow
Solution 6 - UitableviewChrisView Answer on Stackoverflow