SwiftUI - if let 有条件闭包的替代方案

Posted

技术标签:

【中文标题】SwiftUI - if let 有条件闭包的替代方案【英文标题】:SwiftUI - alternative to if let with a conditional closure 【发布时间】: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,但不敢相信解包选项令人头疼!

【问题讨论】:

【参考方案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 的不足。

以上是关于SwiftUI - if let 有条件闭包的替代方案的主要内容,如果未能解决你的问题,请参考以下文章

let块级引起的闭包思考

无法推断复杂的闭包返回类型 SwiftUI ForEach

Vue语法

来自 watchOS 中 if 条件 Text() 的 SwiftUI 运行时错误

SwiftUI 闭包更新 @State init

条件绑定:if let error - 条件绑定的初始化程序必须具有可选类型