什么使SwiftUI的DSL成为可能?

什么使SwiftUI的DSL成为可能?,第1张

什么使SwiftUI的DSL成为可能?

正如马丁说,如果你看的文件

VStack
init(alignment:spacing:content:)
,你可以看到
content:
参数具有的属性
@ViewBuilder

init(alignment: HorizontalAlignment = .center, spacing: Length? = nil,     **@ViewBuilder** content: () -> Content)

此属性引用

ViewBuilder
类型,如果您查看生成的接口,则该类型类似于:

**@_functionBuilder** public struct ViewBuilder {    /// Builds an empty view from an block containing no statements, `{ }`.    public static func buildBlock() -> EmptyView    /// Passes a single view written as a child view (e..g, `{ Text("Hello") }`)    /// through unmodified.    public static func buildBlock(_ content: Content) -> Content       where Content : View}

@_functionBuilder
属性是一个非官方功能的一部分,该功能称为“
函数构建器 ”,在此处已针对Swift演变推出,并专门针对Xpre 11附带的Swift版本实现,从而可以在SwiftUI中使用。

标记类型

@_functionBuilder
允许将其用作各种声明(例如函数,计算属性以及在这种情况下为函数类型的参数)上的自定义属性。此类带注释的声明使用函数构建器来转换代码块:

  • 对于带注释的功能,要转换的代码块是实现。
  • 对于带注释的计算属性,要转换的代码块是getter。
  • 对于带函数类型的带注释的参数,要转换的代码块是传递给它的任何闭包表达式(如果有)。

函数构建器转换代码的方式由构建器方法(例如)的实现定义,该方法

buildBlock
采用一组表达式并将其合并为单个值。

例如,

ViewBuilder
实现
buildBlock
1到10个
View
符合参数的实现,将多个视图合并为一个视图
TupleView

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)extension ViewBuilder {    /// Passes a single view written as a child view (e..g, `{ Text("Hello") }`)    /// through unmodified.    public static func buildBlock<Content>(_ content: Content)       -> Content where Content : View    public static func buildBlock<C0, C1>(_ c0: C0, _ c1: C1)       -> TupleView<(C0, C1)> where C0 : View, C1 : View    public static func buildBlock<C0, C1, C2>(_ c0: C0, _ c1: C1, _ c2: C2)      -> TupleView<(C0, C1, C2)> where C0 : View, C1 : View, C2 : View    // ...}

这允许传递给

VStack
的初始化程序的闭包内的一组视图表达式被转换为对调用的调用,
buildBlock
该调用采用相同数量的参数。例如:

struct ContentView : View {  var body: some View {    VStack(alignment: .leading) {      Text("Hello, World")      Text("Hello World!")    }  }}

转换为对的调用

buildBlock(_:_:)

struct ContentView : View {  var body: some View {    VStack(alignment: .leading) {      ViewBuilder.buildBlock(Text("Hello, World"), Text("Hello World!"))    }  }}

导致不透明的结果类型

someView
由满足
TupleView<(Text, Text)>

您会注意到,最多

ViewBuilder
只能定义
buildBlock
10个参数,因此,如果我们尝试定义11个子视图,则:

  var body: some View {    // error: Static member 'leading' cannot be used on instance of    // type 'HorizontalAlignment'    VStack(alignment: .leading) {      Text("Hello, World")      Text("Hello World!")      Text("Hello World!")      Text("Hello World!")      Text("Hello World!")      Text("Hello World!")      Text("Hello World!")      Text("Hello World!")      Text("Hello World!")      Text("Hello World!")      Text("Hello World!")    }  }

我们会收到一个编译器错误,因为没有构建器方法可以处理此代码块(请注意,由于此功能仍在开发中,因此围绕它的错误消息不会有太大帮助)。

实际上,我不认为人们会经常遇到这种限制,例如,使用

ForEach
视图代替上面的示例会更好:

  var body: some View {    VStack(alignment: .leading) {      ForEach(0 ..< 20) { i in        Text("Hello world (i)")      }    }  }

但是,如果确实需要超过10个静态定义的视图,则可以使用该

Group
视图轻松解决此限制:

  var body: some View {    VStack(alignment: .leading) {      Group {        Text("Hello world")        // ...        // up to 10 views      }      Group {        Text("Hello world")        // ...        // up to 10 more views      }      // ...    }

ViewBuilder
还实现其他功能构建器方法,例如:

extension ViewBuilder {    /// Provides support for "if" statements in multi-statement closures, producing    /// ConditionalContent for the "then" branch.    public static func buildEither<TrueContent, FalseContent>(first: TrueContent)      -> ConditionalContent<TrueContent, FalseContent>where TrueContent : View, FalseContent : View    /// Provides support for "if-else" statements in multi-statement closures,     /// producing ConditionalContent for the "else" branch.    public static func buildEither<TrueContent, FalseContent>(second: FalseContent)      -> ConditionalContent<TrueContent, FalseContent>where TrueContent : View, FalseContent : View}

这使它能够处理if语句:

  var body: some View {    VStack(alignment: .leading) {      if .random() {        Text("Hello World!")      } else {        Text("Goodbye World!")      }      Text("Something else")    }  }

变成:

  var body: some View {    VStack(alignment: .leading) {      ViewBuilder.buildBlock(        .random() ? ViewBuilder.buildEither(first: Text("Hello World!"))       : ViewBuilder.buildEither(second: Text("Goodbye World!")),        Text("Something else")      )    }  }

(发出冗余的1个参数需要

ViewBuilder.buildBlock
明确说明)。



欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5642141.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存