首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >@ViewBuilder闭包的默认值?

@ViewBuilder闭包的默认值?
EN

Stack Overflow用户
提问于 2020-09-24 21:48:53
回答 1查看 294关注 0票数 2

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

当我做一些实验时,出现了这个问题:

代码语言:javascript
复制
import SwiftUI
import PlaygroundSupport

// MyView
struct MyView<S:View, T:View>: View {
    
    let groove: S
    let bar   : T
    let p     : CGFloat = 10  // padding
    
    // ⭐️ no default values
    init(@ViewBuilder groove: () -> S, @ViewBuilder bar: () -> T) {
        self.groove = groove()
        self.bar    = bar()
    }
    
    var body: some View {
        ZStack {
            groove
            bar.padding(p)
        }.frame(height: 80)
    }
}

// content view
struct ContentView: View {
    var body: some View {
        // using MyView
        MyView(groove: { 
            Gradient.down(.gray, .white) // ⭐️ my custom LinearGradient extension
        }, bar: { 
            Gradient.right(.purple, .yellow, .pink) // ⭐️ my custom extension
        })
    }
}

PlaygroundPage.current.setLiveView(ContentView())

(我的自定义Gradient扩展名为here⭐️。)

结果相当不错:

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

代码语言:javascript
复制
import SwiftUI
import PlaygroundSupport

struct MyView<S:View, T:View>: View {
    
    let groove: S
    let bar   : T
    let p     : CGFloat = 10  // padding
    
    // ⭐️ try to give @ViewBuilder closures default values
    init(
        @ViewBuilder 
        groove: () -> S = { Gradient.down(.gray, .white) } as! () -> S, 
        @ViewBuilder 
        bar: () -> T = { Gradient.right(.purple, .yellow, .pink) } as! () -> T
    ) {
        self.groove = groove()
        self.bar = bar()
    }
    
    var body: some View {
        ZStack {
            groove
            bar.padding(p)
        }.frame(height: 80)
    }
}

struct ContentView: View {
    var body: some View {
        VStack {
            // ❌ can't infer `T`
            MyView(groove: { 
                Gradient.down(.gray, .white)
            })
        }
    }
}

PlaygroundPage.current.setLiveView(ContentView())

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

-编辑

我已经为我的问题做了一些努力,到目前为止我得到了以下结果:

代码语言:javascript
复制
import SwiftUI
import PlaygroundSupport

// default values for @ViewBuilder parameters
@ViewBuilder var defaultGroove: some View {
    Gradient.down(.gray, .white)
}

@ViewBuilder var defaultBar: some View {
    Gradient.right(.purple, .yellow, .pink)
}

// MyView
struct MyView<S:View, T:View>: View {
    
    let groove: S
    let bar   : T
    
    // ⭐️ try to give @ViewBuilder parameters default value
    init(
        @ViewBuilder groove: () -> S = { defaultGroove as! S }, 
        @ViewBuilder bar   : () -> T = { defaultBar as! T }
    ) {
        self.groove = groove()
        self.bar = bar()
    }
    
    var body: some View {
        ZStack {
            groove
            bar.padding(10)
        }.frame(height: 80)
    }
}

// Content View
struct ContentView: View {
    var body: some View {
        VStack {
            
            // `bar` omitted
            MyView<LinearGradient, LinearGradient>(groove: { 
                Gradient.bottomRight(.white, .gray, .white)
            })
            // `groove` omitted
            MyView<LinearGradient, Color>(bar: { 
                Color.pink
            }) 
            // both omitted
            MyView<LinearGradient, LinearGradient>()
            
            // ❌ can't infer `S`
            // ⭐️ it would be perfect if `S` can be inferred.
//              MyView(bar: { 
//                  Gradient.right(.purple, .white)
//              })
        }
    }
}

PlaygroundPage.current.setLiveView(ContentView())

结果是:

如果这两个参数都能自动推断出来,那就太好了。

-再次编辑

根据@Asperi的previous advise,我已经做了第三次尝试:

代码语言:javascript
复制
import SwiftUI
import PlaygroundSupport

// default values for @ViewBuilder parameters

@ViewBuilder var defaultGroove: some View {
    Gradient.down(.gray, .white)
}

@ViewBuilder var defaultBar: some View {
    Gradient.right(.purple, .yellow, .pink)
}

// MyView
struct MyView<S:View, T:View>: View {
    
    let groove: S
    let bar   : T
    
    // default value for @ViewBuilder parameters
    init(
        @ViewBuilder groove: () -> S = { defaultGroove as! S }, 
        @ViewBuilder bar: () -> T = { defaultBar as! T }
    ) {
        self.groove = groove()
        self.bar = bar()
    }
    
    var body: some View {
        ZStack {
            groove
            bar.padding(10)
        }.frame(height: 80)
    }
}

// ⭐️ conditional extensions for convenience inits

extension MyView where T == LinearGradient {
    /// MyView(groove:)
    init(@ViewBuilder groove: () -> S){
        self.init(
            groove: groove, 
            bar   : { defaultBar as! T }
        )
    }
}

extension MyView where S == LinearGradient {
    /// MyView(bar:)
    init(@ViewBuilder bar: () -> T){
        self.init(
            groove: { defaultGroove as! S }, 
            bar   : bar
        )
    }
}

extension MyView where S == LinearGradient, T == LinearGradient {
    /// MyView()
    init(){
        self.init(
            groove: { defaultGroove as! S }, 
            bar   : { defaultBar    as! T }
        )
    }
}

// Content View
struct ContentView: View {
    var body: some View {
        VStack {
            
            // ⭐️ `S`, `T` are both inferred in the following cases
            
            MyView(groove: { 
                Gradient.bottomRight(.white, .yellow, .green)
            }, bar: {
                Color(white: 0.8)
                    .shadow(color: .black, radius: 3, x: 3, y: 3)
                    .shadow(color: .white, radius: 3, x: -3, y: -3)
            })
            
            // `bar` omitted
            MyView(groove: { 
                Gradient.right(.red, .purple)
            })
            
            // `groove` omitted
            MyView(bar: { 
                Gradient.right(.purple, .white)
                    .shadow(color: .black, radius: 3, x: 0, y: 2)
            })
            
            // `groove`, `bar` both omitted 
            MyView()
        }
    }
}

PlaygroundPage.current.setLiveView(ContentView())

结果是:

我已经为那些方便的intializer实现了所有需要的扩展,它是有效的,但如果我们能找到一种方法在第一时间避免这些扩展,那就太好了。

有可能吗?

EN

回答 1

Stack Overflow用户

发布于 2020-09-24 22:27:16

这是一个可能的方法的演示(你可以替换你的Gradient类型)-你需要用默认的初始化来扩展特殊的类型。

使用Xcode12/ iOS 14进行了测试

代码语言:javascript
复制
extension MyView where S == Text, T == Button<Text> {
    init() {
        self.init(groove: { Text("Hello") },
            bar: { Button("World", action: { }) })
    }
}

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

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64047904

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档