前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flexbox布局杂谈

Flexbox布局杂谈

作者头像
拉维
发布2019-08-12 15:50:12
2.1K0
发布2019-08-12 15:50:12
举报
文章被收录于专栏:iOS小生活iOS小生活

也许你并不知道Flexbox是什么,但是想必你肯定听说过React Native、Weex、和Texture(AsyncDisplayKit),Flexbox就是这些知名布局库所采用的布局思路。甚至苹果官方在iOS9的时候推出的UIStackView,采用的也是FlexBox思路来实现布局的。

目前看来,iOS系统提供的布局方式有两种

  • 一种是frame这种原始方式,也就是通过设置横纵坐标和宽高来确定布局。这种布局方式代码量大,维护起来超级烦琐,但是性能是最好的。
  • 另一种是自动布局(Auto Layout),相比较于Frame需要指出每个视图的精确位置和大小,自动布局对于视图位置的描述更加简洁和易读,只需要确定两个视图之间的关系就能够确定布局。

通过Masonry和SnapKit这些第三方库,自动布局的易用性也有了很大提升。并且在iOS12以后,苹果公司也已经解决了自动布局在性能方面的问题(详见Auto Layout浅析)。

那么在这种情况下,我们为什么还要关注其他布局思路呢?原因如下:

  1. 虽然AutoLayout已经足够优秀,但是跟Flexbox比,仍有差距。Flexbox比AutoLayout提供了更多、更规范的布局方法,且更容易使用,而且苹果推出的使用Flexbox布局思路的UIStackView,我们也是需要去了解一下的。
  2. 在现在跨端方案越来越火热的时刻,势必要使用一种通用的布局思想。Flexbox在2009年被W3C提出,可以很简单、完整地实现各种页面布局,而且还是响应式的,开始被应用于前端领域,目前所有浏览器都已支持。后来通过RN和Weex等框架,它被带入到客户端开发当中,同时支持了iOS和Android。

与自动布局类似,Flexbox也是使用的描述性的语言来布局。使用Flexbox布局的视图元素叫Flex容器(flex container),其子视图元素会自动成为容器成员,叫做Flex项目(flex item)。Flexbox布局的主要思想是,通过 Flex 容器设定的属性来改变内部 Flex 项目的宽高,并调整 flex 项目的位置来填充 flex 容器的可用空间

如图所示,一个flex容器默认存在两根轴,水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的开始位置(与边框的交叉点)叫做main start,结束位置叫做main end,交叉轴的开始位置叫做cross start,结束位置叫做cross end。

项目默认是沿主轴排列的,单个项目占据的主轴空间叫做mainSize,占据的交叉轴空间叫做crossSize。

目前我的工程还是纯原生开发,因此不能使用ReactNative或者Weex来体验Flexbox布局,不过倒是可以使用Texture来通过flexbox思路进行页面布局。

Texture 如何使用 Flexbox 思路进行布局?

Texture框架的布局方案考虑的是十分长远的,并且也已经十分成熟,虽然学习起来费些力气,但是性能上远好于苹果的自动布局。

Texture框架的布局中,Texture考虑到布局的扩展性,提供了一个基类ASLayoutSpec。这个基类提供了布局的基本能力,使得Texture可以通过它扩展实现多种布局思路,比如Inset、Overlay、Ratio、Relative、Absolute等布局思路,也可以继承自ASLayoutSpec来自定义你的布局算法

ASLayoutSpec的子类及其具体的功能如下:

  • ASAbsoluteLayoutSpec,绝对布局
  • ASBackgroundLayoutSpec,背景布局
  • ASInsetLayoutSpec,边距布局
  • ASOverlayLayoutSpec,覆盖布局
  • ASRatioLayoutSpec,比例布局
  • ASRelativeLayoutSpec,相对布局
  • ASCenterLayoutSpec,居中布局
  • ASStackLayoutSpec,盒子布局
  • ASWrapperLayoutSpec,填充布局
  • ASCornerLayoutSpec,角标布局

ASLayoutSpec子类实现了各种布局思路,ASLayoutSpec会制定各种布局相通的协议方法,遵循这些协议后,可以保证这些子类能够使用相同的规则去实现更丰富的布局。

通过ASLayoutSpec遵循的ASLayoutElement协议,可以知道ASLayoutSpec提供的基本能力有哪些。ASLayoutElement协议定义如下:

代码语言:javascript
复制
@protocol ASLayoutElement <ASLayoutElementExtensibility, ASTraitEnvironment, ASLayoutElementAsciiArtProtocol>
#pragma mark - Getter
@property (nonatomic, assign, readonly) ASLayoutElementType layoutElementType;
@property (nonatomic, strong, readonly) ASLayoutElementStyle *style;
- (nullable NSArray<id<ASLayoutElement>> *)sublayoutElements;
#pragma mark - Calculate layout
/**
 * 要求节点根据给定的大小范围返回布局
 */
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize;
/**
 * 在子 layoutElement 上调用它来计算它们在calculateLayoutThatFits: 方法里面实现的布局
 */
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize;
/**
 * 重写此方法以计算 layoutElement 的 布局.
 */
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize;
/**
 * 重写此方法允许你接收 layoutElement 的大小。使用这些值可以计算最终的约束大小,但这个方法要尽量少用。
 */
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
                     restrictedToSize:(ASLayoutElementSize)size
                 relativeToParentSize:(CGSize)parentSize;
- (BOOL)implementsLayoutMethod;
@end

通过上面的代码可以看出,协议定义了layoutThatFits: 和 calculateLayoutThatFits: 等回调方法。其中,layoutThatFits 回调方法用来要求节点根据给定的大小范围返回布局,重写calculateLayoutThatFits方法用以计算layoutElement的布局。定义了统一的协议方法,能让ASLayoutSpec统一透出布局计算能力,统一规范的协议方法,也有利于布局算法的扩展。

接下来我们来看看ASLayoutSpec的子类中,应用最广泛的ASStackLayoutSpec。它和iOS中自带的UIStackView类似,布局思路参照了Flexbox,比如horizontalAlignment、alignItems、flexWrap等属性很容易和Flexbox对应上。

下面是一段ASStackLayoutSpec示例代码,我们可以通过示例了解如果通过Texture使用Flexbox布局思路开发界面:

代码语言:javascript
复制
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constraint
{
 // 创建一个纵轴方向的 ASStackLayoutSpec 视图容器 vStack
 ASStackLayoutSpec *vStack = [[ASStackLayoutSpecalloc] init];
 // 设置两个子节点,第一个节点是标题,第二个正文内容
    [vStack setChildren:@[titleNode, bodyNode]];
 // 创建一个横轴方向的 ASStackLayoutSpec 视图容器 hstack
 ASStackLayoutSpec *hstack = [[ASStackLayoutSpecalloc] init];
    hStack.direction          = ASStackLayoutDirectionHorizontal;
    hStack.spacing            = 5.0; // 设置节点间距为 5
 // 在 hStack 里添加 imageNode 和 vStack 节点
    [hStack setChildren:@[imageNode, vStack]];
 // 创建一个 ASInsetLayoutSpec 容器,设置四周边距为 5,将 hStack 作为其子节点
 ASInsetLayoutSpec *insetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(5,5,5,5) child:hStack];
 return insetSpec;
}

上面这段代码,首先会创建一个纵轴方向的ASStackLayoutSpec视图容器vStack;然后,为vStack设置两个子节点,第一个子节点是标题,第二个子节点是正文内容;接下来,创建一个横轴方向的ASStackLayoutSpec视图容器hstack,在hstack里添加imageNode和vStack节点;最后,创建一个ASInsetLayoutSpec容器,设置四周边距为5,将hStack作为其子节点。

上面示例代码对应的视图效果如下:

除了Texture用到Flexbox的布局思想以外,ReactNativeWeex也用到了Flexbox布局思路,这两个框架对Flexbox布局思想的实现,通过一个叫Yoga的C++库。

除了React Native、Weex外,Yoga还为很多其他的开源框架提供支持,比如Litho、ComponentKit等。

Yoga布局库是对Texture布局思想的实现,是有C/C++语言编写的,依赖少、编译后的二进制文件也小,基于此,Yoga可以用于多平台,可以很方便地集成到Android和iOS上

除了Flexbox思路的布局ASStackLayoutSpec以外,Texture中还有wrapper、inset、overlay、ratio、relative、absolute等针对不同场景的布局思路,同时还支持自定义布局算法

接下来我们就聊聊Flexbox布局的算法是怎样的。了解Flexbox布局算法设计,一方面能够让你更好地理解flexbox布局;另一方面,你也可以借此完整地了解一个布局算法是怎么设计的,使得你以后也能够设计出适合自己业务场景的布局算法

Flexbox算法

Flexbox算法的主要思想是:让flex容器能够改变其flex项目的宽高和顺序,以填充可用空间,flex容器可以通过扩大flex项目来填充可用空间,或者缩小flex项目来使其不超出可用空间

Flexbox算法的实现是非常复杂的,不是一两句话就能说明白的,可以参考如下网址,或者点击“阅读原文”窥其一二:

https://www.cnblogs.com/yunqishequ/p/10006872.html

总结:

1,iOS原生的Frame布局语法太繁琐;AutoLayout虽然通过masonry减少了使用难度,但是性能不太好;Flexbox布局思路目前流行广泛,大有一统天下之趋势,使用一个统一的布局思想能够大大提高开发效率。基于以上几点,本人倾向于在项目中使用Flexbox布局。

2,如果你目前使用的是RN、Weex等,那么恭喜你已经在使用Flexbox布局。如果你是原生开发,那么可以通过Texture或者UIStackView来使用Flexbox布局。

以上

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-05-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 iOS小生活 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档