【问题标题】:macOS SwiftUI App Tab View with Segmented Control in Toolbar工具栏中带有分段控件的 macOS SwiftUI 应用程序选项卡视图
【发布时间】:2020-02-09 12:40:24
【问题描述】:

我正在尝试使用 SwiftUI 创建一个 macOS 应用程序。我需要 TabView 或类似的东西,但是当我使用 TabView 时,分段控件不在 macOS 工具栏中。 Click here to see an example of what I would like

我当前的代码是:

import SwiftUI

struct ContentView: View {
    var body: some View {
        TabView {
            Text("1")
                .tabItem {
                    Text("1")
            }
        }
    }
}

The result is here as an image

分段控件需要在工具栏而不是视图中。

谢谢。

【问题讨论】:

    标签: swift xcode macos swiftui


    【解决方案1】:

    这是实现此目的的可能方法的简化演示。经过测试并与 Xcode 11.2 一起使用。

    1) 为AppDelegate中的窗口准备所需的样式和背景

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Create the SwiftUI view that provides the window contents.
        let contentView = ContentView()
            .edgesIgnoringSafeArea(.top)
            .frame(minWidth: 480, maxWidth: .infinity, minHeight: 300, maxHeight: .infinity)
    
        // Create the window and set the content view. 
        window = NSWindow(
            contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
            styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
            backing: .buffered, defer: false)
        window.center()
        window.titlebarAppearsTransparent = true
        window.titleVisibility = .hidden
    
        window.setFrameAutosaveName("Main Window")
        window.contentView = NSHostingView(rootView: contentView)
        window.makeKeyAndOrderFront(nil)
    }
    

    2) 准备窗口内容视图以具有所需的行为

    struct ContentView: View {
        private let tabs = ["Watch Now", "Movies", "TV Shows", "Kids", "Library"]
        @State private var selectedTab = 0
        var body: some View {
            VStack {
                HStack {
                    Spacer()
                    Picker("", selection: $selectedTab) {
                        ForEach(tabs.indices) { i in
                            Text(self.tabs[i]).tag(i)
                        }
                    }
                    .pickerStyle(SegmentedPickerStyle())
                    .padding(.top, 8)
                    Spacer()
                }
                .padding(.horizontal, 100)
                Divider()
                GeometryReader { gp in
                    VStack {
                        ChildTabView(title: self.tabs[self.selectedTab], index: self.selectedTab)
                    }
                }
            }
        }
    }
    
    struct ChildTabView: View {
        var title: String
        var index: Int
    
        var body: some View {
            Text("\(title)")
        }
    }
    

    【讨论】:

    • 感谢您的回答。我真的在寻找类似于 Apple 应用程序的东西,即使在 AppKit 中也是如此。不过,感谢您的回复。谢谢
    • 我相信 Apple 在 WWDC 2019 上展示了一款 SwiftUI Mac 应用,工具栏中带有分段控件。
    【解决方案2】:

    当我想在 macOS BigSur 上构建类似的东西时,我偶然发现了你的问题。我正在使用 Xcode 12.2

    这是我的解决方案受 Asperi 的回答启发的样子。将窗口组的标题设置为空字符串“”很重要,否则看起来很奇怪。

    请注意,它仅在您运行应用程序时有效,而不是在预览中!

    应用文件

    import SwiftUI
    
    @main
    struct SegmentedToolbarApp: App {
        var body: some Scene {
            WindowGroup("") {
                ToolbarItemPlacement()
            }
        }
    }
    

    ToolbarItemPlacement 视图

    重要的部分是principal的展示位置。

    设置更大的 minWidth 也很重要 - 否则工具栏会消失!

    import SwiftUI
    
    struct ToolbarItemPlacement: View {
        
        private let tabs = ["Watch Now", "Movies", "TV Shows", "Kids", "Library"]
        @State private var selectedTab = 0
        
        var body: some View {
            VStack {
                ChildTabView(title: self.tabs[self.selectedTab], index: self.selectedTab)
            }
            .toolbar {
                ToolbarItem(placement: .principal) {
                    
                    Picker("", selection: $selectedTab) {
                        ForEach(tabs.indices) { i in
                            Text(self.tabs[i]).tag(i)
                        }
                    }
                    .pickerStyle(SegmentedPickerStyle())
                    .padding(.top, 8)
                }
            }
            .frame(minWidth: 800, minHeight: 400)
        }
    }
    

    ChildTabView

    struct ChildTabView: View {
        var title: String
        var index: Int
    
        var body: some View {
            Text("\(title) - Index \(index)")
                .padding()
        }
    }
    

    【讨论】: