Mehul Patel
Mehul Patel

Reputation: 23053

Autolayout + constraints for dynamic table height based on total rows

First of all this ain't about dynamic cell's height. So don't mix it up.

I have a scenario in that I created three cards

  1. Detail card: Show specific details for location
  2. Chart card: Show different charts based on selection
  3. More details card: Card shows more details

Below are screens for above cards:

enter image description here enter image description here enter image description here

View hierarchy for above screens:

  • Controller
    • ScrollView
    • Card-1
    • Card-2
    • Card-3 <---- We are interested in this card
      • Custom View
        • TableView

So I'm showing the constraints for last card here, all above card works perfect! and there is no constraints ambiguity or warnings.

Let's go through code:

First Add Card-3 to ScrollView: here chartBox is Card-2 and bottomBox is Card-3

[self.scrollView addConstraints:[NSLayoutConstraint
                                 constraintsWithVisualFormat:@"V:[chartBox(200)]-(10)-[bottomBox]"
                                 options:0
                                 metrics:nil
                                 views:@{@"bottomBox" : self.bottomBox, @"chartBox" : self.chartBox}]];

// Below constraint pin to increase content-size
[self.scrollView addConstraints:[NSLayoutConstraint
                                constraintsWithVisualFormat:@"V:[bottomBox]-(10)-|"
                                options:0
                                metrics:nil
                                views:@{@"bottomBox" : self.bottomBox}]];

[self.scrollView addConstraints:[NSLayoutConstraint
                                constraintsWithVisualFormat:@"H:[bottomBox]-(10)-|"
                                options:0
                                metrics:nil
                                views:@{@"bottomBox" : self.bottomBox}]];

Second add CustomView to Card-3: Here bluePrintView is CustomView

_bluePrintView = [[BluePrintView alloc] init];
self.bluePrintView.translatesAutoresizingMaskIntoConstraints = NO;
[self.bottomBox addSubview:self.bluePrintView];

[self.bottomBox addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[bluePrintView]|" options:0 metrics:nil views:@{@"bluePrintView":self.bluePrintView}]];
[self.bottomBox addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[bluePrintView]|" options:0 metrics:nil views:@{@"bluePrintView":self.bluePrintView}]];

CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width - 20;
NSDictionary *metrices = @{@"width" : [NSNumber numberWithFloat:screenWidth]};
[self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(10)-[bottomBox(width)]" options:0 metrics:metrices views:@{ @"bottomBox": self.bottomBox}]];

Third add TableView to CustomView:

self.tableView.tableFooterView = [[UIView alloc] init];

[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[tableView]|" options:0 metrics:nil views:@{@"tableView":self.tableView}]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[tableView]|" options:0 metrics:nil views:@{@"tableView":self.tableView}]];

// Height constraint
CGFloat viewHeight = self.bounds.size.height;
self.tableHeightConstraint = [NSLayoutConstraint constraintWithItem:self.tableView
                                                          attribute:NSLayoutAttributeHeight
                                                          relatedBy:NSLayoutRelationEqual
                                                             toItem:nil
                                                          attribute:NSLayoutAttributeNotAnAttribute
                                                         multiplier:1.0
                                                           constant:viewHeight];
[self addConstraint:self.tableHeightConstraint];

[self.tableView addObserver:self forKeyPath:@"contentSize" options:0 context:NULL];

And in observer method I'll update tableHeightConstraint constraint.

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    self.tableHeightConstraint.constant = self.tableView.contentSize.height;
    [self.tableView setNeedsLayout];
    [self.tableView layoutIfNeeded];
}

After trying all possibilities end up here. Warnings for constraints.

(
    "<NSLayoutConstraint:0x7fbeb1e7e8e0 V:|-(0)-[UITableView:0x7fbeb2843000]   (Names: '|':BluePrintView:0x7fbeb1e7ac30 )>",
    "<NSLayoutConstraint:0x7fbeb1e7e700 V:[UITableView:0x7fbeb2843000]-(0)-|   (Names: '|':BluePrintView:0x7fbeb1e7ac30 )>",
    "<NSLayoutConstraint:0x7fbeb1e7e9b0 V:[UITableView:0x7fbeb2843000(480)]>",
    "<NSLayoutConstraint:0x7fbeb1e81a20 '_UITemporaryLayoutHeight' V:[BluePrintView:0x7fbeb1e7ac30(0)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7fbeb1e7e700 V:[UITableView:0x7fbeb2843000]-(0)-|   (Names: '|':BluePrintView:0x7fbeb1e7ac30 )>

I solved these warnings by removing | line from [tableView] (by removing bottom constraint to superView), but then tableView does not get desire height.

Any idea how to get full tableView height with out any constraint warnings.

P.S. All views are created by code, no XIB no Storyboard layout. iOS 9.2

Upvotes: 0

Views: 1238

Answers (1)

Tanguy G.
Tanguy G.

Reputation: 2303

I did not see any error in your constraints setup.

You may see this UITemporaryLayoutHeight constraint because you added the contentSize observer ([self.tableView addObserver:self forKeyPath:@"contentSize" options:0 context:NULL] ) too soon in your view life cycle, which triggers a [self.tableView layoutIfNeeded].

Try to add the observer in the viewDidLoad, remove it in dealloc of your View Controller ; Or in your viewWillAppear, and remove it in the viewWillDisappear for exemple.

Upvotes: 1

Related Questions