Introduction to grid layout

MyLayout is the eighth layout in MyLayout. This is a layout that separates layout constraint Settings from the view, just as tag elements and CSS styles in HTML can be represented and stored separately. Raster layouts are therefore ideal for scenarios where the data content is the same but the presentation styles are different, and the presentation styles can be configured and changed dynamically, or even delivered dynamically from the server. Raster layout also provides a mechanism for interface layout by describing layout formats based on JSON syntax.

Raster layout requirement scenarios

  • In the home pages of many e-commerce applications, such as Taobao, Tmall, jingdong and so on, it can be seen that commodities are mostly arranged and displayed in the form of classification. The display styles of commodities in different categories are different, and the display styles of commodities in the same category may also be different. Each item occupies a rectangular area block, and these rectangular areas are always arranged in a compact arrangement of some kind, such as horizontal or vertical arrangement, or a mixture of horizontal and vertical arrangement. The goods in each rectangular block are basically composed of the main title, payment title, picture, and some small ICONS of activity, and clicking on the goods in the rectangular block will enter the detailed page of the goods. The arrangement and layout of goods in these e-commerce applications are often not fixed in a certain style, and often change with the time or holidays. One of the things that you can see between them is that they tend to have very fixed data models but very different presentation styles, so we want these presentation and layout styles not to be written in code but to be designed in a way that can be configured dynamically to solve this problem.
  • Such as early Zarker or in some news headlines and netease news today are based on the version of the application of the card in the form of a display, and these CARDS combination likely is each page of the style is different, every page in the card is made up of several different compact news in a certain order of permutation and combination together, every news basic by: Main title, subtitle, introduction, thumbnail, etc. When implementing this card style layout for news applications, we often design different presentation style templates first. Since the content of the news is the same, we only need to apply different card style templates on different pages.
  • Sometimes it is desirable that the presentation style of our application be dynamically changed from the server to achieve flexible and varied effects.

In the above examples, you can see that requirements have the following characteristics:

  1. The interface is always arranged regularly in the form of rectangular blocks.
  2. Each rectangular block corresponds to a data model, and the content and structure of the data model are relatively stable.
  3. The layout of the interface is not fixed but can be flexible.
  4. Rectangles in the interface are always separated by boundaries.
  5. When the user clicks on these rectangular blocks, the logic tends to be fairly uniform.

Most of these requirements can be met through grid layout, and grid layout is a layout system designed to solve these problems!

Principle of grid layout

The idea of raster layout is partly derived from the functionality in Bootstrap, as well as the idea of separating CSS and tag elements from HTML. In raster layout, all views do not need to set any constraints related to layout arrangement. Views are only responsible for setting content, color, font and other related properties, while grids are responsible for setting location and size alignment and boundary related properties. This creates a complete separation of content and layout, and it is this separation mechanism that allows dynamic placement and sizing. So what is a grid?

A grid is simply a grid, a rectangular area

So let’s look at a UIWindow and a UIView. It turns out they’re all just abstract rectangles. Any rectangular area has the concepts of position and size, which are described and implemented through the provided frame property. Our interface is composed of many views. From a layout point of view, our interface is actually composed of several rectangular areas, and the so-called layout is actually set the position and size of each rectangular area. Once the position and size are set, all we need to do is fill in the corresponding rectangle area, and then the system renders the content of each rectangle area separately, thus rendering our interface.

To achieve our goal of separating content and layout, we need to abstract and process the rectangular area, because we define a rectangular area as a grid. Does that mean that a grid will satisfy the condition? The answer is no, since our interface is made up of rectangular areas, a grid layout should also be made up of multiple grids. How to split the grid, and what is the relationship between grids? And how do grids describe an interface? Here we can refer to the following interface:

In this interface, then we can first think of the entire rectangular area of the interface as a grid G. This interface can be seen as composed of three rectangular areas: upper, middle and lower. Therefore, we can divide grid G vertically from top to bottom into three sub-grids: A,B and C. A,B,C three grids can also continue to be divided, which A grid area can be divided horizontally from left to right for A1,A2,A3 three sub-grids, so divided after A1,A2,A3 three sub-grids can be corresponding to fill the specific content, we will not need to continue to divide A1,A2,A3. The same B grid can be divided horizontally into B1 and B2 grids from left to right, B1 grids can be filled with specific content, so there is no need to further division, and B2 grids we also need to continue to be vertical from top to bottom divided into B21,B22 two grids, this division will not need to be divided again; Similarly, the C grid can be divided into three sub-grids, C1,C2 and C2, from left to right as the A grid is divided horizontally. Here is the final result of a grid partition:

As you can see, by dividing the grid, when we finally display, we just need to put the contents of the view into the corresponding non-dividing grid, and we will not continue to divide the grid into the leaf grid. We can summarize some characteristics of this grid division method:

  1. Grids are always divided into 0 to more smaller grids on a horizontal or vertical basis. Because of this partition method, we do not need to record the position and width and height of each grid, but only a size value can describe a grid.
  2. A grid can be divided into many sub-grids and can be recursively divided indefinitely, thus forming a grid tree. The final grid that is no longer divided is called a leaf grid. The criterion for defining a leaf grid is whether it can meet the requirements for storing the displayed content. If a certain grid cannot display a certain independent content, it needs to continue to divide.
  3. This defining nature of the grid makes it unsuitable for scenarios with overlapping display effects.

So let’s define a grid again: a grid is a rectangular area with a tree hierarchy and a specific size that is arranged according to specific rules and can be divided infinitely. This rule of grid definition hides the concept of position, as well as the concept of width and height, and instead uses a single value to describe the position and size of a rectangular area. And we specify that only the area of the leaf grid is used to hold the view’s content.

Properties of the grid

To characterize the above definition and description of grids, we need to actually define grids. So we define a grid interface :MyGrid, the definition of which can be seen in myGrid.h. Let’s take a look at this interface in detail.

Action and event handling mechanisms for grids

In addition to displaying content, we use grids to provide logic for responding to events, such as when a user touches a grid, and to use additional data stored in the grid for event processing. We also want grids to have properties that uniquely identify themselves or that some grids have the same properties. So we abstract the grid’s ability to respond to events and build a grid base interface, MyGridAction.

/** Grid action interface, you can touch the grid to perform specific actions and events. */ @protocol MyGridAction<NSObject> /** The label identifier of the grid, used to distinguish grids in events. */ @property(nonatomic) NSInteger tag; /** Raster action data, this data is raster extension data, you can use this additional data in the action to perform a series of operations. It can be a number, a string, or even a JS script. */ @property(nonatomic, strong) id actionData; /** Set the raster event to nil if the raster event is cancelled @param Target action @param Action action event -(void)handle:(id<MyGrid>)sender */ -(void)setTarget:(id)target action:(SEL)action;

@end

Copy the code

In the grid action definition above, we can see that the tag attribute is used to identify and classify grids; The setTarget: Action: method sets the logic for a grid when the user touches it. ActionData can set any data attached to the grid. The meaning of the specific data is defined by the user, so it can be a URL, a string, or even a JS script. Because our grid layout positioning can be based on server delivery of dynamic layout solutions. So we wanted our interface layout to be dynamic, and we wanted our business logic to be dynamic as well (it’s not that easy to make business logic dynamic in practice, and Apple doesn’t allow business logic to be updated without approval). Therefore, we can leverage the data in actionData to support the dynamic capability of part of the business logic of the raster layout. For example:

Id <MyGrid> gird = // Here we assume that a grid is fetched somewhere and that the grid definition data (including actionData) is delivered dynamically from the server. [gridsetTarget:self action:@selector(handleAction:)]; . -(void)handleAction:(id<MyGrid>)grid {if(grid.tag == XXX) {construct a UIWebView and pass the actionData value to UIWebView assuming the tag is the URL}else if(grid.tag == yyy) {// Assume that the actionData value for tag is yyy is a JS script to build a JSContext object and execute the script described by actionData. }else{/ /.. Other types of data processing. }}Copy the code
The basic properties of the grid

As mentioned above, a grid is actually a rectangular area of a specific size, and a grid is a tree data structure with parent-child relationships. Here is the definition of the grid interface, which is derived from the MyGridAction interface

/** Grid protocol. Used to describe a raster rectangular area, so a raster is a rectangular area. This interface is used to describe the properties of grids and to add and remove grids. Grids can be split into many sub-grids in one direction or another, and the process can be done recursively. All of the subviews in the grid layout are placed in turn into the area of the leaf grid. */ @protocol MyGrid <MyGridAction> // Set and get the grid size @property(nonatomic, assign) CGFloat measure; // Get the parent grid. The parent of the root grid is nil @property(nonatomic, weak,readonly) id<MyGrid> superGrid; @property(nonatomic, strong,readonly) NSArray<id<MyGrid>> *subGrids; /** Clone a new grid and all its subgrids. */ -(id<MyGrid>)cloneGrid; /** The spacing between sub-grids within a grid. */ @property(nonatomic, assign) CGFloat subviewSpace; /** The inner margin of the inner view of a subgrid or leaf grid. */ @property(nonatomic, assign) UIEdgeInsets padding; /** How to align the views inside a sub-grid or a leaf grid. For non-leaf grids, only one direction of docking can be set. 2. For a leaf grid, if gravity is set, the subview must have a definite size. */ @property(nonatomic, assign) MyGravity gravity; /** placeholder flag, leaf grid only. When set to YES, this grid is only used for placeholders and subviews cannot be filled into this grid. */ @property(nonatomic, assign) BOOL placeholder; /** Anchor flag, indicating that this grid, while non-leaf grids, can also be used to fill views. If the anchor flag of a non-leaf grid is set to YES, this grid can also be used to populate subviews, which are typically used as background views. */ @property(nonatomic, assign) BOOL anchor; For leaf raster, if gravity is set, the subview must be filled with a clear size */ @property(nonatomic, assign) MyGravity overlap; /** topBorderline */ @property(nonatomic, strong) MyBorderline *topBorderline; /** Header borderline */ @property(nonatomic, strong) MyBorderline *leadingBorderline; /** bottomBorderline */ @property(nonatomic, strong) MyBorderline *bottomBorderline; / / @property(nonatomic, strong) MyBorderline *trailingBorderline; /** set the leftBorderline with this property if you don't need to worry about internationalization, otherwise use leadingBorderline*/ @property(nonatomic, strong) MyBorderline *leftBorderline; /** Set the right border with this property if you don't need to worry about internationalization, otherwise use trailingBorderline*/ @property(nonatomic, strong) MyBorderline *rightBorderline; /** Add a row grid to return a new grid. The value of measure can be set as follows: 1. A constant greater than or equal to 1 indicates a fixed size. 2. Constants greater than 0 and less than 1 represent the proportion of the overall size. 3. MyLayoutSize. Fill */ -(id<MyGrid>)addRow:(CGFloat)measure; /** Add a column grid to return a new grid. The value of measure can be set as follows: 1. A constant greater than or equal to 1 indicates a fixed size. 2. Constants greater than 0 and less than 1 represent the proportion of the overall size. 3. MyLayoutSize. Fill: use the remaining size */ -(id<MyGrid>)addCol:(CGFloat)measure; // Add a grid to return the added grid. This method and the following methodcloneThe Grid can be used in combination to build scenarios that require repeated addition of grids. -(id<MyGrid>)addRowGrid:(id<MyGrid>)grid; -(id<MyGrid>)addColGrid:(id<MyGrid>)grid; -(id<MyGrid>)addRowGrid:(id<MyGrid>)grid measure:(CGFloat)measure; -(id<MyGrid>)addColGrid:(id<MyGrid>)grid measure:(CGFloat)measure; // Delete from the parent grid. -(void)removeFromSuperGrid; // Construct grids in dictionary style. @property(nonatomic, strong) NSDictionary *gridDictionary; @endCopy the code

To be continued…


Welcome to visit myMaking the addressandJane’s address book