在项目中使用Yoga 布局引擎
1. 有帮助的链接
2. 开始前的叨叨
项目中最早用的是Masonry,它用起来确实十分方便,但是它是基于AutoLayout的,所以到了项目中期替换替换成了给予frame的布局方式,那么为什么要在自己的练习项目中使用Yoga呢?理由有如下几方面:
- Yoga是跨平台的在Android,Reactive Native,iOS 都有对应的版本,这样方便自己后续使用Android,RN时,能够少学点,统一下技术栈(还是懒 ^V^)。
- Yoga是基于Flex布局的,Flutter,以及Web框架,小程序也都是采用这种布局方式,还是想一劳永逸,并且是基于frame的性能上面是可以接受的,何乐而不为。
这篇主要关注的是Flex 以及 YogaKit,大家如果想了解frame布局,AutoLayout布局原理,以及目前iOS比较主流的布局框架,我后面还会另起一个博客来介绍。
3. Flex布局概念
3.1 Flex 容器布局属性
盒子模型:
* position 在当前盒子中item 的定位 |
* Flex direction
Flex 布局是有两类子对象:flex item 和 flex container。flex item 和 flex container组成一个布局层级树。还包括主轴和交叉轴的概念,要解释主轴和交叉轴的概念必须知道flex direction这个概念。
一般如果需要使用flex布局,那么需要使用display属性来开启:
display: flex |
我们先来看下 flex direction及主轴交叉轴:
flex direction 就是布局方向,一般支持row,row reverse,column,clumn reverse. 也就是横轴,横轴逆向,纵轴,纵轴逆向。如果flex direction指定的是row,row reverse 那么主轴就是横轴,如果为column,clumn reverse那么主轴就是纵轴。
flex-direction: row / column / row-reverse / column-reverse |
有了主轴交叉轴的概念后就可以进行后续的概念的介绍了:
* Justify-content
Justify-content 是flex item 沿着主轴方向上的布局方式:
flex-start:所有的flex item 沿着主轴布局的开始方向进行布局
flex-end:所有的flex item 从主轴结尾开始沿着主轴逆向布局
center: 所有flex item 居中布局
space-between:所有flex item 在容器内被空白空间均匀间隔开,第一个项目在开端位置,最后一个项目在末端位置。
space-around:所有flex item周围以同等空间均匀间隔,它和space-between的区别是开始和结束有空白区域,并且为中间区域的一半。
* Align-items
指定item在交叉轴的对齐方式
|
* Flex-wrap
Flex-wrap指明了当可用排版空间不足时,是否允许换行,以及换行后的顺序。
flex-wrap: wrap(默认) / nowrap / wrap-reverse |
* Align-content
指定container中存在多行情况下,在交叉轴上的布局方式,这里需要注意的是与Align-items的区别,Align-items 是单行上各个元素的对齐方式,是站在item元素的角度,Align-content是多行的情况下,整个内容在交叉轴上的分布关系。是站在交叉轴整体布局角度。
align-content: stretch / flex-start / flex-end / center / space-between / space-around |
3.2 Flex item布局属性
* Align-self
align-self 属性可重写flex container 的 align-items 属性。
align-self: auto / stretch / flex-start / flex-end / center / base-line |
* Order
指定项目的排列顺序,值越大排在越后面
order: (默认 0) |
* Flex-grow
指定item的放大比例,默认为0,即如果存在剩余空间,也不进行放大。
flex-grow: (默认 0) |
* Flex-shrink
指定item的缩小比例,默认为1,即在空间不足(仅当不换行时候起效),所有item等比缩小,当设置为0,该item不进行缩小。
flex-shrink: number (默认 1) |
* Flex-basis
指定item的主轴的初始大小,auto 的含义是参考 width 或 height 的大小,
flex-basis: number / auto(默认 auto) |
width * height && max-width * max-height && min-width * min-height
item 的尺寸参数,最大尺寸,最小尺寸
Aspect Ratio
item 的宽高比
4. YogaKit 使用
YogaKit 有如下属性,有了上面的讲解估计都比较熟悉了
@property (nonatomic, readwrite, assign) YGDirection direction; |
我们看下,一个布局实例,整个步骤分成如下4步:
1. 设置view的layout |
其中步骤1.和步骤2是我们来完成的,3,4是yoga完成的。
-(void)configSubViewsLayout { |
是不是很像Masonry? 下一节我们将通过源码分析来看看它具体的工作原理。
5. YogaKit 源码解析
当我们使用view.yoga的时候,YOGA会通过关联属性来为view添加一个YGLayout的属性,这个属性中存放的是yoga的布局约束
- (YGLayout *)yoga { |
调用 configureLayoutWithBlock的时候,我们会将为当前view添加的yoga关联属性引用传出去,在bock中我们为它设置布局约束。
- (void)configureLayoutWithBlock:(YGLayoutConfigurationBlock)block { |
我们来看下YGLayout,,它有两个开关属性,isIncludedInLayout,isEnabled一般我们都是需要将其打开的。
/** |
上面只是通过block将通过关联属性添加的ASLayout类型的yoga属性传递出去,让我们在block里面设置,但是这些属性还没有应用到view上面,所以必须调用applyLayoutPreservingOrigin方法应用这些设置,我们接下来看下applyLayoutPreservingOrigin方法。
|
这里很明确分成两步:
- measure 指测量item所需要的大小,以及子节点的大小,也就是确定width 和 height
- layout 指将item确定的放置在具体的 (x, y) 点,也就是确定对应的posion
二者构成了UIView的frame
我们来看下第一步 – 测量
这里首先构建出当前view为起点的节点树,然后通过YGNodeCalculateLayout来算出宽高传递出去。这里关键的是YGNodeCalculateLayout这个方法,它是yoga的算法,是用C++实现的,由于代码很长,其中核心的Flex布局算法实现函数长达几千行,占Yoga.c的 2/3,第一次看很没有勇气看下去。所以后续会另起一篇博客来详细分析Yoga的底层实现。所以这里只要简单知道这些方法是干啥的就好。
- (CGSize)calculateLayoutWithSize:(CGSize)size { |
通过上面的计算我们获得了节点的详细参数,所以根据这些参数就可以很容易计算出对应的frame数值。下面是布局的具体代码。
static void YGApplyLayoutToViewHierarchy(UIView *view, BOOL preserveOrigin) |
到目前位置我们分析了整个YogaKit的源码,其实最核心最重要的还是Yoga底层的代码,这里由于篇幅原因将其放到下一篇博客中。