“This is the first day of my participation in the Gwen Challenge in November. See details of the event: The Last Gwen Challenge in 2021.”

For example, the commonly used navigation and SnapKit are encapsulated based on Apple’s Auto Layout, but it seems that the new category NSLayoutAnchor is not used.

Apple Auto Layout Constraints was introduced in iOS 6, and it changes the way we think about UI element Layout. But it wasn’t the most elegant or beautiful API at the time, it was a bit cumbersome. This is why there are so many excellent automatic layout libraries emerging. But since then, Apple has improved and optimized the API around Auto Layout.

Automatic layout in iOS 6

When automatic layout first appeared in iOS 6, constraints could be created in user interface layouts in three ways:

Interface Builder, which we won’t discuss in this article.

2. Visual Format Language (VFL). This is a way to create constraints using visual format strings similar to ASCII art. It looks like this | – [the find] – [findNext] – [findField (> = 20)] – |.

The following is a simple example of writing a VFL constraint.

UIView *view1 = [[UIView alloc]init]; view1.backgroundColor = [UIColor yellowColor]; view1.translatesAutoresizingMaskIntoConstraints = NO; [superView addSubview:view1]; UIView *view2 = [[UIView alloc]init]; view2.backgroundColor = [UIColor yellowColor]; view2.translatesAutoresizingMaskIntoConstraints = NO; [superView addSubview:view2]; UIView *view3 = [[UIView alloc]init]; view3.backgroundColor = [UIColor yellowColor]; view3.translatesAutoresizingMaskIntoConstraints = NO; [superView addSubview:view3]; NSString *hVFL = @"H:|-space-[view1(==100)]-space1-[view2]-space-|"; NSString *hVFL1 = @"H:|-space-[view3]-space-|"; NSString *vVFL = @"V:|-space-[view1(==view3)]-space-[view3]-space-|"; NSString *vVFL1 = @"V:|-space-[view2(==view3)]-space-[view3]-space-|"; NSDictionary *metircs = @{@"space":@20,@"space1":@30}; NSDictionary *views = NSDictionaryOfVariableBindings(view1,view2,view3); NSArray *hconstraint = [NSLayoutConstraint constraintsWithVisualFormat:hVFL options:NSLayoutFormatDirectionLeadingToTrailing metrics:metircs views:views]; NSArray *hconstraint1 = [NSLayoutConstraint constraintsWithVisualFormat:hVFL1 options:NSLayoutFormatDirectionLeadingToTrailing metrics:metircs views:views]; NSArray *vconstraint = [NSLayoutConstraint constraintsWithVisualFormat:vVFL options:NSLayoutFormatDirectionLeadingToTrailing metrics:metircs views:views]; NSArray *vconstraint1 = [NSLayoutConstraint constraintsWithVisualFormat:vVFL1 options:NSLayoutFormatDirectionLeadingToTrailing metrics:metircs views:views]; / / add constraints [superView addConstraints: hconstraint]; [superView addConstraints:hconstraint1]; [superView addConstraints:vconstraint]; [superView addConstraints:vconstraint1];Copy the code

For simple layout, this might seem is a good way to create a layout, but for the complex layout, that may not be the case, but it lacks of many layout properties such as height/width percentage and center support, and for us, such a string of writing is very easy to get wrong, and bad test.

3.NSLayoutConstrint API

It also has the obvious disadvantage of being too long. Here is a simple example.

let leadingConstraint = NSLayoutConstraint(
    item: contentView,
    attribute: .leading,
    relatedBy: .equal,
    toItem: view,
    attribute: .leading,
    multiplier: 1,
    constant: 20)

let bottomConstraint = NSLayoutConstraint(
    item: contentView,
    attribute: .bottom,
    relatedBy: .equal,
    toItem: view,
    attribute: .bottom,
    multiplier: 1,
    constant: -20)

let topConstraint = NSLayoutConstraint(
    item: contentView,
    attribute: .top,
    relatedBy: .equal,
    toItem: view,
    attribute: .top,
    multiplier: 1,
    constant: 20)

let trailingConstraint = NSLayoutConstraint(
    item: contentView,
    attribute: .trailing,
    relatedBy: .equal,
    toItem: view,
    attribute: .trailing,
    multiplier: 1,
    constant: -20)

view.addConstraints([
    leadingConstraint,
    trailingConstraint,
    topConstraint,
    bottomConstraint
])
Copy the code

Improvements for iOS 8

In iOS 8, Apple introduced the concept of active state to the NSLayoutConstraint. Constraints affect layout calculations only in the active state. You can enable/disable any constraint without removing it and adding it back to the view.

Activate/Deactivate a collection of constraints

The old way of adding constraints is still compatible with activity state; All constraints passed in will be marked as active by addConstraints(_:). But it is no longer recommended. All constraints must apply only to views within the scope of the receiving view. Specifically, any view involved must be the receiving view itself, or a child of the receiving view. For experienced developers, this may not be a problem. But it can confuse newbies about which view they should add constraints to. You may receive this error if you try to add a constraint to the wrong superclass ().

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Impossible to set up layout with view hierarchy unprepared for constraint.'
Copy the code

In iOS 8, Apple introduced a new method, activate(_:). The activate(_:) method automatically adds the constraint to the correct view. There is also a deactivate(_:) as removeConstraint(_:).

NSLayoutConstraint.activate([
    leadingConstraint,
    trailingConstraint,
    topConstraint,
    bottomConstraint
])

NSLayoutConstraint.deactivate([
    leadingConstraint,
    trailingConstraint,
    topConstraint,
    bottomConstraint
])
Copy the code

You can also enable/disable individual constraints by changing the isActive property.

let leadingConstraint = NSLayoutConstraint(
    item: contentView,
    attribute: .leading,
    relatedBy: .equal,
    toItem: view,
    attribute: .leading,
    multiplier: 1,
    constant: 20)

leadingConstraint.isActive = true
leadingConstraint.isActive = false
Copy the code

Improvements to NSLayoutAnchor for iOS 9

Apple finally fixed the constraint creation syntax in iOS 9. Think of a new NSLayoutAnchor. As a specific point in the view that you can use as a reference point when creating constraints. Each anchor can only form constraints with coaxial anchors. NSLayoutAnchor comes with a set of methods, including those you’ll see in the operations of NSLayoutConstraint, such as equal (=), greater than or equal to (>=), and less than or equal to (<=). All of these take various parameters, including multipliers and constants. Even with many changes, you can access all of these constraints by typing. To explore all of these changes, check out the Apple documentation

The following table shows data from Apple documentation

Property Layout Anchor Description
bottomAnchor NSLayoutYAxisAnchor A layout anchor representing the bottom edge of the view’s frame.
centerXAnchor NSLayoutXAxisAnchor A layout anchor represents the horizontal center of the view’s frame.
centerYAnchor NSLayoutYAxisAnchor A layout anchor representing the vertical center of the view’s frame.
firstBaselineAnchor NSLayoutYAxisAnchor A layout anchor representing the baseline for the topmost line of the view’s frame.
heightAnchor NSLayoutDimension A layout anchor representing the height of the view’s frame.
lastBaselineAnchor NSLayoutYAxisAnchor A layout anchor representing the baseline for the bottommost line of the view’s frame.
leadingAnchor NSLayoutXAxisAnchor A layout anchor representing the leading edge of the view’s frame.
leftAnchor NSLayoutXAxisAnchor A layout anchor representing the left edge of the view’s frame.
rightAnchor NSLayoutXAxisAnchor A layout anchor represents the right edge of the view’s frame.
topAnchor NSLayoutYAxisAnchor A layout anchor representing the top edge of the view’s frame.
trailingAnchor NSLayoutXAxisAnchor A layout anchor representing the trailing edge of the view’s frame.
widthAnchor NSLayoutDimension A layout anchor representing the width of the view’s frame.

A simple example:

let constraints = [
    contentView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
    contentView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
    contentView.topAnchor.constraint(equalTo: view.topAnchor, constant: 20),
    contentView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20)
]
NSLayoutConstraint.activate(constraints)
Copy the code

For storyboard Auto Layout, you can Google it. Here I recommend an article, and in the next article I will write a simple Layout component based on Apple Auto Layout.

References:

  1. addconstraints
  2. VFL
  3. activate
  4. NSLayoutAnchor
  5. IOS Auto Layout: Tips and tricks
  6. Masonry parsing
  7. Constraints