Get Started With JSConstraints - Programmatic Swift Constraints for Beginners
Storyboards in Xcode have always been the first must learn way of building UIs and whoever came with this idea is a geniusšŖšæ. As you develop more apps, you get to learn pros and cons of storyboards.
Today we are going to put storyboards aside and concentrate on writing everything programmatically.
Auto layout constraints is a statement you canāt avoid where Constraints basically define the relationship between our views in the user interface.
If you have used Xcode to create an app and wrote views programmatically, there is a 99% chance that you have come across something similar to this.
That looks fine until the project gets bigger.
What if I tell you that the six lines you see (from 24 to 30) can be reduced to a single line.
Well well, let me introduce you to JSConstraints
It is a tiny Swift library written with only one thing in mind: Blazin' Fast Programmatic Constraints-Typing.
JSConstraints is a really small library extending UIView and UIStackView functionalities.
Prerequisites
- Xcode installed on your machine
- Zeal to learn
Getting started
Create an Xcode project, let's say JSConstraintsTutorial
and donāt forget to choose Storyboard as your interface.
Initiate pods for your newly created project
pod init
Installation
It can be installed using cocoapods (which we are going to use) or swift package manager
Using cocoapods
Open Podfile
and add
pod 'JSConstraints'
Save your file and install pods with
pod install
Open your projectās JSContraintsTutorial.xcworkspace and start getting your hands dirty.
Creating UIViews programmatically
There are many tutorial that can help you get started and here, we shall do the basics.
Creating a UIView is pretty simple and straightforward.
let firstView = UIView()
Import library
To use the library, you will have to import it. You can copy and paste the line of code at the top of your file
import JSConstraints
If that is done, then we can now start exploiting its methods.
Positioning your view
Views in Xcode are positioned relative to other views. Take an example of you trying to paint something, there has to be a material to paint on and that material is the parent view while the image you are painting is a child view.
To position the image, you have to define the distance from top, left, bottom, right or width and height.
For the web gulus, you are probably well oriented with the concept of padding and for others it is a way of creating space around an element.
Look at it in a sense of you seated on a chair, the distance between you and the edge of the chair is the padding.
For now we are talking about the distance of a child view from the edges of itās parent view.
JSConstraints provides us with methods that are easy to use to achieve this.
PinTo
It is a method that is applied to a child view to position it in it's parent view just like you pin a poster on your wall.
Pin to superview with padding
In your viewDidLoad function, create your first view and give it a background color
// 1. Create UIView
let firstView = UIView()
// 2. Give it a background color
firstView.backgroundColor = .blue
Using pinTo
method, let's add our view to the parent view with a padding of 20.
// 3. Pin it to its parent view
firstView.pinTo(superview: view, padding: 20)
Build and run (cmd+r) your project and see the magic happenš.
We have just pinned our child view to our parent view.
You can still customize the padding sizes for each side using pinTo
.
// Method 1: padding applies to both x and y sides
firstView.pinTo(superview: view, padding: 20)
// Method 2: you specify the padding for each side individually
firstView.pinTo(superview: view, xPadding: 20, yPadding: 20)
I know you are probably feeling like an expert which you are right now.
Huhh! Wait until you get the next concept that will build a spaceship equivalent of the best app layout you can think about.
SetConstraints
This is a game changing method that let's you resize your views, position them relative to each other with one principal of keeping the code easy to read.
It takes in an array of constraints and returns an activated array of constraints.
Constraints allow us to create views that dynamically adjust to different size classes and positions.
The most commonly used are:
view.setConstraints([
.top(<NSLayoutAnchor<NSLayoutYAxisAnchor>>),
.leading(<NSLayoutAnchor<NSLayoutXAxisAnchor>>),
.trailing(<NSLayoutAnchor<NSLayoutXAxisAnchor>>),
.bottom(<NSLayoutAnchor<NSLayoutYAxisAnchor>>),
.height(<CGFloat>),
.width(<CGFloat>),
.xCenter(<NSLayoutAnchor<NSLayoutXAxisAnchor>>),
.yCenter(<NSLayoutAnchor<NSLayoutYAxisAnchor>>),
.squared(<CGFloat>)
])
Let's start with the positioning of the view like we did earlier.
Here I'm assuming you already have basic knowledge about Auto layout and if you don't, worry not and keep reading and you will eventually understand the concept.
From our previous code, we are replacing our pinTo method with
// 3. Add child view to parent view as a subview
view.addSubview(firstView)
// 4. apply setConstraints to our view
firstView.setConstraints([
.top(view.topAnchor),
.leading(view.leadingAnchor),
.bottom(view.bottomAnchor),
.trailing(view.trailingAnchor)
])
Build and run (cmd+r) your project
What we have just done is to wrap the whole child view over the parent view and that is why you can't see the white parent view.
What if we want our padding back!
Well rest your worries as JSConstraints has .constant() that takes in a CGFloat
(fancy way of defining a float value in swift) value and is appended to some of the elements above.
If I wanted to add padding to the top, left, bottom and the right side, update your code to have,
firstView.setConstraints([
.top(view.topAnchor) + .constant(20),
.leading(view.leadingAnchor) + .constant(20),
.bottom(view.bottomAnchor) + .constant(20),
.trailing(view.trailingAnchor) + .constant(20)
])
Now our parent view is visible in whiteššæ.
Let's talk more about .squared() that combines .height()
and .width()
.
As we have done before, we are going to instantiate our square view, with a yellow background and add it to our parent view
// 1. Instantiate your view
let squareVIew = UIView()
// 2. Give it a background color
squareVIew.backgroundColor = .systemYellow
// 3. Add it to the parent view
view.addSubview(squareVIew)
After adding our square view to the parent view, then we give it a frame using our setConstraints
method.
// 4. apply constraints to our view
squareVIew.setConstraints([
.top(view.topAnchor) + .constant(50),
.leading(view.leadingAnchor) + .constant(50),
.squared(200) // <---- Our focus
])
Build and run your project.
In this case .squared(200)
is equivalent to
squareVIew.setConstraints([
.height(200),
.width(200)
])
What if we want to position our squareView at the center of the parent view!
CenterIn
It is another method that positions a view at the center of it's parent view.
// Positioning squareView at the center of our parent view
squareView.centerIn(superview: view)
Let's the result of our method
What if you want to offset the center position!
// CenterIn with offset parameters
squareView.centerIn(superview: view, xOffset: <CGFloat>, yOffset: <CGFloat>)
If you have managed to reach here, then grab a cup of coffee and appreciate what youāve learnt as you prepare to read on for the next mind blowing concept.
UIStackView
This is a very powerful component that stacks views together either vertically or horizontally.
It is the most preferred way of building UI since it creates most of the constraints automatically.
Creating a UIStackView canāt get any simpler with the JSConstraint library.
JSConstraints comes with two powerful methods
- setDefaultConfig: Helps to configure stack view subviews layout. It takes in spacing between view, axis which can be either horizontal or vertical, alignment and distribution.
// Initialising setDefaultConfig
stackView.setDefaultConfig(spacing: <CGFloat>, axis: <NSLayoutConstraint.Axis>, alignment: <UIStackView.Alignment>, distribution: <UIStackView.Distribution)
- setEdgeInsets: adds padding to stack view and its child views. You can specify the padding that applies to sides or x and y side or each side be given itās value. Example of how it can be used.
// 1. Padding applies to all sides
stackView.setEdgeInsets(padding: 10)
// 2. Specify the x and y values
stackView.setEdgeInsets(x: 10, y: 10)
// 3. Specify the value for each side
stackView.setEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
We learn by example. Let's stack two views together in a stackView and explain how it was done.
Instantiate the stackView and two UIViews namely redView and blueView.
let redView = UIView()
let blueView = UIView()
let stackView = UIStackView()
Give the views background colors to differentiate them.
redView.backgroundColor = .systemRed
blueView.backgroundColor = .systemBlue
Attach the stackView to parent view.
view.addSubview(stackView)
Add redView and blueView to stackView using addArrangedView
.
stackView.addArrangedSubview(redView)
stackView.addArrangedSubview(blueView)
Now we are applying the knowledge we have acquired about setDefaultConfig to arrange redView and blueView horizontally with spacing of 40 and allow them to fill equally.
stackView.setDefaultConfig(spacing: 40, axis: .horizontal, alignment: .fill, distribution: .fillEqually)
Add padding to stack view child views by using setEdgeInsets
. You can take different approaches as discussed. Let's use the first one.
stackView.setEdgeInsets(padding: 10)
After configuring the child views, now let's pin our stack view to the main view.
stackView.setConstraints([
.top(view.topAnchor),
.leading(view.leadingAnchor),
.trailing(view.trailingAnchor),
.height(200)
])
Conclusion
JSConstaints not only works with blocks, it can also be applied to UITextField, UITextView, ImageView and basically every element that inherits from UIView in Xcode.