background

The Autolayout layout on iOS is a great fit for the screen, and xib or Storyboard layout is a great fit for everyone. But if you’re looking at pure code layout, anyone who’s written using apple’s native API knows that it’s pretty complicated. Therefore, I would like to announce a new framework for navigation, which is aimed at the encapsulation of apple’s Autolayout native code and simplifies the use of navigation by means of chain call. Now let’s look at how it’s encapsulated. Okay?

Architecture diagram

Masonry

  • View+MASAdditionsThis layer is spiced with three main methods for creating constraints in the outermost layer; It also provides methods to obtain constraints.
  • MASConstraintMakerThe second floor. The role of providing a bridge is also central. All the constraints we produce are in an array of this class. When callinginstallMethod, loops through the array
  • MASConstraint: The third floor. This is the parent of the two subclassesMasonryConstraint vector, and has oneMASViewAttributeClass to store applesAutolayoutA view of the carrier and the real role.

The MASCompositeConstraint subclass stores multiple constraints as an array for chained calls like make.width. Height. That is, multiple objects like MASViewConstraint. So the operation of the MASCompositeConstraint class ends up calling the methods in the MASViewConstraint class.

The source code parsing

[redView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(superview.mas_top).with.offset(padding);
}];
Copy the code

Note: The above code is not a complete constraint on a view. We’re just going to go through a constraint resolution, the logic is the same and this code is going to call the outermost API that creates the constraint, so let’s go inside and look at the implementation of that.

- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block { self.translatesAutoresizingMaskIntoConstraints =  NO; MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self]; block(constraintMaker); return [constraintMaker install]; }Copy the code

Here we instantiate a constraintMaker for our upper-layer API to use, execute the code that created the constraint by calling a block, and then call the Install method, finally completing the creation of the view constraint.

Let me analyze the core implementation of this edge in two steps.

  • make.top.equalTo(superview.mas_top).with.offset(padding); The implementation of this code completes the creation of the constraint object

  • [constraintMaker Install] completes the constraints that we finally create that apply to the view

make.top.equalTo(superview.mas_top).with.offset(padding);parsing

This point syntax, each call either creates a constraint or changes the value of the constraint

Let’s start with make. Top

- (MASConstraint *)top {
    return [self addConstraintWithLayoutAttribute:NSLayoutAttributeTop];
}

- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute {
    return [self constraint:nil addConstraintWithLayoutAttribute:layoutAttribute];
}
Copy the code

This method is a simple call to the API and passes in the name of the NSLayoutAttributeTop Apple-level constraint

Next, let’s look at the core method for adding constraints. All methods that add constraints call this method

- (MASConstraint *)constraint:(MASConstraint *)constraint addConstraintWithLayoutAttribute:(NSLayoutAttribute)layoutAttribute { MASViewAttribute *viewAttribute = [[MASViewAttribute alloc] initWithView:self.view layoutAttribute:layoutAttribute]; MASViewConstraint *newConstraint = [[MASViewConstraint alloc] initWithFirstViewAttribute:viewAttribute]; if ([constraint isKindOfClass:MASViewConstraint.class]) { //replace with composite constraint NSArray *children = @[constraint, newConstraint]; MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children]; compositeConstraint.delegate = self; [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint]; return compositeConstraint; } if (! constraint) { newConstraint.delegate = self; [self.constraints addObject:newConstraint]; } return newConstraint; }Copy the code

This method has two branches, one is passing in nil, which is the case we’re looking at so far, and the other case we’ll look at later, which is the continuous case of make.width.height, which doesn’t get in or out of nil. Anyway, when coming in and out of nil, the viewAttribute and newConstraint variables are first instantiated. The viewAttribute variable holds the name of the view and constraint that you’ll implement. NewConstraint is a constraint at the navigation level that implements the concrete logic of the constraint. Next, you set the delegate and add it to the Constraints array. This array stores all the constraint objects. Finally, the newConstraint object is returned

EqualTo (superview.mas_top) This method we should learn the most, this method is also more clever design. You may not know what this syntax is at first glance. Let’s look at the implementation of the equalTo method in the MASConstraint class.

- (MASConstraint * (^)(id))equalTo {
    return ^id(id attribute) {
        return self.equalToWithRelation(attribute, NSLayoutRelationEqual);
    };
}
Copy the code

This method returns a block, so the.equalto () bracket calls the block execution. Block returns MASConstraint * an object.

Next, let’s look at the.offset() method in MASViewConstraint

- (void)setOffset:(CGFloat)offset {
    self.layoutConstant = offset;
}
Copy the code

This method is relatively simple and simply sets a specific value to the MASViewConstraint object.

At this point the method has been analyzed.

Let’s look at another call to make.width.height mentioned above

We’re not going to go through all of these calls, Height :(MASConstraint *)constraint:(MASConstraint *)constraint AddConstraintWithLayoutAttribute: NSLayoutAttribute layoutAttribute in the following code

if ([constraint isKindOfClass:MASViewConstraint.class]) {
    //replace with composite constraint
    NSArray *children = @[constraint, newConstraint];
    MASCompositeConstraint *compositeConstraint = [[MASCompositeConstraint alloc] initWithChildren:children];
    compositeConstraint.delegate = self;
    [self constraint:constraint shouldBeReplacedWithConstraint:compositeConstraint];
    return compositeConstraint;
}
Copy the code

Here we instantiate another MASCompositeConstraint subclass of MASCompositeConstraint, which is a very simple class that stores multiple MASViewConstraint objects and iterates over each object

[constraintMaker install]parsing

- (NSArray *)install {
    if (self.removeExisting) {
        NSArray *installedConstraints = [MASViewConstraint installedConstraintsForView:self.view];
        for (MASConstraint *constraint in installedConstraints) {
            [constraint uninstall];
        }
    }
    NSArray *constraints = self.constraints.copy;
    for (MASConstraint *constraint in constraints) {
        constraint.updateExisting = self.updateExisting;
        [constraint install];
    }
    [self.constraints removeAllObjects];
    return constraints;
}
Copy the code

This method is just a looping operation. But [the constraint install]; This method calls install of MASViewConstraint and install of MASCompositeConstraint.

MASViewConstraintThe install

- (void)install { if (self.hasBeenInstalled) { return; } if ([self supportsActiveProperty] && self.layoutConstraint) { self.layoutConstraint.active = YES; [self.firstViewAttribute.view.mas_installedConstraints addObject:self]; return; } MAS_VIEW *firstLayoutItem = self.firstViewAttribute.item; NSLayoutAttribute firstLayoutAttribute = self.firstViewAttribute.layoutAttribute; MAS_VIEW *secondLayoutItem = self.secondViewAttribute.item; NSLayoutAttribute secondLayoutAttribute = self.secondViewAttribute.layoutAttribute; // alignment attributes must have a secondViewAttribute // therefore we assume that is refering to superview // eg make.left.equalTo(@10) if (! self.firstViewAttribute.isSizeAttribute && ! self.secondViewAttribute) { secondLayoutItem = self.firstViewAttribute.view.superview; secondLayoutAttribute = firstLayoutAttribute; } MASLayoutConstraint *layoutConstraint = [MASLayoutConstraint constraintWithItem:firstLayoutItem attribute:firstLayoutAttribute relatedBy:self.layoutRelation toItem:secondLayoutItem attribute:secondLayoutAttribute multiplier:self.layoutMultiplier constant:self.layoutConstant]; layoutConstraint.priority = self.layoutPriority; layoutConstraint.mas_key = self.mas_key; if (self.secondViewAttribute.view) { MAS_VIEW *closestCommonSuperview = [self.firstViewAttribute.view mas_closestCommonSuperview:self.secondViewAttribute.view]; NSAssert(closestCommonSuperview, @"couldn't find a common superview for %@ and %@", self.firstViewAttribute.view, self.secondViewAttribute.view); self.installedView = closestCommonSuperview; } else if (self.firstViewAttribute.isSizeAttribute) { self.installedView = self.firstViewAttribute.view; } else { self.installedView = self.firstViewAttribute.view.superview; } MASLayoutConstraint *existingConstraint = nil; if (self.updateExisting) { existingConstraint = [self layoutConstraintSimilarTo:layoutConstraint]; } if (existingConstraint) { // just update the constant existingConstraint.constant = layoutConstraint.constant; self.layoutConstraint = existingConstraint; } else { [self.installedView addConstraint:layoutConstraint]; self.layoutConstraint = layoutConstraint; [firstLayoutItem.mas_installedConstraints addObject:self]; }}Copy the code

This method calls apple’s underlying API for constraint creation in both cases of Maker. Update and recreate constraints, respectively.

MASCompositeConstraintThe install

- (void)install { for (MASConstraint *constraint in self.childConstraints) { constraint.updateExisting = self.updateExisting; [constraint install]; }}Copy the code

This is done by looping over the stored MASViewConstraint or MASCompositeConstraint object and calling the above Intall method. If it is a MASCompositeConstraint object object, it is called recursively.

conclusion

The whole structure of navigation will be relatively simple. One of the things we will learn is the architecture design, and the other thing we will learn is how to implement chained call in OC

My blog

FlyOceanFish