Implementing iPad iOS 6 Auto Layout Constraints in Code

From Techotopia
Jump to: navigation, search
PreviousTable of ContentsNext
An iPad iOS 6 Auto Layout ExampleImplementing iPad Cross-Hierarchy Auto Layout Constraints in iOS 6


Learn SwiftUI and take your iOS Development to the Next Level
SwiftUI Essentials – iOS 16 Edition book is now available in Print ($39.99) and eBook ($29.99) editions. Learn more...

Buy Print Preview Book


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.


Contents


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 to the way in which constraints can be represented using linear equations (as outlined in An Introduction to iPad Auto Layout in iOS 6) 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 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 20-1.

iOS 6 Auto Layout View Hierarchy Diagram

Figure 20-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];

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 iPad using the Single View Application template. Enter AutoLayoutCode as the product name and class prefix, making sure that the storyboard and automatic reference counting options are both selected.

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 20-2.


Ipad ios 6 autolayout in code.png

Figure 20-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 iPad Cross-Hierarchy Auto Layout Constraints in iOS 6.

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
SwiftUI Essentials – iOS 16 Edition book is now available in Print ($39.99) and eBook ($29.99) editions. Learn more...

Buy Print Preview Book



PreviousTable of ContentsNext
An iPad iOS 6 Auto Layout ExampleImplementing iPad Cross-Hierarchy Auto Layout Constraints in iOS 6