Why learn about FlexBox?
Recently, I have heard about FlexBox from time to time. In addition to using FlexBox in two well-known cross-platform projects, Weex and React Native , AsyncDisplayKit also introduced FlexBox.
Let's talk about the two layout methods that iOS itself provides us:
- Frame, directly set the horizontal and vertical coordinates, and specify the width and height.
- Auto Layout, which is laid out by setting constraints on relative positions.
Frame doesn't have much to say, directly specify the coordinates and size, and set the absolute value.
Auto Layout
Well-intentioned in and of itself, trying to free us from frames and stereotypes about coordinates and sizes. Instead, use the relative positional relationship between UIs to set corresponding constraints for layout.
But Auto Layout
good intentions don't do good things, and its syntax is stinky and long! I've been learning iOS for two years, and I can count on my fingers to use the native Auto Layout
syntax. It can only be used by a third-party library like Masonry .
How Auto Layout works
After talking about Auto Layout
the use of , let's take a look at how it works.
In fact, Auto Layout
the constitute a series of conditions, which become an equation. Then solve for the coordinates and size of the Frame.
For example, let's set up a UI named A:
A.center = super.center
A.width = 40
A.height = 40
则:A.frame = (super.center.x-40/2,super.center.y-40/2,40,40)
Set up another B:
B.width = A.width
B.height = A.height
B.top = A.bottom + 50
B.left = A.left
则:B.frame = ( A.x , A.y + A.height + 50 , A.width , A.height )
As shown in the figure:
Cassowary
Auto Layout
There is an internal algorithm specially used to deal with constraints. I always thought that it was developed by Apple itself. After consulting the information, I found that it came from Cassowary
an .
Cassowary is a parsing toolkit that can effectively parse linear equation systems and linear inequality systems. Inequality and equality relationships will always appear in the user interface. Cassowary has developed a rule system that can describe the relationship between views through constraints. Constraints are rules that express the position of one view relative to another.
Those who are interested can learn more about the implementation of this algorithm.
Performance comparison of Frame / Auto Layout / FlexBox
Auto Layout
After some understanding of , it is easy to conclude Auto Layout
that the performance is worse than Frame due to redundant computation.
But how much difference? What about the performance of FlexBox?
Here, according to the test code in Auto Layout's Layout Algorithm Talking about Performance, the Frame / Auto Layout / FlexBox is laid out, and the layout time of 10 to 350 UIViews is measured in sections. Take the average of 100 layout times as the result, and the time-consuming unit is seconds.
The result is as follows:
Although the test results are inevitably biased, according to the line chart, it can be clearly found that the layout performance of FlexBox is relatively close to Frame.60 FPS
As a gold standard for iOS fluency, the layout is required to be completed within 0.0166667 s, and Auto Layout
when there are more than 50 views, it may start to have problems maintaining smoothness.
The machine configuration used for this test is as follows:
Using Xcode9.2, iPad Pro (12.9-inch) (2nd generation) simulator.
The project code for testing the layout is uploaded on GitHub
What is FlexBox?
FlexBox
It is a UI layout method and is supported by all browsers. FlexBox
The first is based on the box -like model , Flexible means flexibility, making it adaptable to different screens, complementing the flexibility of the box-like model.
FlexBox
Think of each view as a rectangular box, with inner and outer margins, arranged along the main axis, and there is no dependency between views at the same level.
Auto Layout
Similar to , FlexBox
a descriptive language is used for layout, instead of using absolute coordinates for layout directly like Frame.
The main idea of flex layout is to give the Flex Container the ability to change the width and height of the Flex Item to fill the available space (mainly to accommodate all types of display devices and screen sizes).
Most importantly, FlexBox
layout is orientation-independent, and conventional layout designs lack flexibility to support large and complex applications (especially when it comes to orientation shifts, scaling, stretching, and shrinking, etc.).
FlexBox composition
Elements that take a FlexBox
layout , called Flex Container
.
Flex Container
All child elements of , called Flex Item
.
The following will talk about some concepts in FlexBox to facilitate the use of FlexBox later.
Flex Container
As mentioned earlier, FlexBox
one of the features is that there is no dependency between views.
Flex Item
The arrangement of , depends on Flex Container
the property settings of , rather than setting each other.
So let's talk about Flex Containner
the .
Flex Direction
FlexBox has 主轴(main axis)
a 侧轴(cross axis)
concept of and . The side axis is perpendicular to the main axis.
They can be horizontal or vertical.
The main axis defaults to Row
, and the side axis defaults to Column
:
Flex Direction
Determines Flex Containner
the spindle arrangement direction in .
The main axis defaults to Row (left to right):
At the same time, RowRevers can also be set (from right to left):
Column (top to bottom):
ColumnRevers (bottom to top):
Flex Wrap
Flex Wrap determines how the view wraps when it doesn't fit on the grid.
Flex Wrap is set to NoWrap by default, which will not wrap, and will always be arranged along the main axis to the outside of the screen:
Set to Wrap, then when the space is insufficient, the line will be automatically wrapped:
Set WrapReverse, the wrapping direction is opposite to Wrap:
This is a very useful property. For example, typically 九宫格布局
, if iOS is not used UICollectionView
to do it , then it needs to save 9
an instance, and then make judgments and calculate the frame, and the maintainability is really not high. Using UICollectionView
can solve the layout very well, but many scenarios cannot be reused, and it is not particularly simple to do.
For FlexBox layout Flex Wrap
, Wrap
it can be done directly with property settings.
Similar solutions on mobile platforms, such as Linear Layout for Android and UIStackView for iOS, are far less powerful than FlexBox.
Display
Display chooses whether to calculate it, the default is Flex. If set to None, the calculation of this view is automatically ignored.
Useful when displaying UI based on logic.
For example, our existing business needs to display the Tencent identity logo. According to the general practice, multiple icons are connected to each other in a row, different distances are set according to their identities, and other icons are hidden at the same time, which is troublesome for comparison. The best way for iOS is to use UIStackView, which has issues such as version compatibility. With the FlexBox layout, when it is not a certain identity, as long as the Display is set to None, it will not be included in the UI calculation.
Justify Content
Justify Content
Used to Flex Item
define the alignment on the main axis: FlexStart (spindle start point alignment), FlexEnd (spindle end point alignment), Center (center alignment).
And SpaceBetween (justified):
Set justify so that the spacing Flex Item
between is equal.
SpaceAround (aligned with equal margins):
Make the margins equal Flex Item
around
Align Items
Align Items
Flex Item
Defines the alignment on the side axis.
Align Items
You can set FlexStart, FlexEnd, Center, SpaceBetween, SpaceAround in the same way Justify Content
as .
Align Items
You can also set Baseline (baseline alignment):
As shown, it is based on the baseline alignment Flex Item
of the first line of text.
Baseline
This value is equivalent to if theFlex Item
inline and side axes of and are the same . FlexStart
Otherwise, this value will participate in baseline alignment.
Align Items
Can also be set to Stretch:
Stretch
Let the Flex Item
stretch fill the whole Flex Container
. Stretch
Will make Flex Item
the outer margin as close as possible to the size of the row or column in accordance with the corresponding property constraints.
If the value is Flex Item
not set, or is set auto
, it will occupy the entire Flex Container
height
Align Content
Align Content
It is also the alignment of the side axis Flex Item
in , but it is based on an entire row as the smallest unit.
Note that this property has no effect if there is Flex Item
only one axis (one line only ).Flex Itme
Adjust FlexWrap
to Wrap
, the effect will be displayed:
Flex Item
After I finished talking about Flex Container
the properties of , I finally talked about the properties of Flex Item
. Flex Container
The properties in it all act on what it contains, Flex Item
and Flex Item
the properties in it all act on itself.
AlignSelf
AlignSelf
You can make a single Flex Item
and other Flex Item
have a different alignment, overriding the Align Items
property .
The default is , which auto
means inherited properties. If it doesn't itself , it's equivalent to .Flex Container
Align Items
Flex Container
Stretch
FlexGrow
FlexGrow
Assigned can be 剩余空间
set 比例
. i.e. how to expand.
FlexGrow
The default value is 0
, if not defined FlexGrow
, the layout will not have the right to allocate the remaining space.
E.g:
The overall width is 100, the width of sub1 is 10, and the width of sub2 is 20, so 剩余空间
it is 70.
The setting FlexGrow
is to assign the ratio of this 70 width.
Now for the ratio question:
If all Flex Item
of the FlexGrow
properties are 1
, if there is remaining space, divide the remaining space equally.
If Flex Item
the FlexGrow
property of one is 2
, and the rest Flex Item
are 1
, then the former will occupy the remaining space Flex Item
many 1
times more than the other .
FlexShrink
Contrary to FlexGrow
dealing with space remaining, it FlexShrink
is used to deal with insufficient space. That is how to shrink.
FlexShrink
Defaults to 1, i.e. the item will shrink if there is not enough space
If all Flex Item
of the FlexShrink
properties are 1
, when the space is insufficient, it will be scaled down proportionally.
If Flex Item
the FlexShrink
attribute of one is 0
, and the rest Flex Item
are 1
, then when the space is insufficient, theFlexShrink
former of is will not shrink.0
FlexBasis
FlexBasis
Flex Item
Defines what is occupied before the extra space is allocated main size(主轴空间)
. Based on this property, the browser calculates whether there is excess space on the main axis.
FlexBasis
The default value is auto
, which is Flex Item
the original size of .
To learn more about FlexBox properties, you can refer to A Complete Guide to Flexbox
Implementation of FlexBox -- Yoga
As mentioned at the beginning, the FlexBox layout has been applied to several well-known open source projects, which use Yoga from Facebook.
Yoga is a Flexbox layout engine implemented by C. Its performance and stability have been well verified in major projects, but the disadvantage is that Yoga only implements a subset of the W3C standard.
The following will YogaKit
do .
Based on the basic understanding of the FlexBox
layout above, make some simple layouts.
YGLayout
The key to the whole YogaKit is in the YGLayout
object . Use YGLayout
to set layout properties.
In UIView+Yoga.h
the file of :
/**
The YGLayout that is attached to this view. It is lazily created.
*/
@property (nonatomic, readonly, strong) YGLayout *yoga;
/**
In ObjC land, every time you access `view.yoga.*` you are adding another `objc_msgSend`
to your code. If you plan on making multiple changes to YGLayout, it's more performant
to use this method, which uses a single objc_msgSend call.
*/
- (void)configureLayoutWithBlock:(YGLayoutConfigurationBlock)block
NS_SWIFT_NAME(configureLayout(block:));
You can see yoga
a YGLayout
read-only object named , and a configureLayoutWithBlock:(YGLayoutConfigurationBlock)block
method , and also use NS_SWIFT_NAME()
to define the method name in Swift.
In this way, we can directly use the instance object of UIView to directly set its corresponding layout.
isEnabled
YGLayout.h
is defined isEnabled
in .
/**
The property that decides during layout/sizing whether or not styling properties should be applied.
Defaults to NO.
*/
@property (nonatomic, readwrite, assign, setter=setEnabled:) BOOL isEnabled;
isEnabled
The default is NO
, we need to set it to during layout YES
to enable the Yoga style.
applyLayoutPreservingOrigin:
For this method, the header file explains it like this:
/**
Perform a layout calculation and update the frames of the views in the hierarchy with the results.
If the origin is not preserved, the root view's layout results will applied from {0,0}.
*/
- (void)applyLayoutPreservingOrigin:(BOOL)preserveOrigin
NS_SWIFT_NAME(applyLayout(preservingOrigin:));
Simply put, it is used to perform layout calculations. So, once the layout code is complete, 根视图
call this method on the properties of the yoga object to apply the layout to 根视图
and 子视图
.
Layout demo
The following example shows how to use Yoga
for FlexBox
layout.
Centered
[self configureLayoutWithBlock:^(YGLayout * layout) {
layout.isEnabled = YES;
layout.justifyContent = YGJustifyCenter;
layout.alignItems = YGAlignCenter;
}];
[self.redView configureLayoutWithBlock:^(YGLayout * layout) {
layout.isEnabled = YES;
layout.width=layout.height= 100;
}];
[self addSubview:self.redView];
[self.yoga applyLayoutPreservingOrigin:YES];
The effect is as follows:
Our real layout code, just set
Flex Container
the and
justifyContent
of .
alignItems
Nested layout
Let a be view
slightly smaller than it superView
, with a margin of 10:
[self.yellowView configureLayoutWithBlock:^(YGLayout *layout) {
layout.isEnabled = YES;
layout.margin = 10;
layout.flexGrow = 1;
}];
[self.redView addSubview:self.yellowView];
The effect is as follows:
The layout code only uses settings, View's margin
and flexGrow
.
equally spaced
Arrange a group of views vertically and equally spaced:
[self configureLayoutWithBlock:^(YGLayout *layout) {
layout.isEnabled = YES;
layout.justifyContent = YGJustifySpaceBetween;
layout.alignItems = YGAlignCenter;
}];
for ( int i = 1 ; i <= 10 ; ++i )
{
UIView *item = [UIView new];
item.backgroundColor = [UIColor colorWithHue:( arc4random() % 256 / 256.0 )
saturation:( arc4random() % 128 / 256.0 ) + 0.5
brightness:( arc4random() % 128 / 256.0 ) + 0.5
alpha:1];
[item configureLayoutWithBlock:^(YGLayout *layout) {
layout.isEnabled = YES;
layout.height = 10*i;
layout.width = 10*i;
}];
[self addSubview:item];
}
The effect is as follows:
As long as it is Flex Container
set layout.justifyContent = YGJustifySpaceBetween
, it can be done easily.
Equal spacing, automatic width setting
Make two 100
heights view
centered vertically, equally wide, and equally spaced, with an interval of 10. Calculate their widths automatically:
[self configureLayoutWithBlock:^(YGLayout *layout) {
layout.isEnabled = YES;
layout.flexDirection = YGFlexDirectionRow;
layout.alignItems = YGAlignCenter;
layout.paddingHorizontal = 5;
}];
YGLayoutConfigurationBlock layoutBlock =^(YGLayout *layout) {
layout.isEnabled = YES;
layout.height= 100;
layout.marginHorizontal = 5;
layout.flexGrow = 1;
};
[self.redView configureLayoutWithBlock:layoutBlock];
[self.yellowView configureLayoutWithBlock:layoutBlock];
[self addSubview:self.redView];
[self addSubview:self.yellowView];
The effect is as follows:
We only need to set Flex Container
the paddingHorizontal, and Flex Item
the marginHorizontal, flexGrow on it. And can reuse Flex Item
the layout layout style.
UIScrollView arrangement automatically calculates contentSize
Arrange some in UIScrollView
order view
, and automatically calculate contentSize
:
[self configureLayoutWithBlock:^(YGLayout *layout) {
layout.isEnabled = YES;
layout.justifyContent = YGJustifyCenter;
layout.alignItems = YGAlignStretch;
}];
UIScrollView *scrollView = [[UIScrollView alloc] init] ;
scrollView.backgroundColor = [UIColor grayColor];
[scrollView configureLayoutWithBlock:^(YGLayout *layout) {
layout.isEnabled = YES;
layout.flexDirection = YGFlexDirectionColumn;
layout.height =500;
}];
[self addSubview:scrollView];
UIView *contentView = [UIView new];
[contentView configureLayoutWithBlock:^(YGLayout * _Nonnull layout) {
layout.isEnabled = YES;
}];
for ( int i = 1 ; i <= 20 ; ++i )
{
UIView *item = [UIView new];
item.backgroundColor = [UIColor colorWithHue:( arc4random() % 256 / 256.0 )
saturation:( arc4random() % 128 / 256.0 ) + 0.5
brightness:( arc4random() % 128 / 256.0 ) + 0.5
alpha:1];
[item configureLayoutWithBlock:^(YGLayout *layout) {
layout.isEnabled = YES;
layout.height = 20*i;
layout.width = 100;
layout.marginLeft = 10;
}];
[contentView addSubview:item];
}
[scrollView addSubview:contentView];
[scrollView.yoga applyLayoutPreservingOrigin:YES];
scrollView.contentSize = contentView.bounds.size;
The effect is as follows:
The layout UIScrollView
mainly uses a middle contentView
, which plays scrollview
the contentSize
. It should be noted here that the setting should be made after callingscrollview
, otherwise the result will not be obtained.applyLayoutPreservingOrigin:
For the usage of UIScrollView, I haven't found a more official example on the Internet at present.
The sample code used above has been uploaded to GitHub
Summarize
FlexBox is indeed a layout method that is very suitable for mobile terminals, with clear semantics and stable performance. Now mobile UI views are becoming more and more complex, especially after all browsers have supported FlexBox, it is necessary for mobile developers to understand the new solution.
After you are proficient in using YogaKit, you can also try to encapsulate a set of layout code to speed up development efficiency.
Author: ZenonHuang
Link: https://juejin.im/post/5a33a6926fb9a045104a8d3c
Source: Nuggets The
copyright belongs to the author. For commercial reprints, please contact the author for authorization, and for non-commercial reprints, please indicate the source.