Setting a label to UITableView's tableFooterView - with autolayout

14,225

Solution 1

You shouldn't set translatesAutoresizingMaskIntoConstraints to NO for table views, their cells, or table header and footer views (or for your controller's self.view for that matter). It's ok to use constraints for views inside those views (the content view of cells or the header and footer views). I added a simplified version of your code to viewDidLoad, and it worked as expected. Note that you need to give the footer view a height and an x position (the y position and the width are ignored),

    UIView *tempFooter = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 50)];
    UILabel *atext = [[UILabel alloc] init];
    atext.translatesAutoresizingMaskIntoConstraints = NO;
    atext.numberOfLines = 0;
    [atext setText:@"Add Some Text"];
    atext.textAlignment = NSTextAlignmentCenter;
    atext.font = [UIFont italicSystemFontOfSize:14];
    atext.textColor = [UIColor grayColor];
    atext.backgroundColor = [UIColor clearColor];
    atext.lineBreakMode = NSLineBreakByWordWrapping;

    [tempFooter addSubview:atext];
    NSDictionary *dict = NSDictionaryOfVariableBindings(atext);
    [tempFooter addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-10-[atext]-10-|" options:0 metrics:nil views:dict]];
    [tempFooter addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[atext]|" options:0 metrics:nil views:dict]];
    self.tableView.tableFooterView = tempFooter;

I'm not sure what you were trying to accomplish with your constraint, tvatt3. The multiplier (you have 5) doesn't do anything with a top or left constraint since the value of those attributes is 0. I used the visual constraints, rather than the method you used, but when I used your code to add the constraints, it worked also .

Solution 2

Interestingly, changing the Autoresizing mask in the xib solved my problem.

Constraints not working:

Not working

Working:

Working

Solution 3

I was able to make dynamically auto layout table footer width and height by implementing @rdelmar solution for constraints and adding these lines below. Same solution applies to table header view.

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    if let tableFooter = tableView.tableFooterView {
        let maxSize = CGSize(width: self.tableView.bounds.size.width,
                             height: CGFloat.greatestFiniteMagnitude)
        let size = tableFooter.systemLayoutSizeFitting(maxSize)
        let height = size.height
        var headerFrame = tableFooter.frame

        // If we don't have this check, viewDidLayoutSubviews() will get
        // repeatedly, causing the app to hang.
        if height != headerFrame.size.height {
            headerFrame.size.height = height
            tableFooter.frame = headerFrame
            tableView.tableFooterView = tableFooter
        }
    }
}

P. S. I prefer Interface Builder for constraints setup enter image description here

Share:
14,225
Z S
Author by

Z S

www.contactsjournal.com

Updated on June 19, 2022

Comments

  • Z S
    Z S almost 2 years

    When my table view is empty, I want to add a simple label into the tableview footer to display a "no content" message. It was working fine when I was setting the frames of the UILabel and the UIView that I used for footerView, but now I'm trying to convert to using auto-layout, and I can't get it to work.

    Here's what I'm doing:

    // Create the footer    
    UIView *tempFooter = [[UIView alloc] init];
    UILabel *atext = [[UILabel alloc] init];
    tempFooter.translatesAutoresizingMaskIntoConstraints = NO;
    atext.translatesAutoresizingMaskIntoConstraints = NO;
    
    atext.numberOfLines = 0;
    [atext setText:@"Add Some Text"];
    atext.textAlignment = NSTextAlignmentCenter;
    atext.font = [UIFont italicSystemFontOfSize:14];
    atext.textColor = [UIColor grayColor];
    atext.backgroundColor = [UIColor clearColor];
    atext.lineBreakMode = NSLineBreakByWordWrapping;
    
    // add label to footer view, and add constraints
    [tempFooter addSubview:atext];
    NSLayoutConstraint *tvatt1 = [NSLayoutConstraint constraintWithItem:tempFooter
                                                              attribute:NSLayoutAttributeLeft
                                                              relatedBy:NSLayoutRelationEqual
                                                                 toItem:atext attribute:NSLayoutAttributeLeft
                                                             multiplier:1.0
                                                               constant:10.0];
    
    NSLayoutConstraint *tvatt2 = [NSLayoutConstraint constraintWithItem:tempFooter
                                                              attribute:NSLayoutAttributeRight
                                                              relatedBy:NSLayoutRelationEqual
                                                                 toItem:atext
                                                              attribute:NSLayoutAttributeRight
                                                             multiplier:1.0
                                                               constant:10.0];
    
    NSLayoutConstraint *tvatt3 = [NSLayoutConstraint constraintWithItem:tempFooter
                                                              attribute:NSLayoutAttributeTop
                                                              relatedBy:NSLayoutRelationEqual
                                                                 toItem:atext
                                                              attribute:NSLayoutAttributeTop
                                                             multiplier:5.0
                                                               constant:0];
    
    NSLayoutConstraint *tvatt4 = [NSLayoutConstraint constraintWithItem:tempFooter
                                                              attribute:NSLayoutAttributeBottom
                                                              relatedBy:NSLayoutRelationEqual
                                                                 toItem:atext
                                                              attribute:NSLayoutAttributeBottom
                                                             multiplier:1.0
                                                               constant: 0];
    
    [tempFooter addConstraints: @[tvatt1, tvatt2, tvatt3, tvatt4]];
    
    
    // set the footerview 
    self.tview.tableFooterView = tempFooter;
    

    When I run this, I get a crash:

    2014-09-08 19:57:16.594 SimpleList[49442:60b] * Assertion failure in -[UITableView layoutSublayersOfLayer:], /SourceCache/UIKit_Sim/UIKit-2935.137/UIView.m:8794 2014-09-08 19:57:21.073 SimpleList[49442:60b] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Auto Layout still required after executing -layoutSubviews. UITableView's implementation of -layoutSubviews needs to call super

    I'm not sure what I'm doing wrong. I've also tried setting constraints for the self.tview.tableFooterView wrt the tempFooter (pinning against all 4 sides) but that doesn't change the crash. I also call:

    [self.tview.tableFooterView layoutSubviews];
    

    but that doesn't help either.

    Any ideas what I'm doing wrong?

  • Z S
    Z S over 9 years
    Thanks, but the question is more about a UITableView's tableFooterView. The general use-case of adding a view into another UIView is simple enough.
  • Z S
    Z S over 9 years
    Thanks. The multiplier by 5 is a typo (it should be 1). This seems to work, but there is a major problem, this is that the height is fixed at 50. How do I adjust the header height if the text is much longer? e.g. try with this: [atext setText:@"Add Some Text. Now add a lot more text with newlines thrown in for good measure. Does this work beyond a certain height?\n\nNow how do you calculate the height?"]; The full footer doesn't show anymore. I've tried using [atext systemLayoutSizeFittingSize: UILayoutFittingExpandedSize] to set the frame for footer before setting it, but it crashes
  • Z S
    Z S over 9 years
    I thinkk I have it working; I set the footerView in viewWillLayoutSubviews and set the height of the footer using systemLayoutSizeFittingSize:. The only problem: still getting a "Unable to simultaneously satisfy constraints" message in the debugger. This one looks suspicious, but no idea how it got there: "<NSLayoutConstraint:0x7fa2d0050d20 'UIView-Encapsulated-Layout-Width' H:[UIView:0x7fa2d005acb0(0)]>"
  • Z S
    Z S over 9 years
    Figured it out: the visual constraint for pinning the label horizontally wasn't working. I removed that and added a centering constraint with constraintWithItem between label and tempFooter (with NSLayoutAttributeCenterX) and it works!
  • Anurag Bhakuni
    Anurag Bhakuni over 9 years
    @ZS explore this link(constraints.icodeforlove.com) , might it help you in some way.
  • Lorenzo B
    Lorenzo B over 8 years
    Can you explain why the translatesAutoresingMaskIntoConstraints should not be set? Thanks
  • Evan R
    Evan R almost 8 years
    @rdelmar "Note that you need to give the footer view a height and an x position (the y position and the width are ignored)"—actually, under iOS 9 at least, the width passed in isn't ignored and is used to set the width of the view.
  • ishegg
    ishegg about 5 years
    After literal hours of trying different things, this fixed my problem for both the footer and header views of a UITableView. Thank you so much! I would never have thought of this.