@ViewBuilder 闭包的默认值?

loc*_*wei 5 swiftui

我们可以给@ViewBuilder闭包参数一个默认值吗?

\n

我在做实验的时候出现了这个问题:

\n
import SwiftUI\nimport PlaygroundSupport\n\n// MyView\nstruct MyView<S:View, T:View>: View {\n    \n    let groove: S\n    let bar   : T\n    let p     : CGFloat = 10  // padding\n    \n    // \xe2\xad\x90\xef\xb8\x8f no default values\n    init(@ViewBuilder groove: () -> S, @ViewBuilder bar: () -> T) {\n        self.groove = groove()\n        self.bar    = bar()\n    }\n    \n    var body: some View {\n        ZStack {\n            groove\n            bar.padding(p)\n        }.frame(height: 80)\n    }\n}\n\n// content view\nstruct ContentView: View {\n    var body: some View {\n        // using MyView\n        MyView(groove: { \n            Gradient.down(.gray, .white) // \xe2\xad\x90\xef\xb8\x8f my custom LinearGradient extension\n        }, bar: { \n            Gradient.right(.purple, .yellow, .pink) // \xe2\xad\x90\xef\xb8\x8f my custom extension\n        })\n    }\n}\n\nPlaygroundPage.current.setLiveView(ContentView())\n
Run Code Online (Sandbox Code Playgroud)\n

(\xe2\xad\x90\xef\xb8\x8f 我的自定义扩展在这里Gradient提到。)

\n

结果非常好:

\n

在此输入图像描述

\n

当我尝试更进一步并为这些@ViewBuilder闭包提供默认值时,一切都变得很糟糕:

\n
import SwiftUI\nimport PlaygroundSupport\n\nstruct MyView<S:View, T:View>: View {\n    \n    let groove: S\n    let bar   : T\n    let p     : CGFloat = 10  // padding\n    \n    // \xe2\xad\x90\xef\xb8\x8f try to give @ViewBuilder closures default values\n    init(\n        @ViewBuilder \n        groove: () -> S = { Gradient.down(.gray, .white) } as! () -> S, \n        @ViewBuilder \n        bar: () -> T = { Gradient.right(.purple, .yellow, .pink) } as! () -> T\n    ) {\n        self.groove = groove()\n        self.bar = bar()\n    }\n    \n    var body: some View {\n        ZStack {\n            groove\n            bar.padding(p)\n        }.frame(height: 80)\n    }\n}\n\nstruct ContentView: View {\n    var body: some View {\n        VStack {\n            // \xe2\x9d\x8c can\'t infer `T`\n            MyView(groove: { \n                Gradient.down(.gray, .white)\n            })\n        }\n    }\n}\n\nPlaygroundPage.current.setLiveView(ContentView())\n
Run Code Online (Sandbox Code Playgroud)\n

T无法从上面的代码推断出类型参数。\n有什么想法吗?

\n

----[编辑]----

\n

我已经为我的问题做出了一些努力,这是迄今为止我得到的:

\n
import SwiftUI\nimport PlaygroundSupport\n\n// default values for @ViewBuilder parameters\n@ViewBuilder var defaultGroove: some View {\n    Gradient.down(.gray, .white)\n}\n\n@ViewBuilder var defaultBar: some View {\n    Gradient.right(.purple, .yellow, .pink)\n}\n\n// MyView\nstruct MyView<S:View, T:View>: View {\n    \n    let groove: S\n    let bar   : T\n    \n    // \xe2\xad\x90\xef\xb8\x8f try to give @ViewBuilder parameters default value\n    init(\n        @ViewBuilder groove: () -> S = { defaultGroove as! S }, \n        @ViewBuilder bar   : () -> T = { defaultBar as! T }\n    ) {\n        self.groove = groove()\n        self.bar = bar()\n    }\n    \n    var body: some View {\n        ZStack {\n            groove\n            bar.padding(10)\n        }.frame(height: 80)\n    }\n}\n\n// Content View\nstruct ContentView: View {\n    var body: some View {\n        VStack {\n            \n            // `bar` omitted\n            MyView<LinearGradient, LinearGradient>(groove: { \n                Gradient.bottomRight(.white, .gray, .white)\n            })\n            // `groove` omitted\n            MyView<LinearGradient, Color>(bar: { \n                Color.pink\n            }) \n            // both omitted\n            MyView<LinearGradient, LinearGradient>()\n            \n            // \xe2\x9d\x8c can\'t infer `S`\n            // \xe2\xad\x90\xef\xb8\x8f it would be perfect if `S` can be inferred.\n//              MyView(bar: { \n//                  Gradient.right(.purple, .white)\n//              })\n        }\n    }\n}\n\nPlaygroundPage.current.setLiveView(ContentView())\n
Run Code Online (Sandbox Code Playgroud)\n

结果是:

\n

我的看法也是

\n

如果两个参数都能自动推断出来就完美了。

\n

----[再次编辑]----

\n

根据@Asperi之前的建议,我已经进行了第三次尝试:

\n
import SwiftUI\nimport PlaygroundSupport\n\n// default values for @ViewBuilder parameters\n\n@ViewBuilder var defaultGroove: some View {\n    Gradient.down(.gray, .white)\n}\n\n@ViewBuilder var defaultBar: some View {\n    Gradient.right(.purple, .yellow, .pink)\n}\n\n// MyView\nstruct MyView<S:View, T:View>: View {\n    \n    let groove: S\n    let bar   : T\n    \n    // default value for @ViewBuilder parameters\n    init(\n        @ViewBuilder groove: () -> S = { defaultGroove as! S }, \n        @ViewBuilder bar: () -> T = { defaultBar as! T }\n    ) {\n        self.groove = groove()\n        self.bar = bar()\n    }\n    \n    var body: some View {\n        ZStack {\n            groove\n            bar.padding(10)\n        }.frame(height: 80)\n    }\n}\n\n// \xe2\xad\x90\xef\xb8\x8f conditional extensions for convenience inits\n\nextension MyView where T == LinearGradient {\n    /// MyView(groove:)\n    init(@ViewBuilder groove: () -> S){\n        self.init(\n            groove: groove, \n            bar   : { defaultBar as! T }\n        )\n    }\n}\n\nextension MyView where S == LinearGradient {\n    /// MyView(bar:)\n    init(@ViewBuilder bar: () -> T){\n        self.init(\n            groove: { defaultGroove as! S }, \n            bar   : bar\n        )\n    }\n}\n\nextension MyView where S == LinearGradient, T == LinearGradient {\n    /// MyView()\n    init(){\n        self.init(\n            groove: { defaultGroove as! S }, \n            bar   : { defaultBar    as! T }\n        )\n    }\n}\n\n// Content View\nstruct ContentView: View {\n    var body: some View {\n        VStack {\n            \n            // \xe2\xad\x90\xef\xb8\x8f `S`, `T` are both inferred in the following cases\n            \n            MyView(groove: { \n                Gradient.bottomRight(.white, .yellow, .green)\n            }, bar: {\n                Color(white: 0.8)\n                    .shadow(color: .black, radius: 3, x: 3, y: 3)\n                    .shadow(color: .white, radius: 3, x: -3, y: -3)\n            })\n            \n            // `bar` omitted\n            MyView(groove: { \n                Gradient.right(.red, .purple)\n            })\n            \n            // `groove` omitted\n            MyView(bar: { \n                Gradient.right(.purple, .white)\n                    .shadow(color: .black, radius: 3, x: 0, y: 2)\n            })\n            \n            // `groove`, `bar` both omitted \n            MyView()\n        }\n    }\n}\n\nPlaygroundPage.current.setLiveView(ContentView())\n
Run Code Online (Sandbox Code Playgroud)\n

结果是:

\n

我的观点 3

\n

我已经实现了这些便利初始化程序所需的所有扩展,它可以工作,但如果我们能够找到一种方法来首先避免这些扩展,那就完美了。

\n

是否可以?

\n

Asp*_*eri 7

这是可能方法的演示(您可以替换您的Gradient类型) - 您需要使用默认 init 来扩展专门类型。

经测试可在 Xcode 12 / iOS 14 上使用

extension MyView where S == Text, T == Button<Text> {
    init() {
        self.init(groove: { Text("Hello") },
            bar: { Button("World", action: { }) })
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你就可以使用MyView()它生成默认的凹槽和条。