我们可以给@ViewBuilder闭包参数一个默认值吗?
我在做实验的时候出现了这个问题:
\nimport 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())\nRun Code Online (Sandbox Code Playgroud)\n(\xe2\xad\x90\xef\xb8\x8f 我的自定义扩展在这里Gradient提到。)
结果非常好:
\n\n当我尝试更进一步并为这些@ViewBuilder闭包提供默认值时,一切都变得很糟糕:
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())\nRun Code Online (Sandbox Code Playgroud)\nT无法从上面的代码推断出类型参数。\n有什么想法吗?
----[编辑]----
\n我已经为我的问题做出了一些努力,这是迄今为止我得到的:
\nimport 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())\nRun Code Online (Sandbox Code Playgroud)\n结果是:
\n\n如果两个参数都能自动推断出来就完美了。
\n----[再次编辑]----
\n根据@Asperi之前的建议,我已经进行了第三次尝试:
\nimport 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())\nRun Code Online (Sandbox Code Playgroud)\n结果是:
\n\n我已经实现了这些便利初始化程序所需的所有扩展,它可以工作,但如果我们能够找到一种方法来首先避免这些扩展,那就完美了。
\n是否可以?
\n这是可能方法的演示(您可以替换您的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()它生成默认的凹槽和条。
| 归档时间: |
|
| 查看次数: |
1871 次 |
| 最近记录: |