Hi, Habr! I present to you the translation of the article
Creating UIViews Constraints Programmatically Using PlyLayout by Aly Yaka.

Today I will guide you through the creation of a simple mobile user interface with code, without using storyboards or NIBs. I will not go into discussions about what is best, because everything has its pros and cons, so just
leave a link that will go
deeper into this business .
')
In the second part of this tutorial, we will create some of the most commonly used mobile application user interface elements with code, including the navigation bar, table view, and dynamic-sized cells.
Overview
This tutorial was written using Xcode 9 and Swift 4. I also assume that you are familiar with Xcode, Swift and CocoaPods.
Without further delay, let's start creating our project: a simple Contact Card application. The purpose of this article is to teach you how to create your application's user interface in code, and therefore it will not contain any logic regarding the functionality of the application, unless it is necessary for the purposes of this guide.
Create constraints programmatically with PureLayout
Project Setup
Start by launching Xcode -> Create New Xcode Project. Select “Single View App” and click “Next”.

Call the project what you want, I decided to call it "ContactCard". Uncheck all three options below and select Swift as the programming language, then click Next.

Select a location on your computer to save the project. Uncheck "Create Git Repository on my Mac".
Since we will not use Storyboards or NIBs in this project, delete the “Main.storyboard”, which can be found in the Project Navigator:

After that, click on the project in the project navigator and on the “General” tab, find the section with information about deployment and delete everything that is written in the “Main Interface”. This is what tells Xcode which Storyboard file to load when launching the application, but since we simply deleted “Main.storyboard”, Xcode will not find this file, which will cause the application to crash.

Creating a ViewController
If you start the application now, a black screen will appear, since the application no longer has a user interface source, so in the next section we will create it. Open "AppDelegate.swift" and inside
application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?)
, Paste this code snippet:
self.window = UIWindow(frame: UIScreen.main.bounds) let viewController = ViewController() self.window?.rootViewController = viewController self.window?.makeKeyAndVisible()
This code provides a window for user interaction with the application, which can usually be found in “ViewController.swift”. To quickly check that everything works, go to “ViewController.swift” and in the
viewDidLoad()
method, insert the following line:
self.view.backgroundColor = .blue
Now run the application.
To navigate between files in Xcode, use hotkeys - “O”, and then enter the file name or even a code snippet that you are looking for, and a list of files from which you can choose will appear on the screen.
After running the application, this should be the result on the screen of your simulator:

Of course, we will not use this hideous blue, so just change the background to white by replacing
.blue
with
.white
inside
viewDidLoad ()
.
UI development
To create our user interface, we will use a
library that will make our life much easier. To install PureLayout, you must first open your terminal and type cd, then a space, drag your project folder into the terminal and press "Enter". Now run the following commands inside the terminal:
This should be the output of your terminal after executing the second command:

After that, close Xcode, open the folder in Finder, and you should find “<your project name> .xcworkspace”. This is what we will open to access our application if we ever need to use CocoaPods. Now find the file named “PodFile” and write the following line under the phrase
use_frameworks!
pod “PureLayout”
Run
pod install
in your terminal again, and then build your project by pressing "Command + B".
Coffee break
Now that everything is set up, let's start with the real work. Go to “ViewController.swift” and take a cup of coffee, because here’s what the end result will look like:

Creating an ImageView
Insert the
import PureLayout
line under
import UIKit
so that you can use the library in this file. Then, under the class declaration and outside of any function, we start by creating a lazy (lazy)
Avatar ImageView
variable as follows:
lazy var avatar: UIImageView = { let imageView = UIImageView(image: UIImage(named: "avatar.jpg")) imageView.autoSetDimensions(to: CGSize(width: 128.0, height: 128.0)) imageView.layer.borderWidth = 3.0 imageView.layer.borderColor = UIColor.lightGray.cgColor imageView.layer.cornerRadius = 64.0 imageView.clipsToBounds = true return imageView }()
As for the image, save any image on the desktop that you will use as an avatar, and drag it to Xcode in the <Your Project Name> folder, which in my case is called "ContactCard", and check the "Copy items if needed" box .

After that, write the name of this file along with its extension in the UIImage declaration instead of “avatar.jpg”.
For those of you who do not know, lazy variables look like ordinary variables, except that they are not initialized (or any memory space is allocated) until they are needed or called for the first time. . This means that lazy variables are not initialized when the view controller is initialized, but rather expect a later time when they are really needed, which saves processing power and memory space for other processes. This is especially useful when initializing user interface components.
PureLayout in action
As you can see inside initialization, the
imageView.autoSetDimensions (to: CGSize (width: 128.0, height: 128.0))
line
imageView.autoSetDimensions (to: CGSize (width: 128.0, height: 128.0))
is PureLayout in action. In one line, we set a limit for both the height and the width of the UIImageView, and all the necessary NSLayoutConstraints are created without the need for huge function calls. If you dealt with creating constraints in a software way, then you most likely have already fallen in love with this wonderful library.
To make this image round, we set its angular radius to half its width or height, which is 64.0 points. In addition, we set the
clipsToBounds
property to
true
, which tells the image to cut everything that is outside the radius we just set.
Then we move on to creating a UIView, which will serve as the top of the view behind the gray-colored avatar. Declare the following lazy variable for this view:
lazy var upperView: UIView = { let view = UIView() view.autoSetDimension(.height, toSize: 128) view.backgroundColor = .gray return view }()
Adding subviews
Before we go any further, let's create a function
func addSubviews ()
that adds the views we just created (and all the others we are going to create) as subviews to the view controller:
func addSubviews() { self.view.addSubview(avatar) self.view.addSubview(upperView) }
Now add the following line to
viewDidLoad (): self.addSubviews ()
Setting limits
To just get an idea of ​​how far we have come, let's set limits for these two types. Create another function named
func setupConstraints()
and insert the following restrictions:
func setupConstraints() { avatar.autoAlignAxis(toSuperviewAxis: .vertical) avatar.autoPinEdge(toSuperviewEdge: .top, withInset: 64.0) upperView.autoPinEdge(toSuperviewEdge: .left) upperView.autoPinEdge(toSuperviewEdge: .right) upperView.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .bottom) }
Now inside
viewDidLoad()
call
setupConstraints()
, as follows:
self.setupConstraints()
. Add this AFTER the
addSubviews()
call. This should be the final conclusion:

Bring your avatar to the foreground
Unfortunately, this is not what I would like to receive. As you can see, our
upperView
is on top of the avatar. This is due to the fact that we added an avatar as a
subviews
in front of the
upperView
, and since these subviews are arranged in some form on the stack, we get this result. To fix this, we can simply replace these two lines with each other, but there is another trick I want to show you, namely
self.view.bringSubview (toFront: avatar)
.
This method will transfer the avatar from the bottom of the stack to the top, so choose the method that you like best. Of course, for readability, it is better to add subviews in the order in which they should be displayed, if they intersect, and remember that the first added subviews will be at the bottom of the stack, and therefore any other intersecting views will appear on top of it.
And this is how it should look like:

Creating a segmented control
Next, we will create a segmented control, which is a gray bar containing three sections. In fact, a segmented control is easy to create. Do the following:
lazy var segmentedControl: UISegmentedControl = { let control = UISegmentedControl(items: ["Personal", "Social", "Resumè"]) control.autoSetDimension(.height, toSize: 32.0) control.selectedSegmentIndex = 0 control.layer.borderColor = UIColor.gray.cgColor control.tintColor = .gray return control }()
I believe that everything is clear, the only difference is that after initialization we provide it with an array of strings, each line represents the title of one of our desired sections. We also set
selectedSegmentIndex
to 0, which tells the segmented control to select / select the first segment during initialization. The rest is just a style to play with.
Now let's continue and add it as a subview by inserting the following line at the end of the
addCubviews(): self.view.addSubview(segmentedControl)
function
addCubviews(): self.view.addSubview(segmentedControl)
and its limitations will be:
segmentedControl.autoPinEdge(toSuperviewEdge: .left, withInset: 8.0) segmentedControl.autoPinEdge(toSuperviewEdge: .right, withInset: 8.0) segmentedControl.autoPinEdge(.top, to: .bottom, of: avatar, withOffset: 16.0)
We tell the segmented control that we want to attach it to the left side of its superview, however we want to increase the spacing slightly, rather than attaching it directly to the edge of the screen. If you notice, I use a so-called grid of eight points, where all distances and sizes are a multiple of eight. I do the same on the right side of the segmented control. As for the last limitation, he says to attach the vertex to the base of the avatar with an interval of 16 points.
After adding the above restrictions to the
func setupConstraints()
run the code and make sure that it looks like this:

Add button
We now turn to the last part of the user interface of the textbook, which is the "Edit" button. Add the following lazy variable:
lazy var editButton: UIButton = { let button = UIButton() button.setTitle("Edit", for: .normal) button.setTitleColor(.gray, for: .normal) button.layer.cornerRadius = 4.0 button.layer.borderColor = UIColor.gray.cgColor button.layer.borderWidth = 1.0 button.tintColor = .gray button.backgroundColor = .clear button.autoSetDimension(.width, toSize: 96.0) button.autoSetDimension(.height, toSize: 32.0) return button }()
Don't worry about how big the initialization is, but pay attention to how I set the header and its color by calling the
button.setTitle
and
button.setTitleColor
. For certain reasons, we cannot set the button title directly to its
titleLabel
, and this is because there are different states for the button, and it would be convenient for many to have different headers / colors for different states.
Now add the button as a subview, like the rest of the components, and add the following restrictions so that it appears where it should be:
editButton.autoPinEdge(.top, to: .bottom, of: upperView, withOffset: 16.0) editButton.autoPinEdge(toSuperviewEdge: .right, withInset: 8.0)
Here we set only the right and upper limits for the button, because we gave it the size, it will not expand and nothing else will be needed. Now run the project to see the final result:

Few last notes
Practice adding as many interface elements as you like. Create views of any application that you find difficult. Start simple and gradually build up complexity. Try drawing the UI components on a piece of paper to see how they fit together.
In the second part, I extend this guide to create a navigation bar, a table view, and dynamic-sized cells in code.