【问题标题】:SwiftUI - alternative to if let with a conditional closureSwiftUI - if let 有条件闭包的替代方案
【发布时间】:2019-12-28 06:49:05
【问题描述】:

我正在尝试在 SwiftUI 中实现以下功能:

struct PersonView: View {

    @State private var age: Int? = 0

    var body: some View {
        VStack {
            Text("Just a test")
            if let self.age > 0 {
                Text("Display Age: \(age)")
            } else {
                Text("Age must be greater than 0!")
            }
        }
    }
}

但是,在 SwiftUI 中,if let 会导致以下错误:

包含控制流语句的闭包不能与函数生成器“ViewBuilder”一起使用

所以在研究了这个主题之后,我发现了一个建议,即使用 .map 来解开 age 可选。因此,我修改为VStack 中的代码如下:

Text("Just a test")
self.age.map {elem in
    if elem > 0 {
        Text("Display Age: \(elem)")
    } else {
        Text("Age must be greater than 0!")
    }
}

但是,在 .map 闭包中包含条件会导致调用 VStack 的行出现以下错误:

' (ViewBuilder.Type) -> (C0, C1) -> TupleView' 要求 '()' 符合 'View'

类型'()'不符合协议'View'

关于如何克服第二组错误有什么建议吗?或者,是否有另一种方法可以在 SwiftUI 中展开选项并评估它们?真的很喜欢 SwiftUI,但不敢相信解包选项令人头疼!

【问题讨论】:

    标签: view closures conditional-statements swiftui optional


    【解决方案1】:

    对于这种情况,我更喜欢以下方法

    struct PersonView: View {
    
        @State private var age: Int? = 0
    
        var body: some View {
            VStack {
                Text("Just a test")
                AgeText
            }
        }
    
        private var AgeText: some View {
            if let age = self.age, age > 0 {
                return Text("Display Age: \(age)")
            } else {
                return Text("Age must be greater than 0!")
            }
        }
    }
    

    【讨论】:

    • 请注意,如果您想在if let 条件失败的情况下尝试提供EmptyView(),则此方法不起作用。因此,@LuLuGaGa 建议使用 .map 可能更可取
    • 我很好奇为什么初始版本不起作用以及这实际上是如何解决问题的?似乎 (jasonzurita.com/swiftui-if-statement) 涵盖了一些主题,但它相当复杂。
    【解决方案2】:

    Swift 5.3 (Xcode 12)

    现在您可以在视图构建器中使用条件绑定了:

    if let age = age {
        if age > 0 {
            Text("Display Age: \(age)")
        } else {
            Text("Age must be greater than 0!")
        }
    } else {
        Text("Age not found")
    }
    

    重构(也适用于较旧的 Swift)

    您可以将代码重构为更基本的东西,例如使用函数:

    var body: some View {
        VStack {
            Text("Just a test")
            Text(text(age: age)) // Using the function
        }
    }
    
    func text(age: Int?) -> String { // Defining the function
        guard let age = age else { return "Age not found" }
        if age > 0 { return "Display Age: \(age)" }
        else { return "Age must be greater than 0!" }
    }
    

    一般来说,在需要清理代码的地方使用函数。我希望未来的 Swift 版本能够像我们期望的那样直接支持这一点。

    【讨论】:

    • 感谢 @Mojtaba 的重构。除了克服不支持if letguard let 的SwiftUI 缺点之外,绝对组织得更好。最后,我接受了 Asperi 的解决方案,因为它也重构了 View 对象。
    【解决方案3】:

    您尝试对 age 的值进行两次检查:首先确保它不是 nil,然后检查它是否大于 0。 您可以使用 map 去除潜在的nil,然后使用三元运算符有条件地更改显示的文本:

    var body: some View {
        VStack {
            Text("Just a test")
            age.map { Text( $0 > 0 ? "Display Age: \($0)" : "Age must be greater than 0!") }
        }
    }
    

    【讨论】:

    • 谢谢@LuLuGaGa!使用三元运算符帮助我解决了这个问题。我最终接受了 Asperi 的解决方案,因为它是一种更通用的方法,可以更好地组织代码,并且可以克服 SwiftUI 的 noet 支持 if let 的不足。
    猜你喜欢
    • 2021-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多