# Series :iOS development – Navigation

As a developer, we definitely need to deal with layout. We may use handwritten frame or bounce at the beginning, and later we may conveniently use XIB or storyboard to pull constraints. We’ll find it convenient to use constraints,autolayout, but only for constraints pulled in xiB or storyboard, if we write them by hand, you’ll find it too verbose.

Thus, an efficient tripartial framework for navigation will appear. It is a lightweight layout framework with its own description syntax and elegant chained syntax for automatic layout, which is simple, easy to read and supports iOS and Max OS X. As for learning, the most effective way is to download the official demo to learn. I’m not going to write my own demo here.

Masonry source: https://github.com/Masonry/Masonry

Download and double-click to open the project

int padding = 10;

//ifyou want to use Masonry without the mas_ prefix //define MAS_SHORTHAND before importing Masonry.h see Masonry iOS Examples-Prefix.pch [greenView makeConstraints:^(MASConstraintMaker *make) { make.top.greaterThanOrEqualTo(superview.top).offset(padding); make.left.equalTo(superview.left).offset(padding);  make.bottom.equalTo(blueView.top).offset(-padding); make.right.equalTo(redView.left).offset(-padding);  make.width.equalTo(redView.width); make.height.equalTo(redView.height); make.height.equalTo(blueView.height); }];Copy the code

If you want to use something short for top instead of mas_top, add a macro definition to your PCH file, which we’ll look at here

MakeConstraints simply adds new constraints mas_updateConstraints this method will overwrite certain previous constraints and remove all previous constraints, Autolayout cannot have two constraints on the same object at the same time or else an error will be reported. Mas_updateConstraints updates the constraints that appear in the block for the situation above Instead of causing two constraints to appear the same, mas_remakeConstraints removes all previous constraints and keeps only the latest one among the three aints functions to make use of it for all sorts of situations

It’s pretty simple. It’s basically the same way we did with xiB. It’s also pretty easy to read: make.left.equalto (superview.left).offset(padding); Make.bottom.equalto (blueview.top).offset(-padding); Make the bottom equal to the top of the blue view offset by -10 pixels

Let’s click on Top or something else to navigate to the navigation page to see what properties are supported

@property (nonatomic, strong, readonly) MASConstraint *left;
@property (nonatomic, strong, readonly) MASConstraint *top;
@property (nonatomic, strong, readonly) MASConstraint *right;
@property (nonatomic, strong, readonly) MASConstraint *bottom;
@property (nonatomic, strong, readonly) MASConstraint *leading;
@property (nonatomic, strong, readonly) MASConstraint *trailing;
@property (nonatomic, strong, readonly) MASConstraint *width;
@property (nonatomic, strong, readonly) MASConstraint *height;
@property (nonatomic, strong, readonly) MASConstraint *centerX;
@property (nonatomic, strong, readonly) MASConstraint *centerY;
@property (nonatomic, strong, readonly) MASConstraint *baseline
Copy the code

Same as our constraints, up, down, left, right, width, height, center point, etc., so for the general constraints, we are now completely customizable and of course we can pay attention to this as well

Now look at the next item in the list, MASExampleUpdateView

+ (BOOL)requiresConstraintBasedLayout
{
    return YES;
}

// this is Apple's recommended place for adding/updating constraints - (void)updateConstraints { [self.growingButton updateConstraints:^(MASConstraintMaker *make) { make.center.equalTo(self);  make.width.equalTo(@(self.buttonSize.width)).priorityLow();  make.height.equalTo(@(self.buttonSize.height)).priorityLow(); make.width.lessThanOrEqualTo(self);  make.height.lessThanOrEqualTo(self); }]; //according to apple super should be called at end of method [super updateConstraints]; } - (void)didTapGrowButton:(UIButton *)button {self.buttonsize = CGSizeMake(self.buttonsize.width * 1.3, Self. ButtonSize. Height * 1.3); // tell constraints they need updating [self setNeedsUpdateConstraints]; // update constraints now so we can animate the change [self updateConstraintsIfNeeded]; [UIView animateWithDuration:0.4 animations:^{[self layoutIfNeeded];}]; }Copy the code

First be requiresConstraintBasedLayout introduces the constraint – so -based layout engages lazily when someone tries to use it (e.g., adds a constraint to a view). If you do all of your constraint set up in -updateConstraints, you might never even receive updateConstraints if no one makes a constraint. To fix this chicken and egg problem, override this method to return YES if your view needs the window to use constraint-based layout. UpdateConstraints means that constraint-based layouts are lazy and only automatically call updateConstraints if a constraint is added. If you put all your constraints in updateConstraints, the system will not know that your layout is constraint-based. So rewrite + requiresConstraintBasedLayout returns YES is clearly told system: even though I didn’t add restrictions, but I do is based on the constraints of the layout! This ensures that the system calls the -updateconstraints method to add the constraints correctly.

We can see that this time the navigation constraint is written in updateConstraints. This is just a little bit of knowledge. Next is the way to update, as long as the code is these few

// tell constraints they need updating
    [self setNeedsUpdateConstraints]; // update constraints now so we can animate the change [self updateConstraintsIfNeeded]; [UIView animateWithDuration:0.4 animations:^{[self layoutIfNeeded];}];Copy the code

Using the above method to update the constraints, the system calls updateConstraints again to update the constraints, which animates the animation and allows you to view it by clicking on the button that makes it 1.3 times wider and taller than it was before

[self.growingButton updateConstraints:^(MASConstraintMaker *make) { make.center.equalTo(self);  make.width.equalTo(@(self.buttonSize.width)).priorityLow();  make.height.equalTo(@(self.buttonSize.height)).priorityLow(); make.width.lessThanOrEqualTo(self);  make.height.lessThanOrEqualTo(self); }];Copy the code

In this demo, we can look at the logic of the call and update constraints for priority. First, the center position is the same as the center of the parent view. Second, the width and height priority is less than the width and height of the parent view, which is equal to the specified width and height

Next up is the MASExampleRemakeView and I’m not going to introduce the update constraint method used here

[self.movingButton remakeConstraints:^(MASConstraintMaker *make) { make.width.equalTo(@(100));  make.height.equalTo(@(100));if (self.topLeft) {
            make.left.equalTo(self.left).with.offset(10);
            make.top.equalTo(self.top).with.offset(10);
        }
        else{ make.bottom.equalTo(self.bottom).with.offset(-10); make.right.equalTo(self.right).with.offset(-10); }}];Copy the code

This is also implemented in the updateConstraints method to remove the previous constraint and add a new one

So again, MASExampleSidesView, we’re going to see a lot of views, and it’s going to take us a long time to write this

Well, I made a mistake and it’s only a few lines

UIView *lastView = self;
    for(int i = 0; i < 10; i++) { UIView *view = UIView.new; view.backgroundColor = [self randomColor]; view.layer.borderColor = UIColor.blackColor.CGColor; view.layer.borderWidth = 2; [self addSubview:view]; [view mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(lastView).insets(UIEdgeInsetsMake(5, 10, 15, 20));}]; lastView = view; }Copy the code

A loop is created. Here we can see a new keyword edges. Yes, this is a new keyword that represents the entire edge

- (MASConstraint *)edges {
    return [self addConstraintWithAttributes:MASAttributeTop | MASAttributeLeft | MASAttributeRight | MASAttributeBottom];
}

- (MASConstraint *)size {
    return [self addConstraintWithAttributes:MASAttributeWidth | MASAttributeHeight];
}

- (MASConstraint *)center {
    return [self addConstraintWithAttributes:MASAttributeCenterX | MASAttributeCenterY];
}
Copy the code

It helps you to realize the three methods of edge size center, so we can also try to abbreviate the previous base way to see.. And then insets and what we see is the spacing of groups like collectionView, the pixel difference between the top, the bottom, the left and the right, that’s easy to understand

- (MASConstraint * (^)(MASEdgeInsets))insets {
    return ^id(MASEdgeInsets insets){
        self.insets = insets;
        return self;
    };
}

- (MASConstraint * (^)(CGFloat))inset {
    return ^id(CGFloat inset){
        self.inset = inset;
        return self;
    };
}

- (MASConstraint * (^)(CGSize))sizeOffset {
    return ^id(CGSize offset) {
        self.sizeOffset = offset;
        return self;
    };
}

- (MASConstraint * (^)(CGPoint))centerOffset {
    return ^id(CGPoint offset) {
        self.centerOffset = offset;
        return self;
    };
}

- (MASConstraint * (^)(CGFloat))offset {
    return ^id(CGFloat offset){
        self.offset = offset;
        return self;
    };
}

- (MASConstraint * (^)(NSValue *value))valueOffset {
    return ^id(NSValue *offset) {
        NSAssert([offset isKindOfClass:NSValue.class], @"expected an NSValue offset, got: %@", offset);
        [self setLayoutConstantWithValue:offset];
        return self;
    };
}
Copy the code

In addition, it implements insets\inset\sizeOffset\centerOffset\offset. We can notice these are all Modifies the NSLayoutConstraint constant, which means modifying the constraint, and we can see that they are all ways of adding some fine tuning after fixing the size… It comes in handy when we use it wisely

Now to get a little more complicated, let’s look at the MASExampleAspectFitView demo, which first creates four views

// Create views self.topView = [[UIView alloc] initWithFrame:CGRectZero]; self.topInnerView = [[UIView alloc] initWithFrame:CGRectZero]; self.bottomView = [[UIView alloc] initWithFrame:CGRectZero]; self.bottomInnerView = [[UIView alloc] initWithFrame:CGRectZero]; // Set background colors UIColor *blueColor = [UIColor colorWithRed:0.663 green:0.796 blue:0.996 alpha:1]; // Set background colors UIColor *blueColor = [UIColor colorWithRed:0.663 green:0.796 blue:0.996 alpha:1] [self.topViewsetBackgroundColor:blueColor];

        UIColor *lightGreenColor = [UIColor colorWithRed:0.784 green:0.992 blue:0.851 alpha:1];
        [self.topInnerView setBackgroundColor:lightGreenColor];

        UIColor *pinkColor = [UIColor colorWithRed:0.992 green:0.804 blue:0.941 alpha:1];
        [self.bottomView setBackgroundColor:pinkColor];
        
        UIColor *darkGreenColor = [UIColor colorWithRed:0.443 green:0.780 blue:0.337 alpha:1];
        [self.bottomInnerView setBackgroundColor:darkGreenColor];
Copy the code

That’s not what we’re going to focus on, but let’s look at the constraints and let’s look at the top two

// Layout top and bottom views to each take up half of the window [self addSubview:self.topView]; [self.topView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.and.top.equalTo(self); }]; [self addSubview:self.bottomView]; [self.bottomView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.and.bottom.equalTo(self);  make.top.equalTo(self.topView.mas_bottom); make.height.equalTo(self.topView); }];Copy the code

Here we see the implementation of the chain programming idea of passing multiple operations through. Together to become a code make. Left. Right. And the top. The equalTo (self); Let, left and right are consistent with the parent view, and as for the chain programming ideas, follow-up will be introduced, here aside, the effect is not to need consider the call order, just need to know to consider as a result, similar to the butterfly effect, produce an event that affects a lot of things, these events spread out like a stream, and then influence the results, to borrow a word from the object-oriented, All things flow. About the above constraints are fixed on The following constraint is fixed or so, then let the top to keep up with the bottom of the below And two height, so that we can see the effect

[self.topView addSubview:self.topInnerView]; [self.topInnerView mas_makeConstraints:^(MASConstraintMaker *make) { make.width.equalTo(self.topInnerView.mas_height).multipliedBy(3); make.width.and.height.lessThanOrEqualTo(self.topView);  make.width.and.height.equalTo(self.topView).with.priorityLow(); make.center.equalTo(self.topView); }];Copy the code

Width is 3 times height, and there’s a new constraint, multipliedBy(3) and, of course, there’s nothing there, it’s just for reading and writing Set the width and height to be less than or equal to the width and height of the superview, the location of the center point, and make sure that it is a position and size view in the center of the superview that is the same width as the superview and the height is 1/3 of the width

The multipler property indicates that the constraint value is the multiplicative factor of the constraint object, and the dividedBy property indicates that the constraint value is the divisor of the constraint object

For example, we could write make.height.equalto (self.topinnerView.mas_width).dividedby (3); Same effect

Next comes the animation-based constraint MASExampleAnimatedView

The first constraint is the same three views as Base, just in a different way

UIView *superview = self; int padding = self.padding = 10; UIEdgeInsets paddingInsets = UIEdgeInsetsMake(self.padding, self.padding, self.padding, self.padding); self.animatableConstraints = NSMutableArray.new; [greenView mas_makeConstraints:^(MASConstraintMaker *make) { [self.animatableConstraints addObjectsFromArray:@[ make.edges.equalTo(superview).insets(paddingInsets).priorityLow(), make.bottom.equalTo(blueView.mas_top).offset(-padding), ]]; make.size.equalTo(redView);  make.height.equalTo(blueView.mas_height); }]; [redView mas_makeConstraints:^(MASConstraintMaker *make) { [self.animatableConstraints addObjectsFromArray:@[ make.edges.equalTo(superview).insets(paddingInsets).priorityLow(), make.left.equalTo(greenView.mas_right).offset(padding), make.bottom.equalTo(blueView.mas_top).offset(-padding), ]];  make.size.equalTo(greenView); make.height.equalTo(blueView.mas_height); }]; [blueView mas_makeConstraints:^(MASConstraintMaker *make) { [self.animatableConstraints addObjectsFromArray:@[ make.edges.equalTo(superview).insets(paddingInsets).priorityLow(), ]]; make.height.equalTo(greenView.mas_height);  make.height.equalTo(redView.mas_height); }];Copy the code

Finally we see at the bottom of the priority, the first is to determine the relationship between the position of each view, green here is blue, red on the left is green, here are blue, and three contour, and then see the low priority constraint to supplement, respectively, are all the same spacing distance parent view, so that we can imagine the interface constraint relations form the effect

int padding = invertedInsets ? 100 : self.padding;
    UIEdgeInsets paddingInsets = UIEdgeInsetsMake(padding, padding, padding, padding);
    for (MASConstraint *constraint inself.animatableConstraints) { constraint.insets = paddingInsets; } [UIView animateWithDuration:1 animations:^{ [self layoutIfNeeded]; } completion:^(BOOL finished) { //repeat!  [self animateWithInvertedInsets:!invertedInsets]; }];Copy the code

Modify some of the attributes to animate them by taking one of the constraints in this case insetSD attributes, but you can try other attributes as well

MASExampleDebuggingView, which I won’t elaborate on, is used to check the values of the constraints to see if they are correct

So what we’re going to do is we’re not going to talk about MASExampleLabelView constraints but what are we going to do

- (void)layoutSubviews {
    [super layoutSubviews];

    // for multiline UILabel's you need set the preferredMaxLayoutWidth // you need to do this after [super layoutSubviews] as the frames will have a value from Auto Layout at this point // stay tuned for new easier way todo this coming soon to Masonry CGFloat width =  CGRectGetMinX(self.shortLabel.frame) - kPadding.left; width -= CGRectGetMinX(self.longLabel.frame); self.longLabel.preferredMaxLayoutWidth = width; // need to layoutSubviews again as frames need to recalculated with preferredLayoutWidth [super layoutSubviews]; }Copy the code

It says here that when you want to set a multi-line label you need to set the maximum width, you need to set it in [super] LayoutSubviews] then sets a value so that the width is set, and after that all constraints are in place because in makeConstraints we see that the upper left is set, the position is set, but the size is not fixed, so we need to set the width to determine the height of the label Degree, can continue to rely on his other constraints

Let’s move on to the MASExampleScrollView A scrollerView can be dragged. It has a contentSize attribute. If it is not set properly, it will probably not be able to be pulled down to the bottom

[contentView makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self.scrollView);  make.width.equalTo(self.scrollView); }]; [contentView makeConstraints:^(MASConstraintMaker *make) { make.bottom.equalTo(lastView.bottom); }];Copy the code

Add a contentView to the scrollerView and set its position and size. I don’t need to set the contentSize anymore, so I’m using the bottom here and I’m not going to be able to define the contentSize if I need to add the automatic layout. Mas_leading and mas_trailing are located according to the contentSize, not the frame of the ScrollView, so the layout is not the desired result. (dependency) If the contentSize of the scrollView is set, but in the case of automatic layout, the contentSize is not as set, but is defined by the content constraint (leading,trailing), The post layout code redefines the contentSize. The height of the contentSize is the same as the height of the top and bottom so why can I slide the top? Because we set the top, bottom, left, and right sides of the contentView, the contentSize of the scrollerView is the size of the contentView

Next up is the MASExampleArrayView, where the idea is to put several controls together to control constraints

- (void)setOffset:(CGFloat)offset {
    _offset = offset;
    [self setNeedsUpdateConstraints];
}

- (void)updateConstraints {
    [self.buttonViews updateConstraints:^(MASConstraintMaker *make) {
        make.baseline.equalTo(self.mas_centerY).with.offset(self.offset);
    }];
    
    //according to apple super should be called at end of method
    [super updateConstraints];
}
Copy the code

Here buttonViews is an array of controls. As for the content of the constraints, it is still the ones we learned before, without description

Came to MASExampleAttributeChainingView or familiar interface, three views

[greenView mas_makeConstraints:^(MASConstraintMaker *make) {
        // chain attributes
        make.top.and.left.equalTo(superview).insets(padding);

        // whichis the equivalent of // make.top.greaterThanOrEqualTo(superview).insets(padding); // make.left.greaterThanOrEqualTo(superview).insets(padding); make.bottom.equalTo(blueView.mas_top).insets(padding); make.right.equalTo(redView.mas_left).insets(padding); make.width.equalTo(redView.mas_width); make.height.equalTo(@[redView, blueView]); }]; [redView mas_makeConstraints:^(MASConstraintMaker *make) { // chain attributes make.top.and.right.equalTo(superview).insets(padding); make.left.equalTo(greenView.mas_right).insets(padding);  make.bottom.equalTo(blueView.mas_top).insets(padding); make.width.equalTo(greenView.mas_width);  make.height.equalTo(@[greenView, blueView]); }]; [blueView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(greenView.mas_bottom).insets(padding);  // chain attributes make.left.right.and.bottom.equalTo(superview).insets(padding);  make.height.equalTo(@[greenView, redView]); }];Copy the code

In terms of constraints or previous constraints, what are we talking about? And actually, as I said before, it’s using the lotus idea of constraint. To connect multiple attributes into the make a word. Left. Right. And the bottom. The equalTo (superview). Insets (padding); Let the left, right,and down be equal to some constraint value, there is no order requirement,and can be understood as a link, there is no function, just as read and write smoothly.

Without further discussion of MASExampleMarginView, we will know from using xib that after iOS8.0 constraints will have a Margin property

As for MASExampleDistributeView, we can see the interface

[arr mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedSpacing:20 leadSpacing:5 tailSpacing:5];
            [arr makeConstraints:^(MASConstraintMaker *make) {
                make.top.equalTo(@60);
                make.height.equalTo(@60);
            }];
Copy the code

[arr mas_distributeViewsAlongAxis:MASAxisTypeVertical withFixedSpacing:20 leadSpacing:5 tailSpacing:5];
            [arr makeConstraints:^(MASConstraintMaker *make) {
                make.left.equalTo(@0);
                make.width.equalTo(@60);
            }];
Copy the code

This is a longitudinal isometric distance

explainCopy the code

/ * *

  • Determine spacing for equally spaced layouts
  • @param axisType Layout direction
  • The spacing between two items (the left-most item and the left, and the right-most item and the right are different)
  • @param leadSpacing The first item to the parent view margin
  • The last item to the parent view margin */
  • (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedSpacing:(CGFloat)fixedSpacing leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;


[arr mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedItemLength:30 leadSpacing:200 tailSpacing:30];
            [arr makeConstraints:^(MASConstraintMaker *make) {
                make.top.equalTo(@60);
                make.height.equalTo(@60);
            }];
Copy the code

This is a little bit different, using a different method

** * distribute with fixed item size ** @param axisType Layout direction * @param fixedItemLength Length of each item layout direction * @param The first item to the parent view margin * @param tailSpacing the last item to the parent view margin */ - (void)mas_distributeViewsAlongAxis:(MASAxisType)axisType withFixedItemLength:(CGFloat)fixedItemLength leadSpacing:(CGFloat)leadSpacing tailSpacing:(CGFloat)tailSpacing;Copy the code

Let’s look at the last one

[arr mas_distributeViewsAlongAxis:MASAxisTypeVertical withFixedItemLength:30 leadSpacing:30 tailSpacing:200];
            [arr makeConstraints:^(MASConstraintMaker *make) {
                make.left.equalTo(@0);
                make.width.equalTo(@60);
            }];
Copy the code

We can see the difference between the two: the first fixedItemLength represents width, the second fixedItemLength represents height, the first leadSpacing\tailSpacing represents x, and the second spacing represents y. We just have to pay a little bit of attention here to see that with this API, it’s really easy to write isometric code in the future. Without the need to pull constraints every day, pull equal ratio, pull length…… The other thing we can see is that there are two ways. What’s the difference? By contrast, we can see that the first method is equal distance, and do not care about the width and height of the control itself, only equal distance, the second method is on the contrary, attention is equal size, only control itself size, as for the spacing is equal points… This way, we are really convenient, whether it is a fixed size or a fixed spacing we can do very well.

Finally it care MASExampleLayoutGuideViewController

[topView makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.mas_topLayoutGuide);  make.left.equalTo(self.view); make.right.equalTo(self.view); make.height.equalTo(@40); }]; [topSubview makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.mas_topLayoutGuide);  make.centerX.equalTo(@0); make.width.equalTo(@20); make.height.equalTo(@20); }]; [bottomView makeConstraints:^(MASConstraintMaker *make) { make.bottom.equalTo(self.mas_bottomLayoutGuide);  make.left.equalTo(self.view); make.right.equalTo(self.view); make.height.equalTo(@40); }];Copy the code

We can see the effect

If you make. Top. EqualTo (self. Mas_topLayoutGuide); Make. Top. EqualTo (self.view); What’s going to happen?

We’ll learn how to use the automatic layout framework for navigation. You’ll find that you’ll no longer have to decide whether to use xiB, storyboard, or handwritten layout, and your code efficiency will be greatly improved. Because it’s really worth recommending…

Welcome to my series of blogs

Series: iOS development – preface + outline blog.csdn.net/spicyShrimp…