单个视图的 macOS SwiftUI 导航

Hus*_*r28 16 navigation macos swiftui macos-catalina swiftui-navigationview

我正在尝试为我的 macOS SwiftUI 状态栏应用程序创建一个设置视图。到目前为止,我的实现一直使用NavigationView, 和NavigationLink,但是当设置视图将父视图推到一边时,这个解决方案会产生一个半视图。下面的屏幕截图和代码示例。

导航边栏

在此处输入图片说明

struct ContentView: View {
    var body: some View {
        VStack{
            NavigationView{
            NavigationLink(destination: SecondView()){
                Text("Go to next view")
                }}
        }.frame(width: 800, height: 600, alignment: .center)}
}

struct SecondView: View {
    var body: some View {
        VStack{

                Text("This is the second view")

        }.frame(width: 800, height: 600, alignment: .center)
    }
}
Run Code Online (Sandbox Code Playgroud)

我能找到的少量信息表明,在 macOS 上使用 SwiftUI 是不可避免的,因为NavigationViewiOS ( StackNavigationViewStyle)上的“全屏”在 macOS 上不可用。

在 SwiftUI for macOS 中,是否有一种简单甚至复杂的方法来实现到设置视图的转换,该视图占据整个框架?如果没有,是否可以使用AppKit用来调用用 SwiftUI 编写的 View 对象?

也是一个 Swift 新手 - 请保持温柔。

Asp*_*eri 12

这是自定义导航类解决方案的可能方法的简单演示。使用 Xcode 11.4 / macOS 10.15.4 测试

演示

注意:背景颜色用于更好的可见性。

struct ContentView: View {
    @State private var show = false
    var body: some View {
        VStack{
            if !show {
                RootView(show: $show)
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                    .background(Color.blue)
                    .transition(AnyTransition.move(edge: .leading)).animation(.default)
            }
            if show {
                NextView(show: $show)
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                    .background(Color.green)
                    .transition(AnyTransition.move(edge: .trailing)).animation(.default)
            }
        }
    }
}

struct RootView: View {
    @Binding var show: Bool
    var body: some View {
        VStack{
            Button("Next") { self.show = true }
            Text("This is the first view")
        }
    }
}

struct NextView: View {
    @Binding var show: Bool
    var body: some View {
        VStack{
            Button("Back") { self.show = false }
            Text("This is the second view")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


The*_*eil 12

我扩展了Asperi的伟大建议,并StackNavigationView为 macOS(甚至 iOS,如果你愿意的话)创建了一个通用的、可重用的。一些亮点:

\n
    \n
  • 它支持任意数量的子视图(在任何布局中)。
  • \n
  • 它会自动为每个子视图添加一个“后退”按钮(目前只是文本,但如果使用 macOS 11+,您可以交换图标)。
  • \n
\n

macOS 上的示例视图

\n

斯威夫特 v5.2:

\n
struct StackNavigationView<RootContent, SubviewContent>: View where RootContent: View, SubviewContent: View {\n    \n    @Binding var currentSubviewIndex: Int\n    @Binding var showingSubview: Bool\n    let subviewByIndex: (Int) -> SubviewContent\n    let rootView: () -> RootContent\n    \n    var body: some View {\n        VStack {\n            VStack{\n                if !showingSubview { // Root view\n                    rootView()\n                        .frame(maxWidth: .infinity, maxHeight: .infinity)\n                        .transition(AnyTransition.move(edge: .leading)).animation(.default)\n                }\n                if showingSubview { // Correct subview for current index\n                    StackNavigationSubview(isVisible: self.$showingSubview) {\n                        self.subviewByIndex(self.currentSubviewIndex)\n                    }\n                    .frame(maxWidth: .infinity, maxHeight: .infinity)\n                    .transition(AnyTransition.move(edge: .trailing)).animation(.default)\n                }\n            }\n        }\n    }\n    \n    init(currentSubviewIndex: Binding<Int>, showingSubview: Binding<Bool>, @ViewBuilder subviewByIndex: @escaping (Int) -> SubviewContent, @ViewBuilder rootView: @escaping () -> RootContent) {\n        self._currentSubviewIndex = currentSubviewIndex\n        self._showingSubview = showingSubview\n        self.subviewByIndex = subviewByIndex\n        self.rootView = rootView\n    }\n    \n    private struct StackNavigationSubview<Content>: View where Content: View {\n        \n        @Binding var isVisible: Bool\n        let contentView: () -> Content\n        \n        var body: some View {\n            VStack {\n                HStack { // Back button\n                    Button(action: {\n                        self.isVisible = false\n                    }) {\n                        Text("< Back")\n                    }.buttonStyle(BorderlessButtonStyle())\n                    Spacer()\n                }\n                .padding(.horizontal).padding(.vertical, 4)\n                contentView() // Main view content\n            }\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

有关所用泛型的更多信息可以在此处@ViewBuilder找到。

\n

这是它的基本使用示例。父视图跟踪当前选择和显示状态(使用@State),允许其子视图内的任何内容触发状态更改。

\n
struct ExampleView: View {\n    \n    @State private var currentSubviewIndex = 0\n    @State private var showingSubview = false\n    \n    var body: some View {\n        StackNavigationView(\n            currentSubviewIndex: self.$currentSubviewIndex,\n            showingSubview: self.$showingSubview,\n            subviewByIndex: { index in\n                self.subView(forIndex: index)\n            }\n        ) {\n            VStack {\n                Button(action: { self.showSubview(withIndex: 0) }) {\n                    Text("Show View 1")\n                }\n                Button(action: { self.showSubview(withIndex: 1) }) {\n                    Text("Show View 2")\n                }\n                Button(action: { self.showSubview(withIndex: 2) }) {\n                    Text("Show View 3")\n                }\n            }\n            .frame(maxWidth: .infinity, maxHeight: .infinity)\n            .background(Color.blue)\n        }\n    }\n    \n    private func subView(forIndex index: Int) -> AnyView {\n        switch index {\n        case 0: return AnyView(Text("I\'m View One").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.green))\n        case 1: return AnyView(Text("I\'m View Two").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.yellow))\n        case 2: return AnyView(VStack {\n            Text("And I\'m...")\n            Text("View Three")\n        }.frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.orange))\n        default: return AnyView(Text("Inavlid Selection").frame(maxWidth: .infinity, maxHeight: .infinity).background(Color.red))\n        }\n    }\n    \n    private func showSubview(withIndex index: Int) {\n        currentSubviewIndex = index\n        showingSubview = true\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

注意:像这样的泛型要求所有子视图都属于同一类型。如果不是这样,您可以将它们包装在 中AnyView,就像我在这里所做的那样。AnyView如果您对所有子视图使用一致的类型,则不需要包装器(根视图\xe2\x80\x99s 类型不需要匹配\xe2\x80\x99s 类型)。

\n