0

I want to have a footer view on a static-cell UITableView that has three labels which are equally spaced, like so (from the simulator):enter image description here

I can supply the footer view from my table view controller using this delegate call:

override func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
    // construct view here
    return view
}

And I can construct the view in two ways:

  • Create the labels and the spacers in code and add the appropriate constraints
  • Do all that in a XIB file then load the view from the file

My problem is that the first approach doesn't work and the second does.

This is my code for the first approach:

override func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {

    if section == 0 {

        // Create footer view
        let view = UIView()
        view.backgroundColor = UIColor.yellowColor()
        view.clipsToBounds = false
        view.layer.borderColor = UIColor.greenColor().CGColor
        view.layer.borderWidth = 2
        view.setTranslatesAutoresizingMaskIntoConstraints(false)

        // Create labels
        var labels: [UIView] = []
        for name in ["Label 1", "AAAAAABBB", "Last label"] {
            let v = UILabel()

            v.font = UIFont.preferredFontForTextStyle(UIFontTextStyleFootnote)
            v.textColor = UIColor.darkTextColor()
            v.textAlignment = .Center
            v.text = name
            v.setTranslatesAutoresizingMaskIntoConstraints(false)

            view.addSubview(v)
            labels += [v]
        }

        // Create spacers
        var spacers: [UIView] = []
        for i in 1...4 {
            let v = UIView()

            v.backgroundColor = UIColor.blueColor() // Background color is just so we can see where the view is and what size it has
            v.setTranslatesAutoresizingMaskIntoConstraints(false)

            view.addSubview(v)
            spacers += [v]
        }

        // Constrain all views to top and bottom of superview
        for i in labels + spacers {
            view.addConstraint(NSLayoutConstraint(item: i, attribute: .Top, relatedBy: .Equal, toItem: view, attribute: .Top, multiplier: 1, constant: 0))
            view.addConstraint(NSLayoutConstraint(item: i, attribute: .Bottom, relatedBy: .Equal, toItem: view, attribute: .Bottom, multiplier: 1, constant: 0))
        }

        // Equal width for labels
        labels.pairs {
            view.addConstraint(NSLayoutConstraint(item: $0, attribute: .Width, relatedBy: .Equal, toItem: $1, attribute: .Width, multiplier: 1, constant: 0))
        }

        // Equal width for spacers
        spacers.pairs {
            view.addConstraint(NSLayoutConstraint(item: $0, attribute: .Width, relatedBy: .Equal, toItem: $1, attribute: .Width, multiplier: 1, constant: 0))
        }

        view.addConstraint(NSLayoutConstraint(item: view, attribute: .Left, relatedBy: .Equal, toItem: spacers[0], attribute: .Left, multiplier: 1, constant: 0))
        view.addConstraint(NSLayoutConstraint(item: spacers[0], attribute: .Right, relatedBy: .Equal, toItem: labels[0], attribute: .Left, multiplier: 1, constant: 0))
        view.addConstraint(NSLayoutConstraint(item: labels[0], attribute: .Right, relatedBy: .Equal, toItem: spacers[1], attribute: .Left, multiplier: 1, constant: 0))
        view.addConstraint(NSLayoutConstraint(item: spacers[1], attribute: .Right, relatedBy: .Equal, toItem: labels[1], attribute: .Left, multiplier: 1, constant: 0))
        view.addConstraint(NSLayoutConstraint(item: labels[1], attribute: .Right, relatedBy: .Equal, toItem: spacers[2], attribute: .Left, multiplier: 1, constant: 0))
        view.addConstraint(NSLayoutConstraint(item: spacers[2], attribute: .Right, relatedBy: .Equal, toItem: labels[2], attribute: .Left, multiplier: 1, constant: 0))
        view.addConstraint(NSLayoutConstraint(item: labels[2], attribute: .Right, relatedBy: .Equal, toItem: spacers[3], attribute: .Left, multiplier: 1, constant: 0))
        view.addConstraint(NSLayoutConstraint(item: spacers[3], attribute: .Right, relatedBy: .Equal, toItem: view, attribute: .Right, multiplier: 1, constant: 0))

        return view
    }
    else {
        return nil
    }
}

extension Array {
    func pairs(block: (Element, Element?)->()) {
        if count == 0 { return }
        if count == 1 { block(self.first!, nil) }

        var last = self[0]
        for i in self[1..<count] {
            block(last, i)
            last = i
        }
    }
}

This is the result:

enter image description here

Not at all what I was expecting, right?

Now, on to the second method. Instead of posting a bunch of screenshots from Interface Builder, I have created a sample project available here specifically to test this problem. If you open it, the file FooterView.xib contains the footer view constructed in IB that, as far as I know, has exactly the same view structure and auto-layout constraints.

Using that view, like this:

return (NSBundle.mainBundle().loadNibNamed("FooterView", owner: self, options: nil).first as UIView)

yields the result you saw in the first screenshot, which is exactly what I want.

So, with Interface Builder the constraints work as expected. Why doesn't it work when the views & constraints are created in code? What am I missing?

1 Answer 1

2

The view's size is unknown at creation time, so setting its setTranslatesAutoresizingMaskIntoConstraintsto true does the trick:

view.setTranslatesAutoresizingMaskIntoConstraints(true)

Result:

enter image description here

Sign up to request clarification or add additional context in comments.

3 Comments

It works! But... why? What has setTranslatesAutoresizingMaskIntoConstraints to do with the size being unknown at creation time?
I noticed that the view didn't get the width of tableView.frame.width and tried to set this manually. Unfortunately this didn't work so I played around with setTranslatesAutoresizingMaskIntoConstraints. I think the view now gets contstaints which automatically resizes it to the tableView width.
Glad to help! BTW: Really cool way to set the constraints with your Array extension.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.