1

I have a UITableViewSubclass (style: UITableViewCellStyleValue1) that has a custom label that is essentially a replacement for the normal textLabel label. It should be left aligned with the normal textLabel but has a width to end 8 points to the left of the normal detailTextLabel. I get the desired effect, but when the cells are used, an exception is thrown about being unable to satisfy the constraints simultaneously.

What I don't understand is why it complains, there is no apparent conflict. Calling [self layoutIfNeeded] after setting up the constraints silences the exception. Alternatively, lowering the priority of the constraint that configures the trailing property (for example, UILayoutPriorityDefaultHigh instead of the default UILayoutPriorityRequired) also silences the exception, obviously because I'm informing the Auto Layout engine that it's okay to break/ignore that constraint.

I'm assuming that the standard UITableView implementation maybe oddly lays out the textLabel and textLabel in a fashion that makes it initially impossible to describe the view described. For example, if the textLabel was actually placed on the right side of the cell and detailTextLabel was placed on the left side, then the layout I've described would be impossible.

When does Auto Layout take effect, in the context of this scenario. Is it jumping the gun and attempting to layout things before it's supposed to?

No Storyboards or XIBs are being used. Purely code based.

#import "EBTStoreTableViewCell.h"

@interface EBTStoreTableViewCell ()
@property (nonatomic, readonly, weak) UILabel *storeNameLabel;
@end

@implementation EBTStoreTableViewCell

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuseIdentifier];
    if (self) {
        self.textLabel.text = @" ";

        self.detailTextLabel.text = @" ";

        UILabel *storeNameLabel = [[UILabel alloc] init];
        storeNameLabel.translatesAutoresizingMaskIntoConstraints = NO;
        [storeNameLabel setContentCompressionResistancePriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal];
        [self.contentView addSubview:storeNameLabel];
        _storeNameLabel = storeNameLabel;

        [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:storeNameLabel attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.textLabel attribute:NSLayoutAttributeLeading multiplier:1.0f constant:0.0f]];
        [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:storeNameLabel attribute:NSLayoutAttributeBaseline relatedBy:NSLayoutRelationEqual toItem:self.textLabel attribute:NSLayoutAttributeBaseline multiplier:1.0f constant:0.0f]];

        [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:storeNameLabel attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.detailTextLabel attribute:NSLayoutAttributeLeading multiplier:1.0f constant:-8.0f]];

//        [self layoutIfNeeded]; // doing this makes the issue go away
    }
    return self;
}

// Setters ommited

@end

This the exception message I get:

2014-04-23 11:50:42.092 Application[32507:60b] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0xde03910 UILabel:0xde036b0.leading == UILabel:0xde00590.leading>",
    "<NSLayoutConstraint:0xde03a30 UILabel:0xde036b0.trailing == UITableViewLabel:0xde00c10.leading - 8>",
    "<NSAutoresizingMaskLayoutConstraint:0xde01920 h=--& v=--& UILabel:0xde00590.midX ==>",
    "<NSAutoresizingMaskLayoutConstraint:0xde1de50 h=--& v=--& H:[UILabel:0xde00590(0)]>",
    "<NSAutoresizingMaskLayoutConstraint:0x17f50a10 h=--& v=--& UITableViewLabel:0xde00c10.midX ==>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0xde03a30 UILabel:0xde036b0.trailing == UITableViewLabel:0xde00c10.leading - 8>

Break on objc_exception_throw to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
Neil
  • 1,813
  • 1
  • 12
  • 20
  • I'm having the same issue, the fact that such a simple thing can cause such a headache just makes me sick of iOS SDK. – dreamzor Sep 17 '14 at 11:10
  • UITableView has outlived its usefulness. I'd rather use UICollectionView in all places where a table is needed instead - it receives much more love from the Apple support team apparently, this includes autolayout support – Igor Vasilev Jul 28 '20 at 14:55

1 Answers1

3

You shouldn't mix and match using the textLabel and detailTextLabel. These can not be constrained as their size are managed internally and might produce unexpected results.

You should create your own labels, and then places constraints on them.

From the docs for UITableViewCell:

When creating cells, you can customize them yourself or use one of several predefined styles. The predefined cell styles are the simplest option. With the predefined styles, the cell provides label and image subviews whose positions and styling are fixed. All you have to do is provide the text and image content to go into those fixed views. To use a cell with a predefined style, initialize it using the initWithStyle:reuseIdentifier: method or configure the cell with that style in Xcode. To set the text and images of the cell, use the textLabel, detailTextLabel, and imageView properties.

If you want to go beyond the predefined styles, you can add subviews to the contentView property of the cell. When adding subviews, you are responsible for positioning those views and setting their content yourself.

Community
  • 1
  • 1
Rich
  • 8,108
  • 5
  • 46
  • 59