我是靠谱客的博主 唠叨板栗,最近开发中收集的这篇文章主要介绍SwiftUI 和 Swift 5.1 新特性(4) 苹果先斩后奏?Function Builder 造就 SwiftUI 的 DSL,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

我们首先通过下面这个例子回顾一下已经学习过的 SwiftUI 中的 Swift 5.1 新特性:some SwiftUI 和 Swift 5.1 新特性(1) 不透明返回类型 Opaque Result Type,以及@State@Binding背后的 @propertyDelegate SwiftUI 和 Swift 5.1 新特性(2) 属性代理Property Delegates,和 @dynamicMemberLookup SwiftUI 和 Swift 5.1 新特性(3) Key Path Member Lookup

struct SlideViewer: View {
@State private var isEditing = false
@Binding var slide: Slide
var body: some View {
VStack {
Text("Slide #(slide.number)")
if isEditing {
TextFiled($slide.title)
}
}
}
}
复制代码

我们终于来到了 SwiftUI 中所包含的最后一个重要特性:FunctionBuilder,之所以放在最后一篇中来讲,是因为它到目前为止仍旧是一个还未经过 Swift Evolution 评审的语言特性,苹果为了赶 WWDC 19 的时间点,因此先斩后奏赶鸭子上架了,因此本文讨论的细节可能在将来发生变化,但是由于 iOS 13 发布的迫近,应当不会推倒重来。

1. SwiftUI DSL 的需要

我们仔细分析下上述 DSL 代码中的语法需要:

  1. 从表达方式上从简:尽量省略不必要的逗号,return,中括号等等。
  2. 支持简单的逻辑控制,比如 if 控制语句。
  3. 强类型:some View 代表了一个复合的强类型,在 View 发生改变的时候,复合的强类型有助于做 View diff 优化。
  4. 与 Swift 已有的语法不冲突。

2. 通过 ViewBuilder 来理解 @_functionBuilder

就像 @propertyDelegate 用来修饰 State一样,@_functionBuilder 用来修饰 ViewBuilder,这里同样 ViewBuilder 也不过是一个编译器会使用它、并且对它所包含的方法有一定要求的类型。那么 ViewBuilder 在哪里呢?其实就在各种容器类型的最后一个闭包参数中,以VStack为例:

// 定义
struct VStack<Content> where Content : View {
init(alignment: HorizontalAlignment = .center, spacing: Length? = nil,
@ViewBuilder content: () -> Content)
}
// 使用
struct ContentView : View {
var body: some View {
VStack(alignment: .leading) {
Text("Hello, World")
Text("Leon Lu")
}
}
}
复制代码

上面这个例子中,我们看到 SwiftUI 中如何在一个容器类型中平铺其包含的属性;在闭包的函数定义中,我们看到了 ViewBuidler 的修饰。其实不难推断,为了能编译过,ViewBuidler 对于这个闭包中的代码在编译阶段“动了手脚”,那么这是如何做到的呢?来看 ViewBuilder 中的关键方法:

static func buildBlock() -> EmptyView
static func buildBlock<Content>(Content) -> Content
static func buildBlock<C0, C1>(C0, C1) -> TupleView<(C0, C1)>
static func buildBlock<C0, C1, C2>(C0, C1, C2) -> TupleView<(C0, C1, C2)>
static func buildBlock<C0, C1, C2, C3>(C0, C1, C2, C3) -> TupleView<(C0, C1, C2, C3)>
static func buildBlock<C0, C1, C2, C3, C4>(C0, C1, C2, C3, C4) -> TupleView<(C0, C1, C2, C3, C4)>
...
复制代码

我们的两个 Text 的例子中,编译器自动(根据名称的约定)使用了 static func buildBlock<C0, C1>(C0, C1) -> TupleView<(C0, C1)> 方法,这时候VStack的类型就成为了 VStack<TupleView<(Text,Text)>> 了。经过 ViewBuilder 转换后的代码:

struct ContentView : View {
var body: some View {
VStack(alignment: .leading) {
ViewBuilder.buildBlock(Text("Hello, World"), Text("Leon Lu"))
}
}
}
复制代码

值得一提的是,由于 buildBlock 的 overload 版本最多泛型参数是 10 个。所以当超过 10 个的时候可以使用 Group包一下; 如果有循环可以展开,则可以使用 ForEach

3. FunctionBuilder 分支条件的情况

ViewBuilder 中还有两个函数被用来构建含分支条件时候的类型

static func buildEither<TrueContent, FalseContent>(first: TrueContent) ->
ConditionalContent<TrueContent, FalseContent>
static func buildEither<TrueContent, FalseContent>(second: FalseContent) ->
ConditionalContent<TrueContent, FalseContent>
复制代码

如果根据不同条件返回不同的视图,那么生成的类型中包含两个类型。

struct SlideViewer: View {
@State private var isEditing = false
@Binding var slide: Slide
var body: some View {
VStack {
Text("Slide #(slide.number)")
if isEditing {
TextFiled($slide.title)
} else {
Text(slide.title)
}
}
}
}
复制代码

此时,VStack的类型变成了 VStack<TupleView<(Text, ConditionalContent<TextField,Text>)>>

结语

从命名 @_functionBuilder 中包含的下划线就可以看出,Function Builder 还有一定微调的可能性,因此文中以实用主义 ViewBuilder 的视角来介绍 Function Builder 是什么。

SwiftUI 和 Swift 5.1 新特性 系列文章到此为止暂告段落,如有需要会继续更新和补充。

感谢大家厚爱,今后会有更多的文章带给大家,希望大家喜欢。

相关文章:

SwiftUI 和 Swift 5.1 新特性(1) 不透明返回类型 Opaque Result Type

SwiftUI 和 Swift 5.1 新特性(2) 属性代理Property Delegates

SwiftUI 和 Swift 5.1 新特性(3) Key Path Member Lookup

扫描下方二维码,关注“面试官小健”

转载于:https://juejin.im/post/5d0b2fa45188252e2e078170

最后

以上就是唠叨板栗为你收集整理的SwiftUI 和 Swift 5.1 新特性(4) 苹果先斩后奏?Function Builder 造就 SwiftUI 的 DSL的全部内容,希望文章能够帮你解决SwiftUI 和 Swift 5.1 新特性(4) 苹果先斩后奏?Function Builder 造就 SwiftUI 的 DSL所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(37)

评论列表共有 0 条评论

立即
投稿
返回
顶部