Why does an orientation change AutoLayout for my UITableViewCell?

127 views Asked by At

I am trying to use AutoLayout inside a UITableViewCell with Objective-C. I am not using a nib/xib/storyboard. I create my views in code.

Here's my UITableViewCell:

@implementation SettlementTableViewCell

- (instancetype) initWithReuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
    if (!self) return nil;

    [self setSelectionStyle:UITableViewCellSelectionStyleNone];

    _order = [[UILabel alloc] init];
    [_order setTranslatesAutoresizingMaskIntoConstraints:NO];
    [_order setFont:[UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]];
    [[self contentView] addSubview:_order];

    _amount = [[UILabel alloc] init];
    [_amount setTranslatesAutoresizingMaskIntoConstraints:NO];
    [_amount setFont:[UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]];
    [_amount setTextAlignment:NSTextAlignmentRight];
    [[self contentView] addSubview:_amount];

    _pickupDate = [[UILabel alloc] init];
    [_pickupDate setTranslatesAutoresizingMaskIntoConstraints:NO];
    [_pickupDate setFont:[UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline]];
    [[self contentView] addSubview:_pickupDate];

    _pickupLocation = [[UILabel alloc] init];
    [_pickupLocation setTranslatesAutoresizingMaskIntoConstraints:NO];
    [_pickupLocation setFont:[UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline]];
    [_pickupLocation setAdjustsFontSizeToFitWidth:YES];
    [_pickupLocation setTextAlignment:NSTextAlignmentRight];
    [[self contentView] addSubview:_pickupLocation];

    _deliveryDate = [[UILabel alloc] init];
    [_deliveryDate setTranslatesAutoresizingMaskIntoConstraints:NO];
    [_deliveryDate setFont:[UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline]];
    [[self contentView] addSubview:_deliveryDate];

    _deliveryLocation = [[UILabel alloc] init];
    [_deliveryLocation setTranslatesAutoresizingMaskIntoConstraints:NO];
    [_deliveryLocation setFont:[UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline]];
    [_deliveryLocation setAdjustsFontSizeToFitWidth:YES];
    [_deliveryLocation setTextAlignment:NSTextAlignmentRight];
    [[self contentView] addSubview:_deliveryLocation];

    [self setNeedsUpdateConstraints];

    return self;
}

- (void) updateConstraints {
    [super updateConstraints];

    UIEdgeInsets padding = UIEdgeInsetsMake(5, 20, -5, -20);

    [[self contentView] addConstraint:[NSLayoutConstraint constraintWithItem:_order attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationLessThanOrEqual
                                                                      toItem:[self contentView] attribute:NSLayoutAttributeLeft multiplier:1 constant:padding.left]];
    [[self contentView] addConstraint:[NSLayoutConstraint constraintWithItem:_order attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
                                                                      toItem:[self contentView] attribute:NSLayoutAttributeTop multiplier:1 constant:padding.top]];

    [[self contentView] addConstraint:[NSLayoutConstraint constraintWithItem:_amount attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
                                                                      toItem:[self contentView] attribute:NSLayoutAttributeTop multiplier:1 constant:padding.top]];
    [[self contentView] addConstraint:[NSLayoutConstraint constraintWithItem:_amount attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual
                                                                      toItem:[self contentView] attribute:NSLayoutAttributeRight multiplier:1 constant:padding.right]];

    [[self contentView] addConstraint:[NSLayoutConstraint constraintWithItem:_pickupDate attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual
                                                                      toItem:[self contentView] attribute:NSLayoutAttributeLeft multiplier:1 constant:padding.left]];
    [[self contentView] addConstraint:[NSLayoutConstraint constraintWithItem:_pickupDate attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
                                                                      toItem:_order attribute:NSLayoutAttributeBottom multiplier:1 constant:padding.top]];

    [[self contentView] addConstraint:[NSLayoutConstraint constraintWithItem:_pickupLocation attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
                                                                      toItem:_pickupDate attribute:NSLayoutAttributeTop multiplier:1 constant:0]];
    [[self contentView] addConstraint:[NSLayoutConstraint constraintWithItem:_pickupLocation attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationGreaterThanOrEqual
                                                                      toItem:_pickupDate attribute:NSLayoutAttributeRight multiplier:1.0 constant:2]];
    [[self contentView] addConstraint:[NSLayoutConstraint constraintWithItem:_pickupLocation attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual
                                                                      toItem:[self contentView] attribute:NSLayoutAttributeRight multiplier:1 constant:padding.right]];

    [[self contentView] addConstraint:[NSLayoutConstraint constraintWithItem:_deliveryDate attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual
                                                                      toItem:[self contentView] attribute:NSLayoutAttributeLeft multiplier:1 constant:padding.left]];
    [[self contentView] addConstraint:[NSLayoutConstraint constraintWithItem:_deliveryDate attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
                                                                      toItem:_pickupDate attribute:NSLayoutAttributeBottom multiplier:1 constant:0]];
    [[self contentView] addConstraint:[NSLayoutConstraint constraintWithItem:_deliveryDate attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual
                                                                      toItem:[self contentView] attribute:NSLayoutAttributeBottom multiplier:1 constant:padding.bottom]];

    [[self contentView] addConstraint:[NSLayoutConstraint constraintWithItem:_deliveryLocation attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
                                                                      toItem:_deliveryDate attribute:NSLayoutAttributeTop multiplier:1 constant:0]];
    [[self contentView] addConstraint:[NSLayoutConstraint constraintWithItem:_deliveryLocation attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual
                                                                      toItem:[self contentView] attribute:NSLayoutAttributeBottom multiplier:1 constant:padding.bottom]];
    [[self contentView] addConstraint:[NSLayoutConstraint constraintWithItem:_deliveryLocation attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationGreaterThanOrEqual
                                                                      toItem:_deliveryDate attribute:NSLayoutAttributeRight multiplier:1.0 constant:2]];
    [[self contentView] addConstraint:[NSLayoutConstraint constraintWithItem:_deliveryLocation attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual
                                                                      toItem:[self contentView] attribute:NSLayoutAttributeRight multiplier:1 constant:padding.right]];

//    [_order setBackgroundColor:[UIColor redColor]];
//    [_amount setBackgroundColor:[UIColor orangeColor]];
//    [_pickupDate setBackgroundColor:[UIColor yellowColor]];
//    [_pickupLocation setBackgroundColor:[UIColor greenColor]];
//    [_deliveryDate setBackgroundColor:[UIColor blueColor]];
//    [_deliveryLocation setBackgroundColor:[UIColor purpleColor]];

}
@end

Here's the UIViewController:

@interface Settlement : NSObject
@property (nonatomic, strong) NSString *orderId;
@property (nonatomic, strong) NSString *amount;
@property (nonatomic, strong) NSString *pickupDate;
@property (nonatomic, strong) NSString *deliveryDate;
@property (nonatomic, strong) NSString *pickupLocation;
@property (nonatomic, strong) NSString *deliveryLocation;
@end

@implementation Settlement
@end

@interface SettlementsViewController () <UITableViewDelegate, UITableViewDataSource>
@property (nonatomic, strong) NSMutableArray *rows;
@property (nonatomic, strong) UITableView *tableView;
@end

@implementation SettlementsViewController

- (instancetype) init {
    self = [super init];
    if (!self) return nil;

    [self setTitle:@"Settlements"];

    _rows = [[NSMutableArray alloc] initWithCapacity:5];

    Settlement *set = [[Settlement alloc] init];
    [set setOrderId:@"01234567"];
    [set setAmount:@"$9999.99"];
    [set setPickupDate:@"02/23/2015 0800"];
    [set setDeliveryDate:@"02/25/2015 1100"];
    [set setPickupLocation:@"Point Field Landing on the Severn, MD"];
    [set setDeliveryLocation:@"Winchester-on-the-Severn, MD"];
    [_rows addObject:set];

    set = [[Settlement alloc] init];
    [set setOrderId:@"0006181"];
    [set setAmount:@"$3.42"];
    [set setPickupDate:@"12/26/2013 1040"];
    [set setDeliveryDate:@"02/13/2014 0800"];
    [set setPickupLocation:@"Irondale, AL"];
    [set setDeliveryLocation:@"Lithonia, GA"];
    [_rows addObject:set];

    set = [[Settlement alloc] init];
    [set setOrderId:@"0002586"];
    [set setAmount:@"$1.66"];
    [set setPickupDate:@"09/27/2012 1350"];
    [set setDeliveryDate:@"02/09/2013 1115"];
    [set setPickupLocation:@"Decatur, AL"];
    [set setDeliveryLocation:@"Birmingham, AL"];
    [_rows addObject:set];

    set = [[Settlement alloc] init];
    [set setOrderId:@"0002586"];
    [set setAmount:@"$41.22"];
    [set setPickupDate:@"11/08/2013 1608"];
    [set setDeliveryDate:@"11/11/2013 0000"];
    [set setPickupLocation:@"Birmingham, AL"];
    [set setDeliveryLocation:@"Simi Valley, CA"];
    [_rows addObject:set];

    set = [[Settlement alloc] init];
    [set setOrderId:@"0002586"];
    [set setAmount:@"$41.22"];
    [set setPickupDate:@"11/08/2013 1608"];
    [set setDeliveryDate:@"11/11/2013 0000"];
    [set setPickupLocation:@"Birmingham, AL"];
    [set setDeliveryLocation:@"Simi Valley, CA"];
    [_rows addObject:set];

    return self;
}

- (void) loadView {
    [super loadView];

    _tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
    [_tableView setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
    [_tableView setDelegate:self];
    [_tableView setDataSource:self];
    [_tableView setEstimatedRowHeight:72.0f];
    [_tableView setRowHeight:UITableViewAutomaticDimension];
    [[self view] addSubview:_tableView];
}

- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return _rows.count; }

- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    SettlementTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    if (!cell) cell = [[SettlementTableViewCell alloc] initWithReuseIdentifier:@"cell"];

    Settlement *settlement = [_rows objectAtIndex:indexPath.row];
    [[cell order] setText:[settlement orderId]];
    [[cell amount] setText:[settlement amount]];
    [[cell pickupDate] setText:[settlement pickupDate]];
    [[cell deliveryDate] setText:[settlement deliveryDate]];
    [[cell pickupLocation] setText:[settlement pickupLocation]];
    [[cell deliveryLocation] setText:[settlement deliveryLocation]];

    return cell;
}

@end

When the screen first loads, I get this layout: initial layout

When I change to landscape, I get: landscape layout

When I switch back to portrait, I get: portrait layout

This last state is the layout I really want. How can I make the initial layout look like the one post-orientation change? What am I missing?

This was as short of an example as I could make. I can provide a running project that exhibits the issue, but I don't know the best way to do that. Leave a comment telling me how and I'll put it out there.

Thanks!

1

There are 1 answers

0
bmauter On BEST ANSWER

I continued to play with this. I get the layout I want if I set the width explicitly on both of the date fields:

[[self contentView] addConstraint:[NSLayoutConstraint constraintWithItem:_pickupDate attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual
                                                                  toItem:nil attribute:NSLayoutAttributeWidth multiplier:1.0 constant:120.0]];
[[self contentView] addConstraint:[NSLayoutConstraint constraintWithItem:_deliveryDate attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual
                                                                  toItem:_pickupDate attribute:NSLayoutAttributeWidth multiplier:1 constant:0]];

Can anyone explain why the intrinsic content size isn't good enough? Thanks!