The original link
I will read the source code for navigation to learn about its design. However, after reading part of it, I found that my understanding of iOS auto layout was not systematic enough and not deep enough. So get ready to learn the basics of iOS auto layout.
Here are some of the things I’ve done with the iOS layout system, with auto layout being the main focus.
An overview of the
Apple introduced Absolute Layout with the iPhone 4 and Auto Layout with iOS 6 as the number of iOS devices grew. In the process of gradually improving the automatic layout, Apple also launched such technologies as Size Class, Stack View, UILayoutGuide, etc., but their essence is based on automatic layout.
source
1997 Alan Boring, Kim Marriott, Peter Stuckey et al., in their published paper Solving Linear Arithmetic Constraints for User Interface Applications, identified Cassowary to solve the layout problem Constraints – Solving algorithm is implemented.
In 2011, Apple applied Cassowary’s algorithm to its Layout engine, Auto Layout.
Cassorwary
Cassowary can effectively parse linear equality systems and linear inequality systems to represent equality and inequality relationships in user interfaces. Based on this, Cassowary has developed a rule system that describes the relationships between views through constraints. Constraints are rules that represent the position of one view relative to another.
Due to the advanced nature of Cassowary’s algorithm, many programming languages have implemented corresponding libraries, such as JavaScript,.net, Java, SmallTalk, C++.
The constraint
Cassowary’s core is to describe the relationships between views based on constraints. The constraint is essentially an equation:
Attribute1 = Multiplier × item2. Attribute2 + constantCopy the code
Let’s introduce the constraint equation with a simple constraint.
This constraint means that the left edge of the red view is 8 pixels to the right of the blue view. Note that = doesn’t mean assignment, it means equality.
In an automatic layout system, constraints can define not only the relationship between two views, but also the relationship between two different properties of a single view, such as setting a ratio between the height and width of the view. In general, a view requires four constraints to determine its size and location.
Constraint rules
The constraint equation above mainly describes the relationship between two view attributes. So, let’s take a look at what properties and relationships iOS defines.
attribute
Apple uses an enumerated value of type NSLayoutAttribute to represent layout attributes, which mainly contain the following attributes:
typedef NS_ENUM(NSInteger.NSLayoutAttribute) {
// View position
NSLayoutAttributeLeft = 1.NSLayoutAttributeRight.NSLayoutAttributeTop.NSLayoutAttributeBottom.// Before and after the view
NSLayoutAttributeLeading.NSLayoutAttributeTrailing.// View width and height
NSLayoutAttributeWidth.NSLayoutAttributeHeight.// View center
NSLayoutAttributeCenterX.NSLayoutAttributeCenterY.// View baseline
NSLayoutAttributeLastBaseline.NSLayoutAttributeFirstBaseline NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeLeftMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeRightMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeTopMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeBottomMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeLeadingMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeTrailingMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeCenterXWithinMargins NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeCenterYWithinMargins NS_ENUM_AVAILABLE_IOS(8_0),
// placeholder, which can be used when an attribute is not used in a relationship with another constraint
NSLayoutAttributeNotAnAttribute = 0
};
Copy the code
It is worth noting that NSLayoutAttribute similar NSLayoutAttributeLeft and NSLayoutAttributeLeftMargin enumeration. The difference between the two is:
NSLayoutAttributeLeft
Represents the leftmost part of the view;NSLayoutAttributeLeftMargin
Represents the left side of the view, and how far from the left side is the margin of the viewlayoutMargins
The relevant.
We’ll talk about layoutMargins later on.
Relationship between
Apple uses an enumerated value of type NSLayoutRelation to represent attribute relationships, which mainly include the following relationships:
typedef NS_ENUM(NSInteger.NSLayoutRelation) {
NSLayoutRelationLessThanOrEqual = - 1.NSLayoutRelationEqual = 0.NSLayoutRelationGreaterThanOrEqual = 1};Copy the code
The constraint level
A constraint describes the relationship between two views, but only if both views belong to the same view hierarchy. There are two types of hierarchy:
- A view is a view of another view
- Both views have a not under one window
nil
The common ancestor view of.
Constraint priority
Constraints have priority. When a layout engine evaluates a layout, it evaluates it one by one in order of priority from highest to lowest. If it is found that an optional constraint cannot be satisfied, the constraint is skipped and the next constraint is evaluated. Sometimes a constraint can affect the layout even if it doesn’t fit perfectly.
Apple defines four priority enumerated values by default. In addition, Apple allows you to create other priorities, but they must be in the range of 1 to 1000.
static const UILayoutPriority UILayoutPriorityRequired = 1000;
static const UILayoutPriority UILayoutPriorityDefaultHigh = 750;
static const UILayoutPriority UILayoutPriorityDefaultLow = 250;
static const UILayoutPriority UILayoutPriorityFittingSizeLevel = 50;
Copy the code
Constraints to create
For constraint creation, Apple provides Interface Builds that allow constraints to be created in a non-programmatic way. But in large projects, we still create constraints primarily programmatically.
There are three main ways to create constraints programmatically:
- Constraint constructor (NSLayoutConstraint)
- Layout Anchors
- Visual Format Language (VFL)
Let’s introduce them one by one.
NSLayoutConstraint
Apple uses the NSLayoutConstraint type to represent constraints. The NSLayoutConstraint class provides a constructor to create the constraint directly. The parameters of the constructor correspond to the terms of the constraint equation.
+ (instancetype)constraintWithItem:(id)view1
attribute:(NSLayoutAttribute)attr1
relatedBy:(NSLayoutRelation)relation
toItem:(id)view2
attribute:(NSLayoutAttribute)attr2
multiplier:(CGFloat)multiplier
constant:(CGFloat)c;
Copy the code
Layout Anchors
Apple uses the NSLayoutAnchor type to represent layout anchors. Before introducing NSLayoutAnchor, we need to introduce some other concepts such as UILayoutGuide (used when using NSLayoutAnchor).
UILayoutGuide
UILayoutGuide is a virtual rectangular area that can be thought of as a transparent UIView, but it is not added to the view hierarchy, nor does it block message calls, and is used to interact with Auto Layout. For example, three UIViews are in a row with the same spacing between them. The interval can be replaced with UILayoutGuide.
Now that we know about UILayoutGuide, we need to know about two UIView properties (instances of the UILayoutGuide type) :
layoutMarginsGuide
: first appeared on iOS 9safeAreaLayoutGuide
: first appeared on iOS 11
layoutMarginsGuide
UIView has a property of type UIEdgeInsets, layoutMargins, which represents the margins between the content of a view and its four margins, as shown below.
The layoutMarginsGuide property of UIView is just another form of layoutMargins that can be used to create layout constraints. LayoutMarginsGuide is a read-only property.
safeAreaLayoutGuide
With iOS 11, Apple introduced the concept of Safe Area. Since the iPhone X on iOS 11 removed the Home button, it was necessary to reserve some space for Navigation Bar, Status Bar and Tab Bar. The safeAreaLayoutGuide property comes with Safe Area.
The safeAreaLayoutGuide property, like the layoutMarginsGuide property, is read-only because they all have a virtual region set by default, and we can set constraints directly based on that region.
NSLayoutAnchor
With a brief introduction to UILayoutGuide, let’s take a look at the members it contains. As you can see, UILayoutGuide internally defines a number of members of the NSLayoutAnchor type.
In fact, the NSLayoutAnchor class can use a set of apis to create constraint objects of type NSLayoutConstraint to set layout constraints without directly dealing with the NSLayoutConstraint object.
Normally, we don’t use NSLayoutAnchor directly, but use three subclasses of it, as follows:
NSLayoutXAxisAnchor
: X-axis anchor point used to create horizontal constraintsNSLayoutYAxisAnchor
: anchor point in the Y direction, used to create vertical constraintsNSLayoutDimension
: dimensioned anchor point used to create dimensioned constraints
The many anchor properties of UILayoutGuide can be grouped into one of the three sub-categories.
Anchor point property in the X-axis direction | Anchor point properties in the Y direction | Size dependent anchor point properties |
---|---|---|
centerXAnchor |
centerYAnchor |
widthAnchor |
leftAnchor |
topAnchor |
heightAnchor |
rightAnchor |
bottomAnchor |
|
leadingAnchor |
firstBaselineAnchor |
|
trailingAnchor |
lasBaselineAnchor |
Note: The effects of leadingAnchor and leftAnchor, trailingAnchor and rightAnchor are the same in most cases, but there are essential differences: LeadingAnchor represents the border anchor at the front of the view. In countries such as English that read from left to right, leading means left, but in countries such as Arabic that read from right to left, leading means right.
One view’s multiple anchor attributes (X, Y, dimensions) can interact with the corresponding position and dimension anchors of another view to determine the position and dimension of the view. Anchor relationships between views can create constraints through API calls. The premise is that only anchor properties of the same subclass can interact with each other. That is:
- The X-axis anchor attribute can only interact with the X-axis anchor attribute.
- Anchor attributes in the Y-axis direction can only interact with anchor attributes in the Y-axis direction.
- Dimensional anchor properties can only interact with dimensional anchor properties.
Let’s compare two ways to create a constraint:
- use
NSLayoutConstraint
Create constraints directly - use
NSLayoutAnchor
Indirect create constraint
// 1.Creating constraints using NSLayoutConstraint
NSLayoutConstraint(item: subview,
attribute: .leading,
relatedBy: .equal,
toItem: view,
attribute: .leadingMargin,
multiplier: 1.0,
constant: 0.0).isActive = true
NSLayoutConstraint(item: subview,
attribute: .trailing,
relatedBy: .equal,
toItem: view,
attribute: .trailingMargin,
multiplier: 1.0,
constant: 0.0).isActive = true
// 2. Creating the same constraints using Layout Anchors
let margins = view.layoutMarginsGuide
subview.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true
subview.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true
Copy the code
Some of the methods for creating constraints indirectly for the NSLayoutAnchor are shown below.
// Common API inherited from NSLayoutAnchor
func constraint(equalTo anchor: NSLayoutAnchor<AnchorType>) -> NSLayoutConstraint
func constraint(equalTo anchor: NSLayoutAnchor<AnchorType>, constant c: CGFloat) -> NSLayoutConstraint
/ / NSLayoutXAxisAnchor API
func constraint(equalToSystemSpacingAfter anchor: NSLayoutXAxisAnchor, multiplier: CGFloat) -> NSLayoutConstraint
func anchorWithOffset(to otherAnchor: NSLayoutXAxisAnchor) -> NSLayoutDimension
/ / NSLayoutYAxisAnchor API
func constraint(equalToSystemSpacingBelow anchor: NSLayoutYAxisAnchor, multiplier: CGFloat) -> NSLayoutConstraint
func anchorWithOffset(to otherAnchor: NSLayoutYAxisAnchor) -> NSLayoutDimension
/ / NSLayoutDimension API
func constraint(equalTo anchor: NSLayoutDimension, multiplier m: CGFloat) -> NSLayoutConstraint
func constraint(equalTo anchor: NSLayoutDimension, multiplier m: CGFloat, constant c: CGFloat) -> NSLayoutConstraint
func constraint(equalToConstant c: CGFloat) -> NSLayoutConstraint
Copy the code
VFL
VFL (Visual Format Language) is a Domain-specific Language (DSL) introduced by Apple to simplify Auto Layout coding.
grammar
instructions | The sample |
---|---|
The standard interval | [button]-[textField] |
The width of the constraint | [button(>=50)] |
Relationship to the superview | |-50-[purpleBox]-50-| |
Vertical layout | V:[topField]-10-[bottomField] |
Flush Views | [maroonView][blueView] |
priority | [button(100@20)] |
Width is equal | [button(==button2)] |
Multiple Predicates | [flexibleButton(>=70,<=100)] |
Note the following when creating a VFL statement description:
H:
和V:
You can only use one at a time- The view variable name appears in square brackets, as in:
[blueView]
- Order of statements: top to bottom, left to right
- View intervals appear as numeric constants, such as:
- 10 -
|
Represents the superview
The NSLayoutConstraint class provides apis that allow constraints to be created through VFL statements.
+ (NSArray<NSLayoutConstraint *> *)constraintsWithVisualFormat:(NSString *)format
options:(NSLayoutFormatOptions)opts
metrics:(NSDictionary<NSString *,id> *)metrics
views:(NSDictionary<NSString *,id> *)views;
Copy the code
Format indicates the VFL statement. Options indicates the constraint type. Metrics indicates the specific values used in the VFL statement. Views Indicates the control used in the VFL statement.
Let’s look at an example of creating constraints using VFL.
NSNumber *left = @50;
NSNumber *top = @50;
NSNumber *width = @100;
NSNumber *height = @100;
NSDictionary *views = NSDictionaryOfVariableBindings(view1, view2);
NSDictionary *metrics = NSDictionaryOfVariableBindings(left, top, width, height);
[view1 addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-left-[view(>=width)]" options:0 metrics:metrics views:views]];
Copy the code
Layout factors
The construction of a Layout is mainly done by the Layout Engine. Without a doubt, views are the object of building a layout. As the core of automatic layout, constraint is an important basis for constructing layout. In addition, the layout engine takes these factors into account when building a layout:
- Constraint Priorities
- Content Priorities
- Intrinsic Content Size
- Sizing Constraints
- Horizontal Alignment
- Vertical Alignment
- Baseline Alignment
- Alignment Rect
The size constraint
In fact, the constraints created in constraint creation above already contain size constraints. Here again, size constraints are addressed primarily for the self-sizing view.
For example, we can automatically calculate the Cell height of a TableView through automatic layout. However, this feature is not enabled by default.
By default, TabelView Cell height by agreement statement tableView: heightForRowAtIndexPath: method to determine. In addition, we can enable self-sizing by assigning two properties of the TabeView, as shown below:
tableView.estimatedRowHeight = 85.0
tableView.rowHeight = UITableViewAutomaticDimension
Copy the code
Next, we need to lay it out in the contentView of the TableView Cell. In order for the layout engine to automatically calculate the height of the Cell, we must define a good set of vertical constraints on the child views of the contentView, especially the height constraints. In the layout engine’s calculation of height, it uses dimension constraints first, and intrinsic content dimensions second.
Inherent content size & content priority
Some views in iOS have intrinsic content Size, which is the size occupied by the view’s content and margins. For example, the inherent content size of UIButton is equal to the size of the Title plus the content margin.
Views with inherent content dimensions have the following:
View | Intrinsic Content Size |
---|---|
Sliders | Defines the width, height, or both — depending on the slider’s type (OS X). |
Labels, buttons, switches, and text fields | Defines both the height and the width. |
Text views and image views | Intrinsic content size can vary. |
The size of the inherent content size is influenced by many factors. In the case of a UITextView, the inherent content size depends on the content, whether scrolling is enabled, and other constraints that apply to the UITextView. If scrollable, there is no inherent content size, if not, it depends on the size of all the text.
The size of the inherent content size is also influenced by the content priority, which has the following two aspects:
Content Hugging Priority
Content Compression Resistance Priority
Content Hugging Priority: Specifies the stretch Priority of a view. The higher the value, the higher the Priority, and the harder it seems to stretch.
Content Compressing Priority: indicates the compression Priority of a view. The higher the value, the higher the Priority and the harder it is to compress.
By default, the value of Content Select Priority is 250 and Content Compression Resistance Priority is 750. Therefore, it is easier to stretch a view than to compress it.
Intrinsic Content Size relationship with Fitting Size
The Intrinsic Content Size is the input to the layout engine from which constraints can be generated, and ultimately the layout; Fitting Size, on the other hand, is the output of the layout engine and is the result of the layout generated based on constraints.
alignment
There are three types of alignment:
- Horizontal alignment
- The vertical alignment
- Baseline alignment
For the first two, through the previous description we also have some understanding. Horizontal alignment, used to create constraints on the X-axis; Vertical alignment, used to create constraints on the Y axis.
Baseline alignment is a proprietary alignment for text. Baseline alignment includes firstBaseline and lastBaseline. As follows:
Align the rectangular
In automatic layout, we might think that the constraint uses a frame to determine the size and position of the view, but in fact it uses an alignment rect. In most cases, frame and alignment rect are equal, so this is not a bad idea.
So why use alignment Rect instead of Frame?
Sometimes, when creating complex views, we may add decorative elements such as shadows, corner markers, etc. In order to reduce the development cost, we will directly use the cut drawings given by the designer. As follows:
Where, (a) is the cut diagram given by the designer, and (c) is the frame of this diagram. Obviously, we do not want to include shadows and markers in the layout (the center and bottom and right edges of the view are offset), but only the central core, as shown in the box in Figure (b).
Alignment rectangles are used to deal with this. UIView provides methods to get an alignment rect from a frame and a frame from an alignment rect.
// The alignment rectangle for the specified frame.
- (CGRect)alignmentRectForFrame:(CGRect)frame;
// The frame for the specified alignment rectangle.
- (CGRect)frameForAlignmentRect:(CGRect)alignmentRect;
Copy the code
In addition, the system provides a simple method with UIEdgeInsets to specify the relationship between frame and alignment rect.
// The insets from the view’s frame that define its alignment rectangle.
- (UIEdgeInsets)alignmentRectInsets;
Copy the code
If you want an alignment rect with 10 more points than the bottom of the frame, you can write:
- (UIEdgeInsets)alignmentRectInsets {
return UIEdgeInsetsMake(. 0.. 0.10.0.. 0);
}
Copy the code
Layout of the rendering
IOS layout rendering can be divided into three stages, as shown below:
- Constraints Update
- Layout Update
- Display Redraw
Each step is dependent on the previous one. Display redraw depends on layout updates, and layout updates depend on constraint updates.
Constraint updated
Constraint updates occur from the bottom up, from the child view to the parent view. We can call setNeedsUpdateConstraints update to trigger the constraints. Of course, we have a lot of respect for layout factors (constraints/content priority, constraints, inherent content size…) Any changes made will automatically trigger setNeedsUpdateConstraints method.
For custom views, we can rewrite updateConstraints during the constraint update phase to add the required local constraints to the view.
Layout update
Layout updates are top-down, from parent to child views. In fact, the layout update operation applies the results of the layout engine calculations to the view by setting frame (OS X) or Center and Bounds (iOS). We can trigger layout updates by calling setNeedsLayout. This does not apply the layout immediately, but rather delays processing. All layout requests will be merged into one layout operation. This Deferred processing process is called a Deferred Layout Pass.
We can call layoutIfNeeded (iOS) or layoutSubtreeIfNeeded (OS X) to force the system to update the layout of the view tree immediately. This is useful if our next steps depend on updating the frame in the rear view.
For custom views, we can override layoutSubviews (iOS) or Layout (OS X) during the layout update phase to gain ownership of control over layout changes.
According to redraw
Display redraw top-down (from parent view to child view). We can trigger the display redraw by calling setNeedsDisplay, which causes all calls to be merged together to delay the redraw.
For custom views, we can re-draw Rect: during the display redraw phase to gain ownership of the self-display process.
Matters needing attention
It is important to note that these three phases are not one-way. Constraint-based layout is an iterative process. A layout update can make changes to the constraints based on the previous layout, which again triggers a constraint update, followed by another layout update. This can be used to create advanced custom view layouts. But if every call to custom layoutSubviws resulted in another layout operation, we would be stuck in an infinite loop.
Automatic layout for different versions of iOS
iOS 6
- Apple introduced auto layout with all the core features in this release.
iOS 7
- NavigationBar, TabBar, ToolBar
translucent
Property defaults toYES
. The height of the current ViewController is the height of the entire screen, which can be used in the layout to ensure it is not overwritten by these barstopLayoutGuide
和bottomLayoutGuide
Properties.
iOS 8
- The Self – Sizing Cells. Portal: www.appcoda.com/self-sizing…
UIViewController
Two new methods are added for processingUITraitEnvironment
The agreement. Are there in UIKitUIScreen
,UIViewController
,UIPresentationController
Support the protocol. When the view traitCollection changes,UIViewController
This message can be captured for processing.
- (void)setOverrideTraitCollection:(UITraitCollection *)collection forChildViewController:(UIViewController *)childViewController NS_AVAILABLE_IOS(8_0);
- (UITraitCollection *)overrideTraitCollectionForChildViewController:(UIViewController *)childViewController NS_AVAILABLE_IOS(8_0);
Copy the code
- The Size of the Class.
UIViewController
A new set of protocol support is providedUIContentContainer
.
- (void)systemLayoutFittingSizeDidChangeForChildContentContainer:(id )container NS_AVAILABLE_IOS(8_0);
- (CGSize)sizeForChildContentContainer:(id )container withParentContainerSize:(CGSize)parentSize NS_AVAILABLE_IOS(8_0);
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id )coordinator NS_AVAILABLE_IOS(8_0);
- (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id )coordinator NS_AVAILABLE_IOS(8_0);
Copy the code
UIView
Margin added 3 new apisNSLayoutMargins
You can define the distance between views. Valid only for automatic layouts, and the default is{8, 8, 8, 8}
.NSLayoutAttribute
The enumeration values of the.
// UIView's 3 margin-related apis
@property (nonatomic) UIEdgeInsets layoutMargins NS_AVAILABLE_IOS(8_0);
@property (nonatomic) BOOL preservesSuperviewLayoutMargins NS_AVAILABLE_IOS(8_0);
- (void)layoutMarginsDidChange NS_AVAILABLE_IOS(8_0);
// The NSLayoutAttribute enumeration is updated
NSLayoutAttributeLeftMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeRightMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeTopMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeBottomMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeLeadingMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeTrailingMargin NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeCenterXWithinMargins NS_ENUM_AVAILABLE_IOS(8_0),
NSLayoutAttributeCenterYWithinMargins NS_ENUM_AVAILABLE_IOS(8_0),
Copy the code
iOS 9
UIStackView
: provides simpler automatic layout methods, such as alignment fill, leading, Center, trailing. Distribution’s fill, fill equally, fill proportionally, equal spacing.NSLayoutAnchor
API
NSLayoutConstraint *constraint = [view1.leadingAnchor constraintEqualToAnchor:view2.topAnchor];
Copy the code
reference
- Solving Linear Arithmetic Constraints for User Interface Applications
- Visual Format Language
- Understanding Auto Layout
- NSLayoutAnchor basic knowledge
- Analyze Auto Layout and new features of iOS versions
- IOS automatic layout basics
- WWDC 2015 session 218 Mysteries of Auto Layout Part1
- WWDC 2015 session 219 Mysteries of Auto Layout Part2
- WWDC 2018 session 220 High Performance Auto Layout
- Advanced Auto Layout Toolbox
- WWDC 2015 – Mysteries Of AutoLayout (Mysteries Of AutoLayout)
Further reading
- IOS uses AutoLayout to automatically adjust view intervals
- Ios Auto Layout DemyStified
- Advanced automatic layout toolbox
- About UIView translatesAutoresizingMaskIntoConstraints properties
- Masonry
- IOS layout Rendering — UIView method call timing
- UIStackView basics
(after)