The pure code implementation of AutoLayout with more intentions in history

Overview

Using Objective-C pure code to write AutoLayout, the literal understanding of AutoLayout is automatic layout, which sounds like a dick. To put it bluntly, it is adaptation: adaptation to and compatibility with various situations, including adaptation of different versions of the operating system (system adaptation) and adaptation of different screen sizes (screen adaptation).

In Storyboard, AutoLayout has the following 3 common panels:

Align (alignment)


Pin (relative)


Resolve Auto Layout Issues (constraint processing)


Implementing AutoLayout in Storyboard I will not explain it in this article, because it is against the original intention of not forgetting the original intention. Gotta keep the title up.

Talk is cheap, show me the code

Let's talk about the steps to implement AutoLayout with code, don't blink:

use the NSLayoutConstraint class to create specific constraint objects;

add constraint objects to the corresponding view, there are two types of code:
- (void)addConstraint:(NSLayoutConstraint *)constraint;
- (void)addConstraints:(NSArray *)constraints;

Maybe someone asked, it turns out that only two steps are enough. I just took off my pants, so you can show me this? !

Without further ado, show you the code!

Let's first see how the way we use the frame determines the position of a view:
- (void)viewDidLoad {
    [super viewDidLoad];
    self.title = @"How to use frame";
    UIView *purpleView = [[UIView alloc] initWithFrame:CGRectMake(100, 200, 150, 150)];
    purpleView.backgroundColor = [UIColor purpleColor];
    [self.view addSubview:purpleView];
}

The code is very simple, and the effect is as follows:

Let's take a look at the implementation of AutoLayout:
- (void)viewDidLoad {
    [super viewDidLoad];
    self.title = @"How to use AutoLayout";
    UIView *purpleView = [[UIView alloc] init];
    purpleView.backgroundColor = [UIColor purpleColor];
    // Disable converting AutoresizingMask to Constraints
    purpleView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:purpleView];
    // add width constraint
    NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:purpleView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant:150];
    [purpleView addConstraint:widthConstraint];
    // add height constraint
    NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:purpleView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant:150];
    [purpleView addConstraint:heightConstraint];
    // add left constraint
    NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:purpleView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:100];
    [self.view addConstraint:leftConstraint];
    // add top constraint
    NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:purpleView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:200];
    [self.view addConstraint:topConstraint];
}

After reading this code, I got a shock! I was frightened by this large piece of code. Many children's shoes may be frightened away when they see that such a simple layout needs to write so much code. I can only say one thing: don't go, let me explain slowly ~

common methods of creating constraint objects (NSLayoutConstraint)

An NSLayoutConstraint object represents a constraint.
+ (id)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c;

There are a total of 7 parameters, let's take leftConstraint as an example to introduce these 7 parameters.
  • view1: the control to be constrained (purpleView)
  • attr1: The type of constraint (constant), which is what kind of constraint to do, you can go in and see what constants are there (here is NSLayoutAttributeLeft)
  • relation: The relationship (constant) with the reference control, including equal, greater than or equal, less than or equal (NSLayoutRelationEqual means equal)
  • view2: the referenced control (self.view)
  • attr2: The type of constraint (constant), which is what kind of constraint to do, you can go in and see what constants (here is NSLayoutAttributeLeft) (NSLayoutAttributeLeft)
  • multiplier: multiplier, how many times (1.0)
  • c: constant, this constant (100) will be added after the above constraints are made

So leftConstraint represents: the left spacing of the control purpleView to be constrained is equal to 1.0 times the left spacing of the reference control self.view plus 100.

So we get the core calculation formula of AutoLayout:
obj1.property1 =(obj2.property2 * multiplier)+ constant value

Rules for adding constraints (addConstraint)

After a constraint is created, it needs to be added to the active control to take effect. Note that the target control needs to follow the following rules when adding constraints (here the control is simply represented by view):

(1 ) For the constraint relationship between two views of the same level, add to their parent view

(2) For the constraint relationship between two views of different levels, add to their nearest common parent view

(3) For the hierarchical view The constraint relationship between the two views of the relationship is added to the parent view with a higher level

(4) For things such as length and width, if it only acts on the view itself, add it to the view itself, no need for pictures Bar.

It can be seen that widthConstraint and Constraint belong to category (4), and leftConstraint and rightConstraint belong to category (3).

Precautions for Code Implementation of AutoLayout

If you just create and add constraints, it will not work properly. You must do the following:

(1) You must first disable the autoresizing function to prevent AutoresizingMask from being converted into Constraints to avoid conflicts. You need to set the view's The following properties are NO:
view.translatesAutoresizingMaskIntoConstraints = NO;

(2) Before adding constraints, make sure that the related controls are already on their respective parent controls. The above example is [self.view addSubview:purpleView]; must be placed before adding the left constraint, otherwise the program will crash, because it is necessary to ensure that the purpleView is already on self.view. It is recommended to write [self.view addSubview:purpleView]; first, and then concentrate on writing constraints.

(3) You don't need to set the frame for the view to

see it. For such a simple interface, if you use AutoLayout to implement it, you need so much code. It doesn't feel so convenient, right?

In fact, AutoLayout depends on the application content. The above is just a demo used. If your content is a lot of information, there are also many categories to be displayed, and the size is dynamic, such as Weibo list, QQ dynamic list, etc., writing these complex interfaces and using AutoLayout can give (jǐ yǔ??) Great help .

In order to simplify the complex code of AutoLayout, Apple developed a VFL language (Visual format language). In fact, I did not see much simplification, and there are still relatively large limitations. I will not introduce it here. If you want to know about the children's shoes, go to Google.

Forget it, give an official link: Visual Format Language .

How to code AutoLayout elegantly?

Seeing Apple's own implementation of AutoLayout, I feel really disgusting, so how to code AutoLayout elegantly?

- Use the third-party framework Masonry. GitHub: https://github.com/SnapKit/Masonry, looking at its introduction, I feel pretty awesome:

Harness the power of AutoLayout NSLayoutConstraints with a simplified, chainable and expressive syntax. Supports iOS and OSX Auto Layout.

Read README The .md file found it to be quite elegant indeed.

Let's take a look at how Masonry implements AutoLayout:
#import "ViewController.h"
#import "Masonry.h" // Use quotation marks for third-party or self-written ones, and double quotation marks are used by the system.
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    UIView *purpleView = [[UIView alloc] init];
    purpleView.backgroundColor = [UIColor purpleColor];
    [self.view addSubview:purpleView];
    [purpleView mas_makeConstraints:^(MASConstraintMaker *make) {
        // Inside this block, use the make object to create constraints
        make.size.mas_equalTo(CGSizeMake(100, 100));
        make.center.mas_equalTo(self.view);
    }];
}

Running effect:

Create a view with a length and width of 100, centered with the parent view

Note: purpleView.translatesAutoresizingMaskIntoConstraints = NO; no need to write it here, because Masonry has already written it.

Masonry, drive, get in the car, follow

step by step, hahaha
// The length and width are both 100, sticking to the lower right corner of the parent view
[purpleView mas_makeConstraints:^(MASConstraintMaker *make) {
  make.width.equalTo(@100);
  make.height.equalTo(@100);
  make.right.equalTo(self.view);
  make.bottom.equalTo(self.view);
}];


// The length and width are both 100, stick to the lower right corner of the parent view, and the spacing is 16
[purpleView mas_makeConstraints:^(MASConstraintMaker *make) {
     make.width.equalTo(@100);
     make.height.equalTo(@100);
     // You can also write make.right.equalTo(self.view.mas_right).offset(-16);
     // In order to enhance readability, you can add .with or .and before .offset: make.right.equalTo(self.view).with.offset(-16); see your own habits
     make.right.equalTo(self.view).offset(-16);
     // You can also write make.right.equalTo(self.view.mas_bottom).offset(-16);
     make.bottom.equalTo(self.view).offset(-16);
}];


Seeing the packaged @100 in the above code, you can actually pass the value of 100 directly, but you need to change equalTo to mas_equalTo, so that it will automatically wrap it for you.
make.width.mas_equalTo(100);
make.height.mas_equalTo(100);

In fact, mas_equalTo is a macro, you can go in and see the definition.
  • The mas_equalTo method wraps the parameter
  • The equalTo method does not wrap the parameter
  • mas_equalTo is more powerful than equalTo

You may feel a little dizzy. Sometimes you use mas_equalTo, sometimes you use equalTo. In fact, you can define two macros in the pch file, which can perfectly solve this tangled problem. Note that it should be written before #import "Masonry.h".
//define this constant if you want to use Masonry without the 'mas_' prefix, so that `mas_width` etc can be written as `width`
#define MAS_SHORTHAND
//define this constant if you want to enable auto-boxing for default syntax, so there is no difference between `mas_equalTo` and `equalTo`
#define MAS_SHORTHAND_GLOBALS

Well, now for a slightly more complicated interface:
- (void)viewDidLoad {
    [super viewDidLoad];
    UIView *purpleView = [[UIView alloc] init];
    purpleView.backgroundColor = [UIColor purpleColor];
    [self.view addSubview:purpleView];
    UIView *orangeView = [[UIView alloc] init];
    orangeView.backgroundColor = [UIColor orangeColor];
    [self.view addSubview:orangeView];
    CGFloat margin = 16;
    CGFloat height = 32;
    [purpleView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.view).offset(margin);
        make.bottom.equalTo(self.view).offset(-margin);
        make.right.equalTo(orangeView.left).offset(-margin);
        make.height.equalTo(height);
        make.width.equalTo(orangeView);
    }];
    [orangeView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.bottom.equalTo(self.view).offset(-margin);
        make.right.equalTo(self.view).offset(-margin);
        make.height.equalTo(height);
    }];
}


Two views of equal height and width divide the screen width equally, with a gap.

In fact, there are many ways to write this interface. You can try it. For example, write it like this:
- (void)viewDidLoad {
    ...
    [purpleView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.view).offset(margin);
        make.bottom.equalTo(self.view).offset(-margin);
        make.right.equalTo(orangeView.left).offset(-margin);
        make.height.equalTo(height);
        make.height.equalTo(orangeView);
        make.width.equalTo(orangeView);
        make.top.equalTo(orangeView);
    }];
    [orangeView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.right.equalTo(self.view).offset(-margin);
    }];
}

Summary

In fact, Masonry's documentation is already very detailed, it is recommended that you read the documentation

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326169306&siteId=291194637