处理 SwiftUI 视图中的可选值/nil 值 - 在 SwiftUI 中使用 if let

Ral*_*ert 4 swiftui

视图通常不允许可选参数值,从而导致如下错误Initializer 'init(_:)' requires that 'String?' conform to 'StringProtocol'

struct Person {
    var name : String
}

struct OptionalsExampleView: View {

    var person : Person? = Person(name: "Bob")

    var body: some View {
        VStack() {
            Text("Name:")
            Text(person?.name)
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,从 Xcode 11.4/iOS 13 开始,if letView Builder 块中不允许使用该语句,从而导致出现如下错误Closure containing control flow statement cannot be used with function builder 'ViewBuilder'

struct OptionalsExampleView: View {

    var person : Person? = Person(name: "Bob")

    var body: some View {
        VStack() {
            if let person = person { // <-- not allowed
                Text("Name:")
                Text(person?.name)
            }
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

(我知道这个问题已经被回答和问过很多次了。我自己写了这个问题并回答了它,以便有一篇比现有文章更简洁的简短概述文章,以便我可以在出现这个问题的地方链接到它向上)。

Ral*_*ert 6

a) 在非常简单的情况下,使用可选的后备或带有强制展开的 if 检查(if在 View Builder 块中允许):

struct OptionalsExampleView: View {

    var person : Person? = Person(name: "Bob")

    var body: some View {
        VStack() {
            Text(person?.name ?? "")
            if person != nil {
                Text("Name:")
                Text(person!.name)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

b)Optional#map如果存在单个可选值,则用于显示单个视图:

struct OptionalsMapExampleView: View {

    var person : Person? = Person(name: "Bob")

    var body: some View {
        VStack() {
            person.map { person in
                VStack {
                    Text("Name:")
                    Text(person.name)
                }
            }
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

c)if let使用显式返回类型将块包装在组视图中。if let如果它是视图生成器块中的唯一语句,则此处允许。如果块可以返回不同的视图类型,则视图需要包装为AnyView

struct OptionalsGroupExampleView: View {

    var person: Person? = Person(name: "Bob")

    var body: some View {
        VStack {
            Group { () -> AnyView in
                if let person = person {
                    return AnyView(VStack {
                        Text("Name:")
                        Text(person.name)
                    })
                } else {
                    return AnyView(EmptyView())
                }
            }
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

d) 将逻辑提取到一个单独的函数中 -if let此处允许,因为它位于视图构建器块之外。如果函数可以返回不同的视图类型,则需要将视图包装为AnyView

struct OptionalsFuncExampleView: View {

    var person : Person? = Person(name: "Bob")

    var body: some View {
        VStack() {
            personView()
        }
    }

    func personView() -> some View {
        if let person = person {
            return AnyView(
                VStack {
                    Text("Name:")
                    Text(person.name)
                }
            )
        } else {
            return AnyView(EmptyView())
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

e) 使用封装 if-let 条件的辅助 View 类型,示例实现:IfLet/ OptionalView;也可以作为函数使用ifLet