在View SwiftUI中展开可选

时间:2019-10-13 22:06:34

标签: swift swiftui

我尝试解开我的可选属性,但收到此错误消息:包含控件流语句的闭包不能与函数生成器“ ViewBuilder”一起使用

我看不到代码有什么问题

代码:

HStack {
            if let height = profile.height {
                TagBox(field: "height", value: String(height))
            }
            TagBox(field: "nationality", value: profile.nationality)
            Spacer()
        }.padding(.horizontal)

4 个答案:

答案 0 :(得分:3)

在这种情况下,有两种使用可选方法的方法:

第一个,如果您不希望此视图在profile.height为nil时完全显示:

profile.height.map({ TagBox(field: "height", value: String($0))})

第二个,如果要显示此视图,但使用默认值:

TagBox(field: "height", value: String(profile.height ?? 0))

答案 1 :(得分:1)

Swift 5.3-Xcode 12

现在在ViewBuilder中使用条件绑定非常好:

HStack {
    if let height = profile.height { // <- This works now in Xcode 12
        TagBox(field: "height", value: String(height))
    }
    TagBox(field: "nationality", value: profile.nationality)
    Spacer()
}.padding(.horizontal)

答案 2 :(得分:0)

您可以在Group的视图构建器中使用if语句。我举了一个例子来说明:

var label1 = "label 1"
@State var label2: String?
var label3: String? = "label 3"

var body: some View {
    VStack {
        Text(label1)
        Group {
            if label2 != nil {
                Text(label2!)
            }
            if label3 != nil {
                Text(label3!)
            }
        }
        Button(action: {
            self.label2 = "Button pressed!"
        }) {
            Text("Press me!")
        }
    }
}

当然,在此示例中,您可以执行Text(label2 ?? ""),但是在Text拥有值之前,label2会有一个空白,而且更复杂在这种情况下,可以提供更大的灵活性。如果运行该代码,则将在“标签3”的正上方看到“标签1”,如果您按下按钮,则“按下按钮!”。将出现在“标签1”和“标签3”之间。

基本上,如果label2为nil,则当标签具有值时,将没有空格,而是没有间隙,并且当label2被分配了值,应该在其之上和之下的所有内容都会发生变化以适应它。

在您提供的代码中,您可以将其更改为:

HStack {
    Group {
        if profile.height != nil {
            TagBox(field: "height", value: String(profile.height!))
        }
    }
    TagBox(field: "nationality", value: profile.nationality)
    Spacer()
}.padding(.horizontal)

此解决方案的好处是,您可以在值为nil(或不为nil)的情况下做更复杂的事情,甚至在值为nil的情况下甚至提供默认视图(在我的示例中,这是等同于添加一个else if内的ButtonText等带有elseViewBuilder等的语句。

一个缺点是您必须显式地解开可选对象,这不应该成为问题,因为if语句可以确保它不是nil,但是我仍然发现可选绑定通常是一种更干净的解包途径。

修改

如果您确实不想显式拆开可选选项,则可以使用var label1 = "label 1" @State var label2: String? var label3: String? = "label 3" var body: some View { VStack { Text(label1) Group { self.conditionalText(self.label2) if label3 != nil { Text(label3!) } } Button(action: { self.label2 = "Button pressed!" }) { Text("Press me!") } } } func conditionalText(_ text: String?) -> some View { if let text = text { return ViewBuilder.buildIf(Text(text)) } else { print("Text is nil") } let view: Text? = nil return ViewBuilder.buildIf(view) } s:

Group

此方法还着重说明了{{1}}内的可执行代码,只要该代码位于返回视图的函数中即可。

答案 3 :(得分:0)

因此,如果用力拉开会使您感到不舒服,则可以使用我创建的以下自定义视图:

struct OptionalView<Value, Content>: View where Content: View {
    var content: (Value) -> Content
    var value: Value

    init?(_ value: Value?, @ViewBuilder content: @escaping (Value) -> Content) {
        guard let value = value else {
            return nil
        }
        self.value = value

        self.content = content
    }

    var body: some View {
        content(value)
    }
}

并像这样使用它:

OptionalView(profile.height) { height in
    TagBox(field: "height", value: String(height))
}

我写了一篇有关此问题的文章,并提出了一系列可选视图here