Masonry is a lightweight framework that iOS often uses in control layouts, and masonry makes nslayoutconstraint easier to use. Masonry simplifies the way nslayoutconstraint is used, so that we can assign constraints to our controls in a chained fashion. The topic of this blog is not to teach you how to use the masonry framework, but the masonry framework to parse the source code, let you understand how masonry nslayoutconstraint encapsulation, And the role of the various parts of the masonry framework. In the masonry framework, the careful taste of dry goods is still a lot. The masonry framework is version objective-c, and if your project is in swift language, you have to use the snapkit layout Framework. Snapkit is actually the swift version of Masonry, although the implementation of the language is different, but the implementation of the idea is generally consistent.
Today's blog on the masonry framework of the analysis of the source code is the first comparison to a view to add the same constraints, the use of masonry and system native difference. Then we give the masonry of the main parts of the framework, and from the class diagram we analyze the structure of the masonry framework. Then from the whole to the part of the gradual refinement, prying into its internal implementation details. With the above steps, we will take a detailed look at the internal implementation of the masonry framework. In fact, the masonry framework is lightweight, the total source code is not much upstream, but carefully read its implementation details, or can absorb a lot of practical things.
First masonry the address on GitHub is https://github.com/SnapKit/Masonry, you can clone to masonry frame via the link above, There are masonry frameworks and examples of how some masonry are used. About the use of masonry in today's blog does not do too much to repeat, the specific use of the way please refer to the above GitHub link. Today we will analyze the masonry framework of the source code.
the comparison of masonry frame and Nslayoutconstraint call mode
First, we nslayoutconstraint add a constraint to our view and then give the masonry code. Of course, we do not say that masonry add a simple line of constraints, of course, good things do not need publicity. Entering this section of the topic, we want to add a top constraint to a view, which we use an expression to represent is "subview.top = Superview.top +". That is, the top of the child view is separated from the top of the parent view by 10 pt.
1. Adding a constraint using Nslayoutconstraint
The code below adds a top constraint relative to the Superview to Subview. A view to determine the location of a constraint is not enough, so it is conceivable that we have to write a number of such constraints below to determine the relative position of a view. In fact, below is an expression that nslayoutconstraint each parameter in the constructor to form an integral part of the expression. From top to bottom our team parameters are parsed, parameter constraintwithitem is used to specify the object being constrained, here is subview. The first attribute parameter specifies the attribute that constrains the object, which is the top property of the Subview. The parameter relatedby is used to specify a constraint relationship, such as greater than or equal to, less than or equal to a constraint value. The parameter Toitem specifies the object that is relative to the constraint, which is relative superview, so the parameter here is Superview. The second attribute parameter is the top property of the specified Superview. multiplier Specifies a multiple relationship for a relative constraint, and constant is the offset of the constraint.
From top to bottom, the arguments in thenslayoutconstraint constructor form a mathematical expression, which is subview.top = superview.top * 1 +, The expression gives an intuitive relationship between subview.top and superview.top . With the code below we have added a top constraint relative to the Superview for Subview, with a constraint offset of 10.
2. Add the above constraint using masonry
The next step is masonry, and we'll use masonry to add the above constraint, with the code below. Below is given three kinds of settings, the bottom three ways are equivalent, of course, in the masonry in the following three ways of implementation. Each sentence in the block below represents the meaning of subview.top = superview.top * 1 + , which means that we only need to write one of these three lines of code. The benefits of using masonry at a glance, make your code more concise.
The masonry framework supports the addition of constraints, the updating of constraints, the reconstruction of constraints, the implementation of basic animations, and so on. The features are pretty powerful. Chained calls and anonymous closures are used in the masonry framework to simplify the addition of constraints. For more detailed usage of masonry, please refer to the GitHub link of the masonry framework above, and use it in this way.
second, the class structure of masonry framework
By using the above masonry, we can see that the UIView object can call the mas_makeconstraints method directly to add a constraint to the corresponding View object. Because the Mas_makeconstraints method is in the view+masadditions class of UIView, the UIView object can be called directly. Also in the view+masadditions category There are other methods for the use of UIView objects, will be described in detail later.
Below is the masonry framework core class and the relationship between the class, the following class diagram is in the reading masonry source code when the painting, only this one, if the similarity is purely coincidental. If the text in the smaller, you can save the picture to the local, and then zoom in to see, nonsense less, into the theme of our class diagram. All classes in the masonry framework are not included in the class diagram below, but all the core classes are below. We explain the class diagram below in turn from left to right.
1.view+masadditions Introduction (Part of the red box on the left)
The leftmost large class, that is, the green box part, is the masonry framework of the UIView public category, that is, the source of the view+masadditions part of the file, In this category, a member property of type Masviewattribute is added (it will be introduced later Masviewattribute is a god horse thing). In addition to adding a series of member properties, four public methods have been added: theMas_closestcommonsuperview method is responsible for finding the nearest common parent view of two views (analogy two numbers of least common multiple),Mas The _makeconstraints method is responsible for creating the installation constraints,mas_updateconstraints is responsible for updating the existing constraints (if the constraint does not exist on the install),Mas_ The Remakeconstraints method is responsible for removing the previously created constraint and adding the new constraint. The above approach is the method that the UIView object sets the constraint primarily to invoke, and later details how it is implemented.
Introduction to the 2.MASViewAttribute Class (part of the yellow box on the right)
After introducing the public category of the UIView used directly by the user, let's take a look at the part that the user cannot see, which is the group on the right of the class diagram below. The coupling of the four small classes on the right is relatively high, so let's look at the Masviewattribute class first. The structure of the Masviewattribute class is relatively simple, including three properties and three methods. From the Masviewattribute class name we can see that this class is the encapsulation of UIView and Nslayoutattribute. Using an equation to represent is Masviewattribute = UIView + Nslayoutattribute + item. The View property in the Masviewattribute class represents the object being constrained, and item is the part of the object that can be constrained.
The item member property here will be used later as the parameter of Constraintwithitem and Toitem in the nslayoutconstriant constructor. Of course, for UIView, the item is UIView itself. And for Uiviewcontroller, the item is toplayoutguide,Bottomlayoutguide will give a detailed introduction later. In addition to the two constructors in this class, there is a Issizeattribute method, which is used to determine whether the Layoutattribute property in the Masviewattribute class is Nslayoutattributewidth or nslayoutattributeheight, if it is width or height, then the constraint is added to the current view, Instead of being added on the parent view.
Introduction to 3.MASViewConstraint (part of the yellow box on the right)
We then look at the Masviewconstraint class, which is a further encapsulation of the Nslayoutconstriant class. One of the core things that Masviewconstraint does is initialize the nslayoutconstriant object and add the object to the corresponding view. Because nslayoutconstriant requires nslayoutattribute and constrained view during initialization, Masviewattribute is the encapsulation of view and Nslayoutattribute , so the Masviewconstraint class depends on the Masviewattribute class, and the relationship between the two is as follows.
From the class diagram below we can see that Masconstraint is the parent class of Masviewconstraint, Masconstraint is an abstract class and cannot be instantiated. We can consider masconstraint as an interface or protocol. Masconstraint Abstract class also has a subclass, that is, Masviewconstraint Brothers class Mascompositeconstraint, from the mascompositeconstraint of the name we can see Mascompositeconstraint is a combination of constraints, in which a series of constraints are stored. The structure of the Mascompositeconstraint class is relatively simple, the core of which is an array of Masviewconstraint objects, and Mascompositeconstraint is an encapsulation of the array.
4. Factory class Masconstraintmaker (part of the Middle green box)
On both sides of the watch, next we look at the middle part, that is, the Masconstraintmaker class. This class is a factory class that is responsible for creating objects of type Masconstraint (dependent on the Masconstraint interface and not on the implementation). In the view+masadditions class of UIView, some methods in the Masconstraintmaker class are called. When we use masonry to add constraints to SubView , the parameters of the block in the Mas_makeconstraints method are the objects of Masconstraintmaker. The user can specify the constraint to be added and the value of the constraint through the block callback Masconstraintmaker object. An array of constraints attributes in the Factory records all Masconstraint objects created by the factory.
The core classes in the masonry framework and the relationship between the classes are covered, and below are the core class and class diagram. The code implementation will be gradually explored below.
Third, view+masadditions source code Analysis
We first UIView public class view+masadditions in the source code, that is, corresponding to the upper part of the red box. The user is to add the constraint to the View through View+masadditions, View+masadditions is the channel that masonry frame interacts with the outside world. This part mainly analyzes the view+masadditions source code, first introduces its member properties, and then introduces the main methods. Enter the topic for this section.
1.view+masadditions principal member properties and Getter methods
Below are some of the member properties in the View+masadditions class, and the others are similar to the below, which are all masviewattribute types. Take the Mas_left member property below as an example, because Masviewattribute is a combination of view and Nslayoutattribute , so Mas_ Left represents the Nslayoutattributeleft property of the current view, that is, Mas_left stores the nslayoutattributeleft property of the current view. Similarly, Mas_top represents the Nslayoutattributetop property of the current view, and the other member properties are the same.
Using the Getter method corresponding to the member properties above, we can get a clear glance of what is stored in it. Below are the getter methods that correspond to the Mas_left, Mas_top, and Mas_right member properties, and what you do is instantiate the Masviewattibute and specify the Layoutattribute for the current view when you instantiate it. That is mas_left = self + nslayoutattributeleft, mas_top = self +nslayoutattributetop, of course, the self here represents the current view.
2.Mas_makeconstraints Method Analysis
As mentioned above in the introduction of the class diagram, the user is to add constraints to the current view by calling the Mas_makeconstraints method. The code below is the code implementation of the mas_makeconstraints function, according to the individual understanding of each line of code in Chinese comments, then we will take a good look at the structure of the function. The return value of the mas_makeconstraints method is an array (Nsarray), where all the constraints added in the current view are stored in the array. Because the masonry framework encapsulates nslayoutconstraint into Masviewconstraint, all of the objects stored in this array are masviewconstraint.
next look at mas_makeconstraints parameters, Mas_ Makeconstraints parameter is a type of void (^) ( masconstraintmaker *) anonymous block ( that is, anonymous closure), the return value of the closure is void and requires an object from the Masconstraintmaker factory class. The purpose of this closure is to allow the Mas_makeconstraints method to initialize the Maconstraint attribute in the Masconstraintmaker factory class object through the block. Please take part in the use of block below.
In the mas_makeconstraints method Body, first set the Translatesautoresizingmaskintoconstraints property of the current view to No, It then creates a Masconstraintmaker factory class object Constraintmaker, The Constraintmaker object is then called back through the block to allow the user to initialize the properties of the Maconstraint type in the Constraintmaker. In other words, the thing that is done in block is that the user set constraint is the added code, such as make.top (@10) = = (Constraintmaker.top = ten). Finally, the user-specified constraint is installed by calling the install method of Constraintmaker.
3. Analysis of mas_updateconstraints and mas_remakeconstraints functions
The implementation of these two functions is similar to mas_makeconstraints, which is the setting of one more property. Mas_updateconstraints the updateexisting in Constraintmaker is set to Yes, that is, when the constraint is added, check that the constraint has already been installed, and if it is added, the update will be added if it is not added. The thing to do in Mas_remakeconstraints is to set the Removeexisting property to Yes, to remove the old constraint on the current view, and then add the new constraint.
4,Mas_closestcommonsuperview method Analysis
The Mas_closestcommonsuperview method is responsible for calculating the common parent view of two views, which is similar to the two-digit least common multiple. The code below is looking for the public parent view of two views, which is, of course, the closest public parent view. If found, return nil if not found. Finding the public parent view of two views is important for the addition of constraints, because the relative constraints are added to their public parent views. For example viewa.left = Viewb.right +, because it is the relative constraint of Viewa and VIEWB, then the constraint is added on the public parent view of Viewa and VIEWB, if VIEWB is the parent view of Viewa, The constraint is then added to the VIEWB to constrain the Viewa.
Four, the clues, the analytic restriction factory class Masconstraintmaker
In the previous section we analyzed the view+masadditions category, in which the main use of the constraints of the factory class Masconstraintmaker, then we will spy on Masconstraintmaker content. Masconstraintmaker becomes a constrained factory class because Masconstraintmaker assigns a Nslayoutconstraint object, Because masonry further encapsulates the Nslayoutconstraint class into Masviewconstraint, Masconstraintmaker is the object responsible for creating Masviewconstraint, and call the install method of the Masviewconstraint object to add the constraint to the corresponding view.
The core public attribute in 1.MASConstraintMaker.
Below are some of the attributes in Masconstraintmaker, and you can see that the properties below are msaconstriant types, msaconstriant are abstract classes, So the underlying member variable is stored essentially as an object of the Msaconstriant subclass Masviewconstraint. Masconstraintmaker is responsible for the instantiation of the Masviewconstraint. A word explaining masviewconstraint,masviewconstraint = View + nslayoutconstraint + Install. The implementation of Masviewconstraint specific technical details will be given later. In Masconstraintmaker there is also a private array constraints, which is used to record and create constraint objects.
Analysis of factory methods in 2.MASConstraintMake
Factory class must have a factory method, and then we introduce the factory method method in Masconstraintmaker , above each masconstraint type of property corresponds to a getter method, in the getter method will call Addconstraintwithlayoutattribute method, and Addconstraintwithlayoutattribute invokes the method in the second truncated figure, And this method is the factory method of Masconstraintmaker factory class, create Msaviewconstraint object according to the parameters provided, If the first argument of the function is not empty, the newly created Msaviewconstraint object is combined with the parameter to An object of the Mascompositeconstraint class (Mascompositeconstraint is essentially an array of Msaviewconstraint objects).
masconstraintmaker the factory method for the factory class, which is responsible for creating objects of the Masconstraint class. The methods below can be created mascompositeconstraint and The Masviewconstraint object, which also says that the Mascompositeconstraint object is an array of Masviewconstraint objects. After creating the appropriate object for the Masconstraint class below, the created object is added to the private constraints array of the Masconstraintmaker factory class to record all constraints created by the factory object. newconstraint. delegate self ; It is very important that chained calls (for example: ).
about chained calls we take for example. The maker here is our masconstraintmaker Factory object, Maker.top returns the object of the Masviewconstraint class with the Nslayoutattributetop property, we first make a conversion: . So Maker.top.left Equivalent to Newconstraint.left, it is important to note that the left method that is called at this point is not in our factory masconstraintmaker, but instead is replaced by the getter of the left property in the Masviewconstraint class. The law. The proxy is set for Newconstraint so that masconstraintmaker Factory-class factory method to complete the creation. The code below if there is no newconstraint. delegate self ; If the proxy is set, then chained calls are not supported.
Say so much, sum up, if you call maker.top, Maker.left and so on, these methods will call the factory method below to create the corresponding Masviewconstraint object, And is recorded in the constraint array of the factory object. A chained invocation is to say that the current factory object is specified as a proxy for the Masviewconstraint object, so a Masviewconstraint object can invoke the factory method through the proxy to create another new Masviewconstraint object. , the proxy mode is used here.
3. The install method in the factory class
Although we see Masconstraintmake as a factory class, the functionality of the factory class does not just create masconstraint objects, It is also responsible for invoking the install method of the Masconstraint object to install the appropriate constraint on the desired view. The install method in the Masconstraintmake class is to traverse the factory object to create all the constraint objects and invoke the installation method of each constraint object for the constraint. Below is the install method in the factory class.
When installing a constraint, if self.removeexisting = = Yes, then the user passes the install method called by the mas_remakeconstraints method, First remove the original constraint and then add the new constraint. When a constraint is installed, Updateexisting is assigned to each constraint, and each constraint determines whether to update it when it calls its own install method. Below is the implementation and comment of the Masconstraintmake's install method.
Five, continue to follow the clues, analysis Masviewconstraint
The object created by the Masconstraintmaker factory class is essentially an object of the Masviewconstraint class. The Masviewconstraint class is essentially the encapsulation of the maslayoutconstraint, further speaking Masviewconstraint is responsible for organizing parameters for the Maslayoutconstraint constructor and creating Maslayoutconstraint objects, and adding the object to the appropriate view. Next we will parse the contents of the Masviewconstraint class.
1.MASViewConstraint Object Chaining invocation exploration
The object of Masviewconstraint is to support chained invocation, such as Constraint.top.left.equalTo (Superview). Offset (ten); the above is a chained call, and like This form of Equalto (Superview) is also not a way of function invocation in objective-c, where the function is called through [] in Objective-c, where () is used. The next step is to analyze how this chain-like invocation is implemented.
The left, top, and other bound getter methods in the Masviewconstraint class will call this method below, The thing to do in this method is to invoke the factory method in the factory by proxy to create the corresponding Masconstraint object according to Layoutattribute.
And like offset (10) How is this method of invocation implemented? We know that in OC it is not possible to invoke the method with parentheses, but the closure is okay, but offset () is not a simple closure. After the Code Analysis of offset () it is not difficult to find offset () = offset + (); The code for offset is implemented as follows. Offset is the name of a getter method, and the return value of the OFFSET function is an anonymous block, which is the () behind offset. This anonymous closure has a cgfloat parameter that returns an Masconstraint object in order to support chaining calls to that anonymous closure.
2.install Method Parsing
The install method in Masviewconstraint is responsible for creating the maslayoutconstraint object and adding the object to the appropriate view. The code below is the install to create the Nslayoutconstraint object according to the parameters collected by Masviewconstraint, the maslayoutconstraint below is actually the alias of Nslayoutconstraint. Below is the nslayoutconstraint of the calling system to create the appropriate constraint object, and the constructor below is consistent with the Nslayoutconstraint in the first section.
After creating the constraint object, we are looking for the constraint to be added to that view. The code snippet below is the one that gets the view that receives the constraint object. If you have two view-relative constraints, you get two common parent views. If width or height is added, it is added to the current view for a long time. If neither a relative view nor a size type constraint is specified, the constraint object is added to the parent view of the current view. The code is implemented as follows:
After you create the constraint object and find the view that hosts the constraint, the next step is to add the constraint to the view. Oh, son. Adding a constraint is an update to the constraint, and if it is an update to the constraint, it gets the existing constraint and updates the constraint, and adds it if the constraint being updated does not exist. Once added, we will record the constraints on this installation through the Mas_installedconstraints property. Mas_installedconstraints is a property of a nsmutable type that is associated with the UIView at run time, and is used to record all constraints that constrain the view.
3.UIView Private category uiview+masconstraints
A UIView private class uiview+masconstraints is defined in masviewconstraint , The purpose of this class is to uiview the mas_installedconstraints property of a Nsmutableset type through the runtime. All constraints that constrain the view are recorded in this property. The code is implemented as follows.
Because space is limited, today's blog is here first. The code in the masonry framework is not likely to be described in this blog post. On GitHub , however, a masonry is shared with a demo and source code parsing project. The key codes for masonry are described and commented. Below is its github sharing link.
GitHub share address:https://github.com/lizelu/MasonryDemo
iOS Development Masonry framework Source depth analysis