咱們首先經過下面這個例子回顧一下已經學習過的 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
掃描下方二維碼,關注「面試官小健」