6
votes

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?

4
Try commenting out the line, tempFooter.translatesAutoresizingMaskIntoConstraints = NO; and see if that fixes the problem. - rdelmar
No, unfortunately results in a different crash: 2014-09-08 22:46:19.905 Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want.... ( "<NSLayoutConstraint:0x117137d80 V:[UIView:0x10dfd5170(17)]>", "<NSAutoresizingMaskLayoutConstraint:0x117082480 h=--& v=--& V:[UIView:0x10dfd5170(0)]>" ) - Z S
That new error is caused by having no height set for the footer view (so it will be 0), but having constraints to the top and bottom of it for a label that has a non-zero height. See my answer. - rdelmar

4 Answers

13
votes

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 .

4
votes

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

Constraints not working:

Not working

Working:

Working

1
votes

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

0
votes

hope this will help you

I am not sure of this but it help me in my project, first add every thing in contentView (all the subview and the constraint)

 [self.contentView addSubview:atext]

and then comes to code part

NSLayoutConstraint *tvatt1 = [NSLayoutConstraint constraintWithItem:tempFooter
                                                          attribute:NSLayoutAttributeLeft
                                                          relatedBy:NSLayoutRelationEqual
                                                             toItem:atext attribute:NSLayoutAttributeLeft
                                                         multiplier:1.0
                                                           constant:10.0]; 

code saying for left attribute

 temfooter(must be equal) = 1.0*atext +10.0 

i.e temfooter must be equal to the modified atext , so need to make sure that your constraints are not wrong.