声明式UI布局库Sugar

573 阅读8分钟

1、命令式编程和声明式编程

  • 命令式(imperative)编程:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。
  • 声明式(declarative)编程:告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。

image.png

命令式炒菜方式:
我:机器,开火
机器:执行开会操作
我:倒油
机器:执行倒油操作.
我:放菜
机器:执行放菜操作.
我:…(继续发号施令)

总结:怎么做,从头到尾告诉你

声明式炒菜方式:

我:机器,做一盘西红柿炒鸡蛋

机器:OK,做好了(机器内部自己做了、开火、倒油、放菜等操作,会直接给我们想要的结果)。

总结:通知你我现想要什么,至于有什么步骤我不关心,你给我结果就行

命令式编程: 是一种描述电脑所需作出的行为的编程典范

声明式编程: 是一种编程范式,与命令式编程相对立。它描述目标的性质,让电脑明白目标,而非流程。而命令式编程则需要用算法来明确的指出每一步该怎么做。

声明式是面向一个环境、一个群体表达某些规则,而命令式所面向的,是一个明确的对象,在指使他们做一些明确的事情

所有计算机编码的过程,就是用逻辑表达现实的过程。定义的接口,越接近现实的表达就叫越声明式(declarative),越接近计算机的执行过程就叫越命令式(imperative)。注意这不是绝对的概念,而是相对的概念。

2、声明式编程的优缺点

2.1 优点

  • 声明式让你可以更关注在状态表现,而不用去考虑底层如何实现
  • 声明式编程能在特定的更高层码领域给我们带来效率的提升,程序员只需要对想要的结果(What)进行深思熟虑,程序会帮助你解决过程(How)
  • 当然代码看起来更加简洁,可读性更强
  • 视图描述语法,是和我们设备屏幕上的布局视图相对应的

2.2 不足

  • 调试困难
  • 对于Native开发者来说有一定门槛

3、UI中的设计趋势

3.1 SwiftUI (iOS 13及之后)

SwiftUI对Apple世界中的整个UI开发进行了革新。在所有Apple平台上都是本机,具有所有这些出色的设计工具,为UI开发树立了很高的标准。还可以从命令式UIKit / AppKit / WatchKit转到声明式设计,以使UI开发更加轻松直接。那么,您今天如何实现UI?您可以通过文字描述用户界面。如果您需要状态,则可以使用@State标记变量,然后使用它向其中写入内容,例如$ name。要读取内容,我们只需要使用(name)。这就是我们所说的"两种方式绑定"。因此,每次我们将某些内容写入TextField时,变量名称都会更新,并且使用该变量的Text也会自动更新。

image-20211216134809420.png

3.2 Android Jetpack Compose

通过将Android Jetpack Compose添加到Android的UI开发区域,Android UI开发遵循SwiftUI的方式。由于Kotlin优于Java,Jetpack Compose仅使用Kotlin(无Java实现)?我不同意,我们只能在Kotlin中进行此类UI开发,但是Google似乎希望在Android开发中将Kotlin推向Java之上,并且不会为Java开发人员实现相同的功能。

image-20211216134837676.png

3.3 Flutter

Flutter是面向iOS和Android应用,提供一套基础代码的高性能高可靠软件开发工具包,使开发者能够在iOS和Android两个最主要的移动平台上,打造统一代码的高性能应用。

image-20211216134853619.png

3.4 示例

分别用 UIKit 和 SwiftUI 创建下图这个简单的列表

image-20211202103519514.png

如果用 UIKit 创建则需要下面这下代码

image-20211216135901956.png

而 SwiftUI 仅仅需要下面几行清晰的代码就可以完成

image-20211216135929777.png

可以明显的看到差别,SwiftUI 对于效率的提升是比较可观的

4、布局库Sugar

4.1 简介

截屏2021-12-16 下午1.50.12.png

Sugar,基于布局引擎yoga的声明式布局框架,支持响应式的修改,iOS13及以上支持实时预览视图。无需关心位置坐标,只需要在行布局row、列布局column、堆叠布局overlay等堆砌UI控件即可。便捷、快速高效的UI布局利器,可以支持iOS10及以上版本

Yoga 是一个基于 Flexbox 的跨平台布局引擎,能使布局工作更容易。你可以使用 Yoga 作为一个通用的布局系统,来代替 iOS 上的 Auto Layout 。最初是 Facebook 在 2014 年推出的一个 CSS 布局的开源库,2016 年改版并更名为 Yoga。Yoga 支持多个平台,包括 Java、C#、C 和 Swift。


下面可以看下这个视图,思考是如何实现的

image-20211216135048917.png

SnapKit版:

image-20211216135142091.png

UIStackView版:

image-20211216135232543.png

Sugar版:

image-20211216135301467.png

4.2 Sugar的结构图和原理

Z8wuQn4u71.png

主要计算原理是基于yoga,初始化关联YGNode和当前对象,设置好视图的yoga布局的一些属性(如宽、高、对齐方式等),yoga布局引擎自动算出视图对应的xywidthheight。然后设置到当前视图的frame

同时利用swift的@resultBuilder语法糖实现嵌套式的布局写法,响应式的修改则是hookview的layoutSubviews方法,当frame变化时,自动调用根节点的sizeThatFits,applyLayout的方法,计算出视图新的坐标和大小。响应式的功能支持,布局时传入@State修饰的变量,比如width,后续只需要修改width属性的值,即可自动更新视图的宽度。Overlay(Z轴)的使用务必提前设置好宽高

4.3 Sugar如何使用

案例1:

image-20211216135421925.png

image-20211216135456696.png

案例2:

a2sjd-u4mhi.gif

image-20211216135532815.png

image-20211216135542183.png

4.4 Sugar的主要布局属性Api

width:宽度

maxWidth:最大宽度

minWidth:最小宽度

height:高度

maxHeight:最大高度

minHeight:最小高度

  • AUTO是默认值,Yoga根据元素的内容(无论是其他子元素,文本还是图像)计算元素的宽度/高度。 
  • PIXELS 定义绝对像素的宽度/高度。 取决于Yoga节点上设置的其他属性(译者注:如flex grow ,padding),这可能是节点的最终尺寸,也可能不是。 
  • PERCENTAGE 分别以其父对象的宽度或高度的百分比定义宽度或高度 

justifyContent:在主轴上的对齐方式:FlexStart(主轴起点对齐),FlexEnd(主轴终点对齐),Center(居中对齐)

flexShrink:与 FlexGrow 处理空间剩余相反,FlexShrink 用来处理空间不足的情况。即怎么缩小。

FlexShrink 默认为1,即如果空间不足,该项目将缩小

如果所有 Flex Item 的 FlexShrink 属性都为 1,当空间不足时,都将等比例缩小。

如果一个 Flex Item 的 FlexShrink 属性为 0 ,其余 Flex Item 都为1,则空间不足时,FlexShrink 为 0 的前者不缩小。

flexGrow:决定剩余空间怎么分配。如果flex item的flexGrow为0,该flex item不会占用剩余的空间。如果多个flex item的flexGrow不为0,则按flexGrow的值按比例进行划分。

margin:(类似于针对子视图)影响节点外部周围的间距。 具有边距的节点将使其自身偏离其父级的边界,但也会偏移所有同级的位置。 如果父节点是自动调整大小的,则节点的边距将对其父节点的总大小有所贡献。

padding:(类似于针对父视图)会影响它所应用的节点的大小。在 Yoga中,填充就像box-sizing:border-box;一样。 也就是说,如果元素具有显式大小,则填充不会增加元素的总大小。 对于自动调整大小的节点,填充将增加节点的大小以及偏移任何子节点的位置。

border:在Yoga中的行为与填充(padding)完全一样,因为作为单独的属性存在,优先级高于框架的暗示(注,类似于框架的全局设定)。 然而,Yoga并没有做任何绘图,因此仅在布局过程中使用此信息,边框的行为完全类似于填充。

aspectRatio:宽高比,具有比flex grow(弹性增长)更高的优先级

position:针对Overlay使用,上下左右的edge

alignContent:定义沿交叉轴(cross-axis,也主轴垂直的轴)的线分布。 仅当使用flex wrap将项目包装到多行时,此功能才有效。

wrap:是在容器上设置的,决定在轴线上排列不下时,视图的换行方式。

默认设置为 NoWrap,不会换行,一直沿着主轴排列到屏幕之外

设置为 Wrap ,则空间不足时,自动换行

设置 WrapReverse,则换行方向与 Wrap 相反

5、写在最后

目前我们只是依据平台语言特性,针对UI布局库的优化,提供高效的布局库。暂时没有类似于SwiftUI的多种控件的功能。

如果有需要,后续考虑进一步支持UIButton、UILabel、UIText、UIImageView等UI视图的直接使用,并支持直接调用属性即可实现多种动画的效果。