Introducing ConstraintFormatter
We all know how painful and verbose building a layout in iOS can be.
On a previous blog post about this subject Gabriel presented a library called Masonry which makes, using Gabriel's words, attribute constraints succinct.
While working on that blog post and discussing it around the office, the idea for ConstraintFormatter was born.
With ConstraintFormatter you describe your visual constraints and constraints based on attributes for Auto Layout in a single place.
In order to make it easier to understand, I've changed the ViewsAlignment example app shown earlier to use our shiny new library. You can check all changes on this commit.
To recap, the app example consists of one simple screen with:
- UIImageView for the group picture
- UIImageView for the logo
- UILabel for the company name
- UIView with white background color as the container for the logo and name
That is what it should look like:
App Internals
To draw that screen we organized this app with a ViewController called RootViewController.m, where we describe the background, and a view called LogoView.m, where we describe the logo and label on the bottom.
Changes on the RootViewController.m
The first step is to import the library:
#import "ConstraintFormatter.h"
Then, go ahead and remove all setTranslatesAutoresizingMaskIntoConstraints:NO from your code because ConstraintFormatter will add it in all views declared on your constraints. Pretty handy, don't you think?
The main changes are:
- describe the views
- describe the metrics
- describe the formats
-(void)addConstraints {
id views = @{@"logo": self.logoView, @"background": self.backgroundView};
id metrics = @{@"margin": @10};
id formats = @[@"logo.centerX == superview.centerX",
@"logo.bottom == superview.bottom",
@"H:|[background]|",
@"V:|[background]|"];
}
We are declaring constraints to the "logo" and "background" views as following:
- logo centerX (horizontal center) position is the same as the superview centerX (the whole screen)
- the bottom of the logo is the same as the bottom of the superview
- the background will fill the whole screen vertically
- the background will fill the whole screen horizontally
Your constraints are:
[self.view addConstraintsWithFormats:formats views:views metrics:metrics];
Changes on the LogoView.m
Once again, we import the library:
#import "ConstraintFormatter.h"
Clean up your code by removing the objc setTranslatesAutoresizingMaskIntoConstraints:NO
Describe our constraints for this view:
-(void)addConstraints {
id views = @{@"image": self.imageView, @"label": self.label};
id metrics = @{@"margin": @6};
id formats = @[@"label.centerY == superview.centerY",
@"H:|-margin-[image]-margin-[label]-margin-|",
@"V:|-margin-[image]-margin-|"];
[self addConstraintsWithFormats:formats views:views metrics:metrics];
}
Here we have a view we call "image" (for the logo) e "label" (for the Hashrocket text) and the rules are:
- label centerY (vertical center) is equal to superview centerY (note that superview here is the LogoView, the white area on the bottom of the screen).
- Horizontally we have a margin, the image, a margin, the text, and a final margin. Meaning: I want the image on the left, the label on the right, and the spacing between them is 6 points. For the image, add a 6 point margin on the left side and add a 6 point margin on the right of the label.
- Vertically we have a margin, the image, and a margin. Meaning: add the image within 6 points from the top and 6 points from the bottom.
I don't know about you, but it seems much easier to me to describe my elements this way and even easier to understand it months later when I have to look at this code again.
What do you think?