【问题标题】:How to treat if-let-else as a single view in SwiftUI?如何在 SwiftUI 中将 if-let-else 视为单个视图?
【发布时间】:2022-06-23 12:35:55
【问题描述】:

我正在尝试编写一个扩展程序,在其下方添加一个标题(我称之为statusBar),无论标题是什么类型(例如文本、图像、链接......)。所以我尝试了下面的代码。但是 Xcode 在.statusBar 之前的那行说Type '()' cannot conform to 'View' 的错误。您可以在下面的代码中找到我添加的注释。

我知道我的.statusBar 中一定有问题,因为当我用单个视图替换 if-let-else 块时错误消失了(例如 Text("Hello, world!"))。但我仍然想根据那个 if-let 语句显示不同的内容。那么我该如何使用我的代码来解决这个问题呢?


// .statusBar extension

struct StatusBarView: ViewModifier {
    
    let statusBar: AnyView
    
    init<V: View>(statusBar: () -> V) {
        self.statusBar = AnyView(statusBar())
    }
    
    func body(content: Content) -> some View {
        VStack(spacing: 0) {
            content
            statusBar
        }
    }
}

extension View {
    func statusBar<V: View>(statusBar: () -> V) -> some View {
        self.modifier(StatusBarView(statusBar: statusBar))
    }
}

// Inside main app view

Image(systemName: "link")
    .font(.system(size: 48))      // Xcode error: Type '()' cannot conform to 'View'
    .statusBar {
        //
        // If I change below if-let-else to a single view
        // (e.g. Text("Hello, world!"))
        // Then it works.
        //
        if let url = mediaManager.url {
            Text(url.path)
        } else {
            Text("No media loaded.")
        }
    }

【问题讨论】:

    标签: swift swiftui


    【解决方案1】:

    使其闭包参数成为视图构建器,例如

    extension View {
        func statusBar<V: View>(@ViewBuilder statusBar: () -> V) -> some View {
            self.modifier(StatusBarView(statusBar: statusBar))
        }
    }
    

    在修饰符的 init 中也可以这样做,但在这种情况下不需要特别使用。

    使用 Xcode 13.4 / iOS 15.5 测试

    【讨论】:

      【解决方案2】:

      将 if-else 包装到 Group 中。示例:

      Image(systemName: "link")
      .font(.system(size: 48))
      .statusBar {
           Group {
               if let url = mediaManager.url {
                   Text(url.path)
               } else {
                   Text("No media loaded.")
               }
           }
      }
      

      【讨论】:

        【解决方案3】:

        你也可以这样做:

            Image(systemName: "link")
                .font(.system(size: 48))
                .statusBar {
                    
                    ZStack {
                        if let url = mediaManager.url {
                            Text(url.path)
                        } else {
                            Text("No media loaded.")
                        }
                        
                    }
                }
        

        【讨论】:

        • 确实如此。每个包装器都有效,但它们可能违反直觉并使代码变得多余。我喜欢 Asperi 使用 ViewBuilder 的方法。
        • 是的,这是最好的解决方案,我的意思是提供另一种选择。
        猜你喜欢
        • 1970-01-01
        • 2019-10-12
        • 2020-10-03
        • 2019-10-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-07
        • 1970-01-01
        相关资源
        最近更新 更多