在 macOS 上的 SwiftUI NavigationView 中切换侧边栏
Toggle Sidebar in SwiftUI NavigationView on macOS
我有一个带 NavigationView
的 macOS 应用程序,我想在 window 的工具栏中使用默认的 ToggleSidebar 项目。
目前我在 NSToolbarDelegate
的 toolbarWillAddItem(_)
中将 ToolbarItem 的目标设置为 AppDelegate。
我实现的 AppDelegate 内部
@objc func toggleSidebar(_ sender: Any) {
((window.contentView?.subviews.first?.subviews.first?.subviews.first as? NSSplitView)?.delegate as? NSSplitViewController)?.toggleSidebar(self)
}
此解决方案目前有效。如果 SwiftUI 的实现会改变这个中断。
那么如何才能更好地做到这一点?
我不为此使用 NavigationView。改用 HSplitView 并使用
中的隐藏标志
在 SwiftUI 2.0 中,您可以使用 NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
和这样的工具栏按钮:
.toolbar {
ToolbarItem(placement: .navigation) {
#if os(macOS)
Button(action: toggleSidebar, label: {
Image(systemName: "sidebar.left")
})
#endif
}
}
func toggleSidebar() {
#if os(macOS)
NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
#endif
}
从 macOS Big Sur beta 4 开始,您可以使用 SwiftUI 2.0 添加默认侧边栏命令。
var body: some Scene {
WindowGroup {
NavigationView {
Group {
SidebarView()
ContentView()
}
}
}
.commands {
SidebarCommands()
}
}
此代码将添加“切换边栏”快捷方式:
侧边栏查看代码:
var body: some View {
List {
ForEach(0..<5) { index in
Text("\(index)")
}
}
.listStyle(SidebarListStyle())
}
虽然您可以尝试在 keyWindow?.contentViewController
或 keyWindow?.firstResponder?
上执行 #selector(NSSplitViewController.toggleSidebar(_:))
,但在某些情况下似乎无法始终如一地工作。
相反,您可以使用此命令:
NSApp.sendAction(#selector(NSSplitViewController.toggleSidebar(_:)), to: nil, from: nil)
它将 toggleSidebar
选择器发送到第一个可以对其做出反应的对象,这意味着当前 window 中唯一的侧边栏。这种行为有更好的记录 on Apple's documentation website.
此方法是 SidebarCommands()
菜单项使用的默认实现。这是通过添加 Toggle Sidebar 菜单项找到的,然后像这样获取它的选择器:
let menu = NSApp.mainMenu!.items.first(where: { [=11=].title == "View" })!
let submenu = menu.submenu!.items.first(where: { [=11=].title == "Toggle Sidebar" })!
submenu.target // nil
submenu.action // #selector(toggleSidebar:)
这意味着它很可能比其他方法更一致(和受支持)。
我有一个带 NavigationView
的 macOS 应用程序,我想在 window 的工具栏中使用默认的 ToggleSidebar 项目。
目前我在 NSToolbarDelegate
的 toolbarWillAddItem(_)
中将 ToolbarItem 的目标设置为 AppDelegate。
我实现的 AppDelegate 内部
@objc func toggleSidebar(_ sender: Any) {
((window.contentView?.subviews.first?.subviews.first?.subviews.first as? NSSplitView)?.delegate as? NSSplitViewController)?.toggleSidebar(self)
}
此解决方案目前有效。如果 SwiftUI 的实现会改变这个中断。
那么如何才能更好地做到这一点?
我不为此使用 NavigationView。改用 HSplitView 并使用
在 SwiftUI 2.0 中,您可以使用 NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
和这样的工具栏按钮:
.toolbar {
ToolbarItem(placement: .navigation) {
#if os(macOS)
Button(action: toggleSidebar, label: {
Image(systemName: "sidebar.left")
})
#endif
}
}
func toggleSidebar() {
#if os(macOS)
NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
#endif
}
从 macOS Big Sur beta 4 开始,您可以使用 SwiftUI 2.0 添加默认侧边栏命令。
var body: some Scene {
WindowGroup {
NavigationView {
Group {
SidebarView()
ContentView()
}
}
}
.commands {
SidebarCommands()
}
}
此代码将添加“切换边栏”快捷方式:
侧边栏查看代码:
var body: some View {
List {
ForEach(0..<5) { index in
Text("\(index)")
}
}
.listStyle(SidebarListStyle())
}
虽然您可以尝试在 keyWindow?.contentViewController
或 keyWindow?.firstResponder?
上执行 #selector(NSSplitViewController.toggleSidebar(_:))
,但在某些情况下似乎无法始终如一地工作。
相反,您可以使用此命令:
NSApp.sendAction(#selector(NSSplitViewController.toggleSidebar(_:)), to: nil, from: nil)
它将 toggleSidebar
选择器发送到第一个可以对其做出反应的对象,这意味着当前 window 中唯一的侧边栏。这种行为有更好的记录 on Apple's documentation website.
此方法是 SidebarCommands()
菜单项使用的默认实现。这是通过添加 Toggle Sidebar 菜单项找到的,然后像这样获取它的选择器:
let menu = NSApp.mainMenu!.items.first(where: { [=11=].title == "View" })!
let submenu = menu.submenu!.items.first(where: { [=11=].title == "Toggle Sidebar" })!
submenu.target // nil
submenu.action // #selector(toggleSidebar:)
这意味着它很可能比其他方法更一致(和受支持)。