SwiftUI:可重用 Cross-Platform(iOS 和 macOS)视图中的导航栏标题

SwiftUI: Navigation Bar Title in Reusable Cross-Platform (iOS & macOS) View

我正在尝试为框架创建可重用的 SwiftUI View,然后可以在 iOS/iPadOS 和 macOS 上使用它。

这通常可以正常工作;但是,由于 macOS 视图没有导航栏,因此当视图包含在 macOS 目标中时,包括导航栏标题(对 iOS 很重要)会导致错误:

.navigationBarTitle(Text("Page Title"))

Value of type '...' has no member 'navigationBarTitle'

对于允许为两个平台构建相同视图的条件编译(或任何其他)方法有什么建议吗?

我最接近的如下。包括额外的 Text 视图很容易,但是因为栏标题是修饰符,在条件编译中仅包装该部分会产生其他错误:

public struct ExampleView: View {

    private let pageTitle = "Page Title"
    
    public var body: some View {

        VStack(alignment: .center) {
            
            #if os(macOS)
            Text(pageTitle)
                .font(.title)
            #endif

            Spacer()           
            Text("This is the view content")
            Spacer()
        }

        #if !os(macOS)
        .navigationBarTitle(Text(pageTitle))
        #endif
    }
}

Unexpected platform condition (expected 'os', 'arch', or 'swift')

Function declares an opaque return type, but has no return statements in its body from which to infer an underlying type

我建立了一种方法:将主要内容移动到另一个 View 属性,然后修改它以添加导航栏标题(如果需要),然后将其作为 body 返回。这样分开的时候,就可以使用条件编译了。

这有点不雅,但很管用。它还可以用于设置 macOS-specific 东西,例如视图的整体框架大小。欢迎就更好的方法提出建议!

Swift v5.1

public struct ExampleView: View {

    private let pageTitle = "Page Title"

    #if !os(macOS)
    public var body: some View {
        main.navigationBarTitle(Text(pageTitle))
    }
    #else
    public var body: some View {
        main.frame(
            minWidth: 500,
            minHeight: 500
        )
    }
    #endif
    public var main: some View {

        VStack(alignment: .center) {

            #if os(macOS)
            Text(pageTitle)
                .font(.title)
            #endif

            Spacer()           
            Text("This is the view content")
            Spacer()
        }
    }
}

您可以向视图结构添加扩展。它会将条件编译放在块之外并按您预期的方式工作

#if os(macOS)
extension View {
    func navigationBarTitle(_ title: Text) -> some View {
        return self
    }
}
#endif