返回

神奇 Function Builder 造就 SwiftUI 独有 DSL

IOS

我们终于来到了 SwiftUI 中所包含的最后一个重要特性:FunctionBuilder,之所以放在最后一篇中来讲,是因为它到目前为止仍旧是一个还未经过 Swift Evolution 评审的语言特性,苹果为了赶 WWDC 19 的时间点,因此先斩后奏赶鸭子上架了,因此本文讨…

Function Builder 的前世今生

SwiftUI 中的 Function Builder 脱胎于《A DSL for Builders》,作者是来自 Facebook 的好友 Chris Eidhof,相信很多开发者对 Chris 并不会感到陌生,他曾写过不少与 Swift 和 SwiftUI 相关的文章,其中影响力最大的一篇是《Building SwiftUI Views with Functions》,里面首次对 Function Builder 的构建、使用以及扩展做了详尽的。

了解 Function Builder,需要首先知道它的上下文(Context),顾名思义,上下文就是指能对执行上下文产生影响的一切因素,例如:函数(参数/返回值)、闭包(参数/返回值)、类型方法(Self/返回值)、类方法(Self/返回值)等。

而 Function Builder 就是一种接受一个或多个代码块作为参数,并执行这些代码块,最后返回一个特定结果的函数。

Function Builder 在 SwiftUI 中的使用

说了这么多,Function Builder 到底是怎么用呢?让我们先看这样一个例子:

struct MyHStack<Content: View>: View {
  private let content: Content
  
  init(@ViewBuilder content: () -> Content) {
    self.content = content()
  }
  
  var body: some View {
    HStack { content }
  }
}

这是一个自定义的 Stack 视图,与 SwiftUI 的 HStack 唯一区别就是允许开发者在自定义的 Stack 视图中采用 ViewBuilder 构建 UI 组件,使用起来就像这样:

struct ContentView: View {
  var body: some View {
    MyHStack {
      Text("Hello, World!")
      Image(systemName: "moon.stars")
    }
  }
}

使用 ViewBuilder 来构建 UI 组件,使得整个代码看起来更加自然,更具可读性,此外 ViewBuilder 还可以用来解决一些问题,例如:

  • @Environment@EnvironmentObject 只能在 View 中使用,但有的時候需要在一个 View 的子视图中使用它们,此时可以使用 ViewBuilder 将子视图抽出来,单独放在一个 View 中,然后在该 View 中使用 @Environment@EnvironmentObject

  • 如果想把一个 View 作为另一个 View 的子视图,但又不想让该 View 受到父视图的影响,此时可以使用 ViewBuilder 将该 View 抽出来,单独放在一个 View 中,然后在该 View 中使用 @Environment(\.self) 获取父视图的环境,这样该 View 就可以不受父视图的影响了。

结语

Function Builder 是 SwiftUI 中一个非常强大的特性,它使得开发者可以使用更自然、更具可读性的代码来构建 UI 组件,此外它还可以用来解决一些问题,例如:@Environment@EnvironmentObject 只能在 View 中使用,但有的時候需要在一个 View 的子视图中使用它们,此时可以使用 ViewBuilder 将子视图抽出来,单独放在一个 View 中,然后在该 View 中使用 @Environment@EnvironmentObject