Auto Layout Visual Format Language Tutorial
In this tutorial you will learn how to use the Auto Layout Visual Format Language to easily lay out your app’s user interface using code. By József Vesza.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Auto Layout Visual Format Language Tutorial
25 mins
Layout Options
Layout options provide the ability to manipulate view constraints perpendicular to the current layout orientation being defined.
Applying vertical centering to all views in a horizontal layout orientation by using NSLayoutFormatOptions.AlignAllCenterY
is an example of layout options.
You wouldn't use this option in vertical orientation since there's no sense in trying to set all of the views' centers vertically while laying them out vertically, edge by edge. It's also not provided for vertical orientation, so there you go.
Next, you'll see how layout options are useful when it comes to constructing layouts. Remove the following code from viewDidLoad()
:
let nameLabelVerticalConstraints = NSLayoutConstraint.constraintsWithVisualFormat(
"V:|-23-[appNameLabel]",
metrics: nil,
views: views)
allConstraints += nameLabelVerticalConstraints
let skipButtonVerticalConstraints = NSLayoutConstraint.constraintsWithVisualFormat(
"V:|-20-[skipButton]",
metrics: nil,
views: views)
allConstraints += skipButtonVerticalConstraints
You're just removing the vertical constraints from the appNameLabel
and skipButton
. Instead, you're going to use the layout options to give them a vertical position.
Find the code that creates topRowHorizontalConstraints
and set the options
parameter to [.alignAllCenterY]
. It should now look like the following:
let topRowHorizontalConstraints = NSLayoutConstraint.constraints(
withVisualFormat: "H:|-15-[iconImageView(30)]-[appNameLabel]-[skipButton]-15-|",
options: [.alignAllCenterY],
metrics: nil,
views: views)
allConstraints += topRowHorizontalConstraints
You've provided the NSLayoutFormatOption
.alignAllCenterY
that takes each view inside the format string and creates layout constraints to align each of them by their vertical centers. This code works since the iconImageView
has previously defined vertical layout constraints, including its height. Thus, the appNameLabel
and skipButton
are vertically centered with the iconImageView
.
If you build and run now, the layout will look exactly the same, but you know the code is better :]
Remove the code that creates welcomeHorizontalConstraints
and adds it to the constraints array.
This removes the horizontal constraints from the welcomeLabel
.
Next, update the options when creating summaryLabelVerticalConstraints
definition to the following:
let summaryLabelVerticalConstraints = NSLayoutConstraint.constraints(
withVisualFormat: "V:[welcomeLabel]-4-[summaryLabel]",
options: [.alignAllLeading, .alignAllTrailing],
metrics: nil,
views: views)
allConstraints += summaryLabelVerticalConstraints
This code adds the NSLayoutFormatOptions
options .alignAllLeading
and .alignAllTrailing
to the options array. The welcomeLabel
and summaryLabel
's leading and trailing spacing will be aligned 15 points from the leading and trailing edge of their superview. This occurs because the summaryLabel
already has its horizontal constraints defined.
Again, this will give you the same layout as you already had, but in a better way.
Next, update the options when you create summaryToPageVerticalConstraints
to match the following:
let summaryToPageVerticalConstraints = NSLayoutConstraint.constraints(
withVisualFormat: "V:[summaryLabel]-15-[pageControl(9)]-15-|",
options: [.alignAllCenterX],
metrics: nil,
views: views)
allConstraints += summaryToPageVerticalConstraints
Adding this option aligns the views on the center X axis. Do the same for imageToWelcomeVerticalConstraints
:
let imageToWelcomeVerticalConstraints = NSLayoutConstraint.constraints(
withVisualFormat: "V:[appImageView]-10-[welcomeLabel]",
options: [.alignAllCenterX],
metrics: nil,
views: views)
allConstraints += imageToWelcomeVerticalConstraints
Build and run your project; how do the interface elements look?
Feeling centered yet? Layout options have taken you closer to that nice user interface you're after.
NSLayoutFormat Options Quick Reference
Here are the options you've used in Grapevine:
-
.alignAllCenterX
-- align interface elements usingNSLayoutAttribute.centerX
. -
.alignAllCenterY
-- align interface elements usingNSLayoutAttribute.centerY
. -
.alignAllLeading
-- align interface elements usingNSLayoutAttribute.leading
. -
.alignAllTrailing
-- align interface elements usingNSLayoutAttribute.trailing
.
Below are some more of these options:
-
.alignAllLeft
-- align interface elements usingNSLayoutAttribute.left
. -
.alignAllRight
-- align interface elements usingNSLayoutAttribute.right
. -
.alignAllTop
-- align interface elements usingNSLayoutAttribute.top
. -
.alignAllBottom
-- align interface elements usingNSLayoutAttribute.bottom
. -
.alignAllLastBaseline
-- align interface elements usingNSLayoutAttribute.lastBaseline
.
You can find the complete list in the documentation.
Note: At least one of the elements must have enough defined perpendicular constraints for layout options to work. See the example below:
The topView
, middleView
or bottomView
must have constraints defining the position of their leading edge for Auto Layout to generate the correct constraints.
Note: At least one of the elements must have enough defined perpendicular constraints for layout options to work. See the example below:
NSLayoutConstraints.constraintsWithVisualFormat(
"V:[topView]-[middleView]-[bottomView]",
options: [.alignAllLeading],
metrics: nil,
views: ["topView": topView, "middleView": middleView, "bottomView": bottomView"])
The topView
, middleView
or bottomView
must have constraints defining the position of their leading edge for Auto Layout to generate the correct constraints.
NSLayoutConstraints.constraintsWithVisualFormat(
"V:[topView]-[middleView]-[bottomView]",
options: [.alignAllLeading],
metrics: nil,
views: ["topView": topView, "middleView": middleView, "bottomView": bottomView"])
And now for a new concept! Meet Metrics.
Metrics
Metrics are a dictionary of number values that can appear inside the VFL format string. These are particularly useful if you have standardized spacing or calculated size values that you can't type directly into the format string.
Add the following constant declaration above your @IBOutlet
declarations in ViewController.swift:
private enum Metrics {
static let padding: CGFloat = 15.0
static let iconImageViewWidth: CGFloat = 30.0
}
Now you have a constant for the padding, and icon image width, you can create a metrics dictionary and utilize the constant. Add the following code above your views
declaration in viewDidLoad()
:
let metrics = [
"horizontalPadding": Metrics.padding,
"iconImageViewWidth": Metrics.iconImageViewWidth]
The above code creates a dictionary of key / value pairs to be substituted into the format string.
Next, replace the topRowHorizontalConstraints
and summaryHorizontalConstraints
definitions with the following:
let topRowHorizontalFormat = """
H:|-horizontalPadding-[iconImageView(iconImageViewWidth)]-[appNameLabel]-[skipButton]-horizontalPadding-|
"""
let topRowHorizontalConstraints = NSLayoutConstraint.constraints(
withVisualFormat: topRowHorizontalFormat,
options: [.alignAllCenterY],
metrics: metrics,
views: views)
allConstraints += topRowHorizontalConstraints
let summaryHorizontalConstraints = NSLayoutConstraint.constraints(
withVisualFormat: "H:|-horizontalPadding-[summaryLabel]-horizontalPadding-|",
metrics: metrics,
views: views)
allConstraints += summaryHorizontalConstraints
You're replacing the hard coded values in the format string with placeholders that represent keys from the metrics
dictionary. You also set the metrics parameter to the metrics
dictionary.
Auto Layout will perform string substitution, replacing the placeholder text with the value inside the metrics dictionary. In the above case, horizontalPadding
will be replaced with the constant 15 points, and iconImageViewWidth
will be replaced with the constant 30 points.
You've removed a repeatedly used magic number and replaced it with a nice clean variable. If you decide to change the padding, you only have to change one thing. Isn't that better? The metrics dictionary isn't limited to constants either; if you need to calculate things at run time and put them in the dictionary, that's fine too.
The final piece of the puzzle to place is how you lay out interface elements when your view controllers are embedded inside a UINavigationController
or UITabBarController
.