SwiftUI 和 Swift 5.1 新特性(4) 苹果先斩后奏?Function Builder 造就 SwiftUI 的 DSL

栏目: Swift · 发布时间: 6年前

内容简介:我们首先通过下面这个例子回顾一下已经学习过的 SwiftUI 中的 Swift 5.1 新特性:我们终于来到了 SwiftUI 中所包含的最后一个重要特性:FunctionBuilder,之所以放在最后一篇中来讲,是因为它到目前为止仍旧是一个还未经过 Swift Evolution 评审的语言特性,苹果为了赶 WWDC 19 的时间点,因此先斩后奏赶鸭子上架了,因此本文讨论的细节可能在将来发生变化,但是由于 iOS 13 发布的迫近,应当不会推倒重来。我们仔细分析下上述 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 代码中的语法需要:

some View

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

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

SwiftUI 和 Swift 5.1 新特性(4) 苹果先斩后奏?Function Builder 造就 SwiftUI 的 DSL

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Math Adventures with Python

Math Adventures with Python

Peter Farrell / No Starch Press / 2018-11-13 / GBP 24.99

Learn math by getting creative with code! Use the Python programming language to transform learning high school-level math topics like algebra, geometry, trigonometry, and calculus! In Math Adventu......一起来看看 《Math Adventures with Python》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

MD5 加密
MD5 加密

MD5 加密工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具