Jeff V
Jeff V

Reputation: 581

Adjusting NSLayoutConstraint per cell on UITableView

I have a UITableViewController, Grouped style, with a custom UITableViewCell. In the UITableViewCell I have NSLayoutConstraints, defined in a storyboard (on the prototype cell), that dictate the size and positioning of a UIImageView. These NSLayoutConstraints are linked to properties of the UITableViewCell subclass.

In the -(void)layoutSubviews method of the subclassed UITableViewCell, I'm attempting to use

self.topSpacingConstraint.constant = 0;

to change the arrangement on a per-cell basis.

That line of code adjusts the constraint for all of my cells, though. I don't understand why, and I don't know how to set the constraint for a single cell.

Update

Here's the full code for the -(void)layoutSubviews method of the UITableViewCell subclass. self.topCell and self.bottomCell are properties set by the UITableViewController while configuring the cell. Based on those values, I'm trying to update the AutoLayout spacing of an image on a per-cell basis:

- (void)layoutSubviews
{
    [super layoutSubviews];

    UIImage *mask;

    if (self.topCell && self.bottomCell) {
        self.imageSpacingTopConstraint.constant = 1;
        self.imageSpacingBottomConstraint.constant = 2;
        mask = MTDContextCreateRoundedMask(self.wineImage.bounds, 6.0, 0.0, 6.0, 0.0);
    } else if (self.topCell) {
        self.imageSpacingTopConstraint.constant = 1;
        self.imageSpacingBottomConstraint.constant = 0;
        mask = MTDContextCreateRoundedMask(self.wineImage.bounds, 6.0, 0.0, 0.0, 0.0);
    } else if (self.bottomCell) {
        self.imageSpacingTopConstraint.constant = 0;
        self.imageSpacingBottomConstraint.constant = 2;
        mask = MTDContextCreateRoundedMask(self.wineImage.bounds, 0.0, 0.0, 6.0, 0.0);
    } else {
        self.imageSpacingTopConstraint.constant = 0;
        self.imageSpacingBottomConstraint.constant = 0;
        mask = MTDContextCreateRoundedMask(self.wineImage.bounds, 0.0, 0.0, 0.0, 0.0);
    }

    CALayer *layerMask = [CALayer layer];
    layerMask.frame = self.wineImage.bounds;
    layerMask.contents = (id)mask.CGImage;
    self.wineImage.layer.mask = layerMask;
}

My need to do this stems from the inconsistent height of cells on the table with Grouped style. The top and bottom cell are 1pt taller than 'middle' cells, while a single cell in the table is 2pts taller. Using the same spacing constraints for the image in all cells results in the image partially obscuring the cell borders for some cells, or leaving whitespace between the image and the border in others.

If anyone can think of an easier way to attack this, I'd appreciate it.

Solution

Per the marked solution below, removing the image spacing constraints and then creating new constraints between the image and the cell.contentView did the trick. I removed the code to adjust the constraints from the -(void)layoutSubviews method. I added the code to remove and make new constraints to awakeFromNib of the UITableViewCell subclass:

- (void)awakeFromNib
{
    [super awakeFromNib];

    // Since Xcode 4.6.3 creates constrain relationships between the image and the cell, we need to delete and then recreate between the image and the cell.contentView

    [self removeConstraint:self.imageSpacingTopConstraint];
    [self removeConstraint:self.imageSpacingBottomConstraint];

    self.imageSpacingTopConstraint = [NSLayoutConstraint constraintWithItem:self.wineImage attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0];
    self.imageSpacingBottomConstraint = [NSLayoutConstraint constraintWithItem:self.wineImage attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0];

    [self addConstraint:self.imageSpacingTopConstraint];
    [self addConstraint:self.imageSpacingBottomConstraint];
}

Upvotes: 0

Views: 2692

Answers (2)

rob mayoff
rob mayoff

Reputation: 385540

One problem is you're updating your constraints in layoutSubviews, but layoutSubviews runs after auto layout. You should update your constraints in updateConstraints instead.

Another problem is Xcode, through at least version 4.6.3, is buggy. Your image view is a subview of the cell's contentView, and constraints should be between the image view and the contentView. But in nibs and storyboards, Xcode incorrectly sets the constraints between the image view and the cell. (Note: this problem was fixed in Xcode 5 so you probably don't need to worry about it if you are reading this unless you are a time traveler.)

If you poke around in your view hierarchy in the debugger, you'll discover that the cells have varying heights, but their content views all have the same height. Here are the default sizes:

Section Size  Row Position  Cell Height   contentView Height

     1            sole          46              43

     2            first         45              43
     2           second         45              43

     3+           first         45              43
     3+          middle         44              43
     3+           last          45              43

Thus when you rely on constraints in your nib/storyboard to position your cell's subviews, you end up with problems.

The simplest solution is to just write code (in your cell's awakeFromNib) that deletes the constraints from the nib/storyboard, and creates new constraints between the contentView and the image view (and any other subviews of the contentView.) This should eliminate the need to modify the constraint constants based on the cell's position within its section.

Upvotes: 2

Mundi
Mundi

Reputation: 80265

There is no conditionality in your code. Therefore all cells are changed.

Now that you have added the code, there are 2 possible reasons that come to mind:

  1. The if statement will never produce a 0 constant if topCell is not nil. That does not seem entirely logical.

  2. The layer section below the if statement could potentially change the expected layout again.

Upvotes: 0

Related Questions