使用Binding创建符合SwiftUI设计理念的组件

SwiftUI中的Binding为我们提供了双向绑定的功能,来确保View内部与外部的数据具备一致性,并且在系统组件中也有很多的应用。除了最基本的在TextField中使用Binding<String>来传递字符串数据、在Toggle中使用Binding<Bool>来记录当前开关状态,还有其他很多使用,比如在Picker中使用Binding<SelectionValue:Hashable>来记录当前选中项、在TabView中使用Binding<SelectionValue:Hashable>?来决定当前哪个TabItem是在屏幕上活跃的、为拓展Present视图的sheet方法中的Binding<Item:Identifiable?>等等。

在最开始使用SwiftUI构建功能组件的时候,往往会沿用在UIKit中的构建思路。编写的组件虽说也是可以使用,但是不论在api的简洁性上,还是在具体实现上都与系统提供的差一些意思。虽然SwiftUI为我们提供的组件有很多,但是细看归类还是可以发现大部分还是有一定设计规范的,比如数据传递中对Binding的运用,从上面列举的一部分就可以发现在某些场景下会使用对应的模式。本文会对系统提供的Binding进行应用场景的归类,接着结合两个具体的开发实例来讨论下如何编写符合SwiftUI设计理念的组件。

SwiftUI中的Data Flow(二)

我们在SwiftUI中修改数据,依赖这些数据的UI就会自动更新出来最新的值,这看起来很神奇,但是如果你看了前文之后,就多多少少会猜测到一些,我们统称这为Data Flow。在SwiftUI中,Data Flow是一个很重要很核心的概念,在我们实际的开发中,需要合理的管理自己的data flow,以确保我们拥有唯一的数据源(source of truth)。

在UIKit中,我们使用的是命令式的编程方式(imperative programming),比如前文中ModelY的属性发生变动,需要同步手动的更新我们的ModelYView。SwiftUI则采用一种声明式的编程方式(declarative programming),轻量化视图,让我们只关注会发生变化的数据,Data Flow会来完成数据与视图之间的依赖绑定,从而将最新的状态更新到视图上。在这种方式下,如果数据发生变化,SwiftUI根据数据重新构建视图,并以一种高效的diff算法来最小化的变动视图结构,这不在我们本文的讨论范围。

SwiftUI中的Data Flow(一)

SwiftUI提供了一个声明式的框架让我们来构建界面,其核心的View只是状态的一个计算属性,而不是UIKit中所真实表示的界面元素。在我们使用View构建界面的时候,还需要指明视图之间的数据依赖关系。这在往常会使用属性来设置依赖,进一步使用响应式框架(RxSwift等)的时候会使用特殊的操作来完成依赖、绑定。在swiftUI中,由于View只是状态的计算属性,而状态是以数据的形式进行存储,所以就需要有个唯一的source of truth来保证各个View与数据的状态保持同步。

Views are a function of state

在不同的使用场景下,source of truth可以是@State@Binding,也可以是@ObservedObject@EnvironmentObject,甚至是@StateObject。他们功能各不相同,但是他们所运用的核心技术是一样的:Property WrappersDynamic Member Lookup等等。本文在此基础上,先来探讨一些非swiftUI框架下开发遇到的问题,以及使用这些核心技术来解决他们,然后逐步引出swiftUI中要如何设计数据流,为后续章节做铺垫。

基于Responder Chain的对象交互

问题

在直播间中对视图进行解耦的时候,遇到了一个问题:

因为是将具体业务视图放到对应的contentView中,并且这些业务视图的事件接收方还是当前视图控制器,就会造成事件的传递需要透过contentView这一层。

如果中间只有contentView这一层还好,可以使用一个多余的delegate或者block将事件转换一下,但如果业务视图自己中也有其他的子控件需要传递事件到视图控制器,那就不止一层了。在软件开发中,只有变和不变,在这里就是如果他有一层,那么就可能有n多层,为了一层提出的方案在n多层中就会显得不那么适用,因为这样并没有解决根本问题。

当然使用通知可以无视事件触发层和处理层之间的距离,但是,通知在我看来不是一个很好的UI通信方式,并且满天飞的通知很难管理。

Self-Manager模式在业务开发中的组件化应用

Self-Manager直译就是自管理,第一次听说是在孙源的博文中,基于业务的行为一致,并且具有高复用性,可以将其进行自管理,这种模式称为Self-Manager模式。作者还举了两个例子,其中用户头像的例子在业务开发中会经常遇到,统一的圆角设计、统一的边距设计、统一的点击跳转等等,通过提供分类这种AOP思想让这个用户头像视图自己决定样式以及行为。

在常规的MVC开发中,会将子视图的样式在自身完成,而行为则上报给上层来处理,这个上层更多的是视图控制器。但是在多地方对视图的使用会使得业务代码很臃肿,行为要一次次的传递个上层,有的甚至要穿越好几层视图。而为这种行为统一的视图提供自己处理事件的能力,能够很好的解决这样的问题,这种思想也很契合组件化的初衷。

开源学习之PKProtocolExtension

在swift中有为协议添加拓展的功能,这个功能可以使协议中的一些方法能够默认实现,这个功能是一个很实用的点,但是在oc中并不支持协议的拓展,PKProtocolExtension这个工具结合runtime为oc提供了’协议拓展’的功能。

解析一个算术表达式

基于这么多年的学习,看到1+2*3-4这样的表达式,我们可以很快的想到这是一个算数表达式,使用四则运算的法则就可以得到最后的结果为2,这仅仅是由于我们固定思维(或者是学习沉淀)得出来的结果。如果将这一段字符串交给计算机,并且让它输出我们想要的结果,计算后的结果,就需要将四则运算法则告诉计算机,然后根据这个规则来解析这个表达式,最后它才能给出来我们想要的结果。

从一个纯文本表达式到最后正确的结果这个过程,可以理解为是一个翻译,将文本翻译成算术表达式,在四则运算的规则下,对这个表达式进行它语法下的运算,最后得出正确的结果。

这个简单的四则运算仅仅是一个缩影,往大了就会涉及到json的解析、解释器模式、编程语言的编译等等。这篇文章就将从解析一个算术表达式开始,来对涉及到的深层知识做一个整理。