I am quite late in my schedule because I had this trip in the middle which screwed up my timetables! But today I should manage to catch up—I hope. The first part of this article is exactly the one I wrote from Turin’s airport in the night between the 2nd and the 3rd of March.
I’m writing from Turin airport and it is 10pm of March 2nd. I would have never expected that such an important (though local) airport as Turin didn’t have a single power outlet in the whole section before security controls. My flight for Frankfurt is tomorrow at 6am so no way to pass the gates before 4am. That means I have about 6hrs to dedicate to Swift, brilliant right? Well yes but, as I said, no power outlet means I will be dead in about 3 to 4 hours. Still, plenty of time so let’s get to work.
Today’s project is a technique project where we will learn the magic of Auto Layout, the powerful engine that makes our interfaces adapt to every iOS device.
Paul’s suggestion is to make a copy of our project but, as I have done for Project 1, I prefer to keep improving the app from where it is and maybe branch it on GitHub (something I also need to learn about!) if I want to modify it further. Also, I do not like the fact that when we make a copy of the folder the project inside is not renamed. The effect is pretty annoying: we get in any case two Project-2.xcodeproj files just with two different paths in our folder. Not brilliant at all and something that Apple should fix, in my opinion. If you then try to modify the name of the file yourself … well I will let you discover what happens and rip your chest apart when you see that…
Lastly, I want my supercharged version to get the good layout, the one where I solved the challenges, not the one we started with!!
Advanced Auto Layout
The main issue right now is that if we run our project it looks good in portrait orientation (that is, vertical) but not in landscape. The constraints we set in Interface Builder force the size, the Y-position and the distance between each of the buttons, it is more than normal that at least one flag will end offscreen on some narrower iPhones.
One temporary —yet cheap— solution would be to disable the two possible landscape orientations, something that in some apps is done with quite some success. But in our case there is a better solution.
Once in our storyboard we need to perform the following actions:
- Ctrl-drag from the third-flag to below it and let go, selecting Bottom space to Safe Area in the menu that appears. This tells Auto-Layout that it needs to keep the distance between the bottom-last flag and the safe area to the value seen in Interface Builder.
- As we know that this will cause problems (many iPhones do not have so much space available when in landscape orientation) we need to select our newly created constraint, open the Size inspector and change its Constant to 20 and its Relation to Greater Than or Equal. This will tell Auto-Layout to make sure that the distance below the last flag should be at least 20, but if it is already more than that, nothing should be done.
- This will look better but we are far from being done: if the device is very small the flags will be shrunk and in a non-proportionate way. The next operations, though, will fix this:
- Ctrl-drag from the second-flag to the first one and select Equal Heights. Repeat the process for the third to second flag. This is not yet good, though, because if we shrink all of the flags together, they will look wrong. So…
- Ctrl-drag from the first-flag to itself (!) and select Aspect Ratio. Repeat the process for the other two flags. This will tell Auto-Layout to keep the proportion when the height is modified.
Go ahead, build and run trying different devices.
Auto Layout in code: addConstraints with Visual Format Language
In this last part for Day30 we meet a new friend, called Visual Format Language, a language that allows us to set up constraints in code directly without passing from Interface Builder at all. I cannot say I liked this part because I am a designer in my main life (!) and even if I like to know how things work I prefer to mess up with the UI than to write thousands of lines of code.
This for sure is a great way to do that but I hope I will never have to do it in person!
We are tasked with creating a new project, called Project6b and to create everything in the viewDidLoad() method. I will summarise as much as possible and then you can always download the project from Paul’s repository (being at the airport I am writing in the Notes app for macOS and discovered that you cannot select a word and store a link inside it…).
We create five labels like this just changing the backgroundColor to something sensible and the following text(s) to “ARE” “FIVE” “AWESOME” “LABELS”.
We then add these labels to the main view using view.addSubview(label1) —please substitute the number with the appropriate one.
To be able to use the Visual Format Language we have to create a dictionary of the views that will be used. We have to remember that variable names are there only to help us human understand machine code so we need to use a String (which is an array of characters which are just a number in the ASCII language!) to bridge the gap between us and … IT!
Next, we utilise a loop to add an horizontal constraint to each of the labels, like this:
Then, last thing for today, we add a vertical constraint between the views:
Auto Layout metrics and priorities: constraints(withVisualFormat:)
The main issue we are encountering right now is that our labels aren’t high enough and we are missing a rule for the bottom of the last label, which may cause it to be pushed off the bottom edge.
To add a constraint to the bottom edge we have to add
-(>=10)-| at the end of our vertical constraints String. Let’s break this down a bit:
- the pipe (
|) at the end represents the end of the screen.
- the two dashes are needed to signify the size of a space, while we saw before that a single dash would only mean “some space in between other views”.
(>=10)part means that we want a space of at least 10 points (it could be much more but it cannot be less than 10).
The next thing is fixing the height of the labels. If we wanted to hardcode it we could simply add
(==88) (or any other number) after
label1 (or any other label) and before the closing bracket
]. It is better, though, to use Auto Layout’s solution: metrics. We have set this parameter to
nil before but if we pass a dictionary into it we can solve our problems. So we create a dictionary of type
[String: Int] and set it to
["labelHeight": 88]. We then substitute
nil with the name of our dictionary and the hardcoded constant in the visual format string with
By now our interface looks great…in portrait orientation. If we rotate our device in landscape we will get a bunch of errors in our console saying the Auto Layout could not satisfy our constraints.
Every constraint has a priority between 1 and 1000, with 1000 being equal to “absolutely needed” and everything else being optional. So, assuming we have 1000 different views in our layout we could give a different priority to each one of them. Nice, right? Please read the very careful explanation in Paul’s article if you want to understand more about this topic.
To set our priority to something that will allow Auto Layout to actually adapt our interface we need to add
@999 after the dictionary entry in the VFL String, like this:
[label1(labelHeight@999)]. Then, we just substitute
label1 in the other entries because once the first label has been set we want the other ones to be equally sized.
The only question I still have after this is why we are not trying to avoid the status bar in the iPhone XS (or XS Max or XR for what matters).
The rest of the article has been eaten up by WordPress. I have pressed the Save button multiple times before publishing and the UI changed the button to “Saved”. Unfortunately I have not enough time in my life to cope with the deficiencies of technological systems. You can read about the rest of the project in the already linked article by Paul Hudson and see how it developed in this repository.
If you like what I’m doing here please consider liking this article and sharing it with some of your peers. If you are feeling like being really awesome, please consider making a small donation to support my studies and my writing (please appreciate that I am not using advertisement on my articles).
If you are interested in my music engraving and my publications don’t forget visit my Facebook page and the pages where I publish my scores (Gumroad, SheetMusicPlus, ScoreExchange and on Apple Books).
You can also support me by buying Paul Hudson’s books from this Affiliate Link.
Anyways, thank you so much for reading!
Till the next one!