Day 31 - 100 Days of Swift

3 minute read

Project 6 (part 2)

Day 31 is the second part of the sixth project, working with Auto Layout. You make some changes to the visual format constraints that make things a little easier to maintain. And then you scrap those in favor of anchor based constraints, to do basically the same layout. Then at the end he gives you a couple of challenges to work with the anchor based constraints.

First, you add some values to the visual format constraints that we made yesterday:

1
let format = "V:|-36-[label1(==88)]-[label2(==88)]-[label3(==88)]-[label4(==88)]-[label5(==88)]-(>=36)-|"

That looks like this (I also centered the labels, because I thought that looked better):

Screenshot with hard-coded 88 points of height for labels.

Then, to make things easier to maintain, you swap out the hard-coded 88 for a metric you can pass in:

1
2
3
4
5
6
let metrics = ["LH": 88]
let format = "V:|-36-[label1(LH)]-[label2(LH)]-[label3(LH)]-[label4(LH)]-[label5(LH)]-(>=36)-|"
let constraints = NSLayoutConstraint.constraints(withVisualFormat: format,
                                                 options: [],
                                                 metrics: metrics,
                                                 views: viewsDictionary)

This looks exactly the same, it just allows you to change the 88 in one spot, or pass in a computed value if you want. The last thing you do is set all the labels to be the same height as the first one and set the priority of the first one to be a little lower, so the labels can be squished on screen in the landscape layout:

1
let format = "V:|-36-[label1(LH@999)]-[label2(label1)]-[label3(label1)]-[label4(label1)]-[label5(label1)]-(>=36)-|"

Now, it looks like this if you rotate it:

Screenshot of app in landscape with equal height labels.

Then you switch to using anchors. You loop through the labels and constrain each one to its superview and to the view above it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var previous: UILabel?

for label in [label1, label2, label3, label4, label5] {

    label.widthAnchor.constraint(equalTo: view.widthAnchor,
                                 constant: 0).isActive = true
    label.heightAnchor.constraint(equalToConstant: 88).isActive = true

    if let previous = previous {
        label.topAnchor.constraint(equalTo: previous.bottomAnchor,
                                   constant: 10).isActive = true
    } else {
        label.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor,
                                   constant: 0).isActive = true
    }

    previous = label
}

This leads to a view that looks exactly the same as the previous ones in the vertical orientation, except that the top is tied to the safe area, instead of to a hard-coded 36 points, so it will adapt to different phone sizes/shapes better. In the horizontal it doesn’t look good though, so you fix that in the challenges:

Screenshot of bad landscape layout with anchor constraints.

The three challenges are to use the leadingAnchor and trailingAnchor instead of the widthAnchor for the labels, to tie them to the safeAreaLayoutGuide, and to tie the heights of the labels to be 1/5 of the available height, minus 10 points. Here’s what I did for that:

1
2
3
4
5
6
7
label.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor,
                               constant: 0).isActive = true
label.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor,
                                constant: 0).isActive = true
label.heightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.heightAnchor,
                              multiplier: 0.2,
                              constant: -10).isActive = true

Once that is all done, it looks like this:

Screenshot of working app in portrait layout.
Screenshot of working app in landscape layout.

You can find my version of this project at the end of day 31 on Github here.

Reflections

Today also went by pretty quickly. I liked getting some more exposure to the visual format language, in case I ever come across it in the wild, but I don’t think I’ll be using it much. It doesn’t really mesh with how I think. The anchors make way more sense to me though. I have used them in the past and I’m sure I’ll continue using them in the future.