咱们首先经过下面这个例子回顾一下已经学习过的 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 发布的迫近,应当不会推倒重来。swift
咱们仔细分析下上述 DSL 代码中的语法须要:闭包
some View
表明了一个复合的强类型,在 View 发生改变的时候,复合的强类型有助于作 View diff 优化。就像 @propertyDelegate
用来修饰 State
同样,@_functionBuilder
用来修饰 ViewBuilder
,这里一样 ViewBuilder
也不过是一个编译器会使用它、而且对它所包含的方法有必定要求的类型。那么 ViewBuilder
在哪里呢?其实就在各类容器类型的最后一个闭包参数中,以VStack
为例:ide
// 定义
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 中如何在一个容器类型 VStack
的构造函数的闭包中平铺其包含的两个 Text
;另外一方面,在闭包的函数声明中,咱们看到了 @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
转换后的代码:post
struct ContentView : View {
var body: some View {
VStack(alignment: .leading) {
ViewBuilder.buildBlock(Text("Hello, World"), Text("Leon Lu"))
}
}
}
复制代码
值得一提的是,因为 buildBlock
的 overload 版本最多泛型参数是 10 个。因此当超过 10 个的时候可使用 Group
包一下; 若是有循环能够展开,则可使用 ForEach
。学习
ViewBuilder
中还有两个函数被用来构建含分支条件时候的类型优化
static func buildEither<TrueContent, FalseContent>(first: TrueContent) ->
ConditionalContent<TrueContent, FalseContent>
static func buildEither<TrueContent, FalseContent>(second: FalseContent) ->
ConditionalContent<TrueContent, FalseContent>
复制代码
若是根据不一样条件返回不一样的视图,那么生成的类型中包含两个类型。ui
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>)>>
spa
从命名 @_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
扫描下方二维码,关注“面试官小健”