内容简介:我们首先通过下面这个例子回顾一下已经学习过的 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
扫描下方二维码,关注“面试官小健”
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 直击|淘宝蒋凡:个性化推荐超传统搜索 AI造就新业态
- 有了ACE王牌计划的百度AI City,19年要造就一批网红城市?
- 『互联网架构』软件架构-redis特性和集群特性(中)(49)
- 『互联网架构』软件架构-redis特性和集群特性(上)(48)
- 『互联网架构』软件架构-redis特性和集群特性(下)(50)
- JDK 14 功能特性
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。