Implementing iOS 7 Auto Layout Constraints in Code
Previous | Table of Contents | Next |
An iOS 7 Auto Layout Example | Implementing Cross-Hierarchy Auto Layout Constraints in iOS 7 |
Learn SwiftUI and take your iOS Development to the Next Level |
In addition to using Interface Builder, it is also possible to create auto layout constraints directly within the code of an application. These approaches, however, are not necessarily mutually exclusive. There are, for example, situations where a layout will be constructed using a combination of Interface Builder and manual coding. Furthermore, some types of constraint cannot yet be implemented in Interface Builder, constraints that cross view hierarchies being a prime example. Interface Builder is also of limited use when user interfaces are created dynamically at run time.
Given these facts, an understanding of how to create auto layout constraints in code is an important skill, and is the focus of this chapter.
Creating Constraints in Code
Implementing constraints in code is a two step process which involves first creating the constraint, and then adding the constraint to a view.
In order to create a constraint, an instance of the NSLayoutConstraint class must be created and initialized with the appropriate settings for the auto layout behavior it is to implement. This is achieved by calling the constraintWithItem method and passing through a set of arguments for the constraint.
When considering this syntax, it is helpful to recall the way in which constraints can be represented using linear equations (as outlined in An Introduction to Auto Layout in iOS 7) because the elements of the equation match the arguments used to create an NSLayoutConstraint instance.
Consider, for example, the following constraint expressed as an equation:
view1.bottom = view2.bottom – 20
The objective of this constraint is to position view1 so that its bottom edge is positioned a distance of 20 points above the bottom edge of view2. This same equation can be represented in code as follows:
NSLayoutConstraint *myConstraint =[NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:view2 attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-20];
As we can see, the arguments to the method exactly match those of the equation (with the exception of the multiplier which is absent from the equation and therefore equates to 1 in the method call).
The following equation sets the width of a Button view named myButton to be 5 times the width of a Label view named mylabel:
NSLayoutConstraint *myConstraint =[NSLayoutConstraint constraintWithItem:mybutton attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:mylabel attribute:NSLayoutAttributeWidth multiplier:5 constant:0];
So far the examples shown in this chapter have been equality based constraints and, as such, the relatedBy: argument has been set to NSLayoutRelationEqual. The following equation uses a greater than or equal to operator:
mybutton.width >= 200;
Translated into code, this reads as follows:
NSLayoutConstraint *myConstraint =[NSLayoutConstraint constraintWithItem:mybutton attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeWidth multiplier:1 constant:200];
Note that since this constraint is not related to another view, the toItem: argument is set to nil.
Adding a Constraint to a View
Once a constraint has been created, it needs to be assigned to a view in order to become active. This is achieved by passing it through as an argument to the addConstraint method of the view instance to which it is being added. In the case of multiple constraints, each is added by a separate call to the addConstraint method. This leads to the question of how to decide which view the constraint should be added to.
In the case of a constraint that references a single view, the constraint must be added to the immediate parent of the view. When a constraint references two views, the constraint must be applied to the closest ancestor of the two views. Consider, for the purposes of an example, the view hierarchy illustrated in Figure 17-1.
Figure 17-1
A constraint referencing only Label A should be added to the immediate parent, in this case View B. A constraint referencing Button B and Label B, on the other hand, must be added to the nearest common ancestor, which in this case is View C. A constraint referencing Button A and Button B must, once again, be added to the nearest common ancestor which equates to View A.
For the purposes of an example, the following code excerpt creates a new constraint and adds it to a view:
NSLayoutConstraint *myConstraint =[NSLayoutConstraint constraintWithItem:mybutton attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:mylabel attribute:NSLayoutAttributeWidth multiplier:5 constant:0]; [superview addConstraint: myConstraint];
Learn SwiftUI and take your iOS Development to the Next Level |
Turning off Auto Resizing Translation
When adding views to a layout in code the toolkit will, by default, attempt to convert the autosizing mask for that view to auto layout constraints. Unfortunately those auto-generated constraints will conflict with any constraints added within the application code. It is essential, therefore, that translation be turned off for views to which constraints are to be added. This is achieved by calling the setTranslatesAutoresizingMaskIntoConstraints method of the target view, passing through NO as an argument. For example, the following code creates a new Button view, turns off translation and then adds it to the parent view:
UIButton *mybutton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [mybutton setTitle:@"My Button" forState:UIControlStateNormal]; [mybutton setTranslatesAutoresizingMaskIntoConstraints:NO]; [superview addSubview:mybutton];
An Example Application
Create a new Xcode project for the iPhone or iPad using the Single View Application template. Enter AutoLayoutCode as the product name and class prefix.
Creating the Views
For the purpose of this example, the code to create the views and constraints will be added to the viewDidLoad method of the AutoLayoutCode view controller. Select the AutoLayoutCodeViewController.m file, locate this method and modify it to create a button and a label and add them to the main view:
- (void)viewDidLoad { [super viewDidLoad]; UIView *superview = self.view; UILabel *mylabel = [[UILabel alloc]init]; [mylabel setTranslatesAutoresizingMaskIntoConstraints:NO]; mylabel.text = @"hello"; UIButton *mybutton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [mybutton setTitle:@"My Button" forState:UIControlStateNormal]; [mybutton setTranslatesAutoresizingMaskIntoConstraints:NO]; [superview addSubview:mylabel]; [superview addSubview:mybutton]; }
Creating and Adding the Constraints
Constraints will be added to position the label in the horizontal and vertical center of the superview. The button will then be constrained to be positioned to the left of the label with the baselines of both views aligned. To achieve this layout, the viewDidLoad method needs to be modified as follows:
- (void)viewDidLoad { [super viewDidLoad]; UIView *superview = self.view; UILabel *mylabel = [[UILabel alloc]init]; [mylabel setTranslatesAutoresizingMaskIntoConstraints:NO]; mylabel.text = @"MyLabel"; UIButton *mybutton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [mybutton setTitle:@"My Button" forState:UIControlStateNormal]; [mybutton setTranslatesAutoresizingMaskIntoConstraints:NO]; [superview addSubview:mylabel]; [superview addSubview:mybutton]; NSLayoutConstraint *myConstraint = [NSLayoutConstraint constraintWithItem:mylabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0]; [superview addConstraint:myConstraint]; myConstraint = [NSLayoutConstraint constraintWithItem:mylabel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]; [superview addConstraint:myConstraint]; myConstraint = [NSLayoutConstraint constraintWithItem:mybutton attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:mylabel attribute:NSLayoutAttributeLeading multiplier:1 constant:-10]; [superview addConstraint:myConstraint]; myConstraint = [NSLayoutConstraint constraintWithItem:mybutton attribute:NSLayoutAttributeBaseline relatedBy:NSLayoutRelationEqual toItem:mylabel attribute:NSLayoutAttributeBaseline multiplier:1 constant:0]; [superview addConstraint:myConstraint]; }
When the application is compiled and run, the layout of the two views should match that illustrated in Figure 17-2.
Figure 17-2
Removing Constraints
Whilst it has not been necessary to do so in this example, it is important to be aware that it is also possible to remove constraints from a view. This can be achieved simply by calling the removeConstraint method of the view to which the constraint was added, passing through as an argument the NSLayoutConstraint object matching the constraint to be removed:
[self.myview removeConstraint:self.myconstraint];
It is also worth knowing that constraints initially created in Interface Builder can be connected to outlet properties, thereby allowing them to be referenced in code. The steps involved in creating an outlet for a constraint are covered in more detail in Implementing Cross-Hierarchy Auto Layout Constraints in iOS 7.
Summary
Whilst Interface Builder is the recommended method for implementing auto layout constraints, there are still situations where it may be necessary to implement constraints in code. This is typically necessary when dynamically creating user interfaces, or in situations where specific layout behavior cannot be achieved using Interface Builder (a prime example of this being constraints that cross view hierarchies as outlined in the next chapter).
Constraints are created in code by instantiating instances of the NSLayoutConstraint class, configuring those instances with the appropriate constraint settings and then adding the constraints to the appropriate views in the user interface.
Learn SwiftUI and take your iOS Development to the Next Level |
Previous | Table of Contents | Next |
An iOS 7 Auto Layout Example | Implementing Cross-Hierarchy Auto Layout Constraints in iOS 7 |