Contentview Not Indenting in iOS 6 Uitableviewcell Prototype Cell

contentView not indenting in iOS 6 UITableViewCell prototype cell

On further investigation (viewing the subview hierarchy of the cell) Interface Builder does place subviews within the cell's contentView, it just doesn't look like it.

The root cause of the issue was iOS 6 autolayout. When the cell is placed into editing mode (and indented) the contentView is also indented, so it stands to reason that all subviews within the contentView will move (indent) by virtue of being within the contentView. However, all the autolayout constraints applied by Interface Builder seem to be relative to the UITableViewCell itself, rather than the contentView. This means that even though the contentView indents, the subviews contained within do not - the constraints take charge.

For example, when I placed a UILabel into the cell (and positioned it 10 points from the left-hand side of the cell) IB automatically applied a constraint "Horizontal Space (10)". However, this constraint is relative to the UITableViewCell NOT the contentView. This means that when the cell is indented, and the contentView moves, the label stays put as it is complying with the constraint to remain 10 points from the left-hand side of the UITableViewCell.

Unfortunately (as far as I am aware) there is no way to remove these IB created constraints from within IB itself, so here is how I solved the problem.

Within the UITableViewCell subclass for the cell, I created an IBOutlet for that constraint called cellLabelHSpaceConstraint. You also need an IBOutlet for the label itself, which I called cellLabel. I then implemented the -awakeFromNib method as per below:

- (void)awakeFromNib {

// -------------------------------------------------------------------
// We need to create our own constraint which is effective against the
// contentView, so the UI elements indent when the cell is put into
// editing mode
// -------------------------------------------------------------------

// Remove the IB added horizontal constraint, as that's effective
// against the cell not the contentView
[self removeConstraint:self.cellLabelHSpaceConstraint];

// Create a dictionary to represent the view being positioned
NSDictionary *labelViewDictionary = NSDictionaryOfVariableBindings(_cellLabel);

// Create the new constraint
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|-10-[_cellLabel]" options:0 metrics:nil views:labelViewDictionary];

// Add the constraint against the contentView
[self.contentView addConstraints:constraints];

}

In summary, the above will remove the horizontal spacing constraint which IB automatically added (as is effective against the UITableViewCell rather than the contentView) and we then define and add our own constraint to the contentView.

In my case, all the other UILabels in the cell were positioned based upon the position of the cellLabel so when I fixed up the constraint/positioning of this element all the others followed suit and positioned correctly. However, if you have a more complex layout then you may need to do this for other subviews as well.

Indentation not working on Custom UITableViewCell

Indentation isn't the same as adjusting the content view when entering edit mode.

You're right, this seems simple and usually works almost straight out of the box. Typically the only things you need to change are the autoresizing masks of the components you add to the cell. Content on the left should have a fixed left margin, content on the right should have a fixed right margin.

When the cell enters editing mode the content view's size is adjusted to allow room for the editing accessories. If your content has these resizing masks, it will move along with this. Your layoutSubviews method above is quite likely undoing all this by setting the content view back to full width because you are using indentation incorrectly - though I can't be sure of that from the information in the question.

UPDATE

This is a issue (bug?) with constraints in UITableViewCells. You can go back to autoresizing masks by selecting the "File" tab in the storyboard / interface builder screen (if you want to use constraints elsewhere, use a standalone nib for the cell) and unchecking "Use Autolayout":

Sample Image

This arranges your cell content properly.

UITableViewCell editing indentation

Simple way is in your custom cell class - in layoutSubviews method adjust your cell.view.frame

if (self.isEditing) {

// then shift cell frame to left side
}
else {

// then shift cell frame to right side
}

Reusable UITableView's prototype cell

I don't think you can create a prototype cell and share it between tables in a storyboard, but you can create a prototype cell in a nib and then load that in the ViewDidLoad method and then use that in your table view. It is really quite simple, here is how...

A. add the nib file:

1. Select New File...
2. Select IOS -> User Interface

3. Select "Empty" -> this will add a new file .xib file to your project

4. Drag a UITableViewCell from the object browser into your xib file and customize to your liking

5. Use the Utilities pane to change properties -> editing a nib is very
similar to editing a storyboard.

6. Make sure you name your cell - I chose the name cellFromNib, but you will probably want something else.

B. Load the UITableViewCell in each table's viewDidLoad method:

- (void)viewDidLoad
{
// load the cell in the nib - the nib can only contain this one UITableViewCell
[self.tableView
registerNib:[UINib nibWithNibName:[self @"nibFileNameWithoutExtension"]
bundle:[NSBundle mainBundle]]
forCellReuseIdentifier:[self @"cellFromNib"]];
}

C. De-queue the nib's tableViewCell...

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellFromNib" forIndexPath:indexPath];
// customize your cell here...
}

D. Add a "dummy" prototype cell to your TableView in your storyboard.
Make a segue from this "dummy" cell to the view you want displayed when the cell is selected - make sure to name the segue - I'll call it @"theSegue" for this example. You will reference this segue in your code.

E. Finally, add code to segue from that cell...

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// this is basically what the storyboard does under the hood...
// make sure you have a "dummy" prototype cell with this segue name for each
// tableview that uses this cell
[self performSegueWithIdentifier:@"theSegue" sender:self];
}

If you want to specialize your cell code, create a class that subclasses UITableViewCell

I think that is everything you need.

I would say do not be afraid of doing something like this because, if you are serious about IOS programming, you will learn something new. It really does make for much better reusable code.

indentationLevelForRowAtIndexPath not indenting custom cell

Yeah, it seems like custom table cells don't do this automatically? You need to override the layoutSubviews method in the table cell class. See this question for how to do this.

This code worked perfectly for me (although be careful if you are setting a custom height w/ the delegate as well, they seem to interfere with each other):

- (void)layoutSubviews
{
[super layoutSubviews];
float indentPoints = self.indentationLevel * self.indentationWidth;

self.contentView.frame = CGRectMake(
indentPoints,
self.contentView.frame.origin.y,
self.contentView.frame.size.width - indentPoints,
self.contentView.frame.size.height
);
}

Edit for iOS8 and later

The above does work for me on iOS, but it causes subtle bugs when trying to autosize the height of the cell as well. There is n easier solution: If you have autolayout turned for the cell just set the left margin of the contentView:

override func layoutSubviews() {
super.layoutSubviews()
self.contentView.layoutMargins.left = CGFloat(self.indentationLevel) * self.indentationWidth
self.contentView.layoutIfNeeded()
}


Related Topics



Leave a reply



Submit