Toggle Sidebar in SwiftUI NavigationView on macOS
Since macOS Big Sur beta 4 you can add default sidebar commands with SwiftUI 2.0.
var body: some Scene {
WindowGroup {
NavigationView {
Group {
SidebarView()
ContentView()
}
}
}
.commands {
SidebarCommands()
}
}
This code will add the "Toggle Sidebar" shortcut:
SidebarView code:
var body: some View {
List {
ForEach(0..<5) { index in
Text("\(index)")
}
}
.listStyle(SidebarListStyle())
}
Placing the toggle sidebar button on the sidebar toolbar
try this:
ToolbarItem(placement: .primaryAction) {
SwiftUI: Get sidebar isCollapsed state on macOS
The idea is to find NSSplitViewController
(which is still used internally as engine) and subscribe to observe its first split item (which is our sidebar) isCollapsed
state.
Tested with Xcode 13.3 / macOS 12.2.1
Here is main part:
// find split view through hierarchy
while sview != nil, !sview!.isKind(of: NSSplitView.self) {
sview = sview?.superview
}
guard let sview = sview as? NSSplitView else { return }
controller = sview.delegate as? NSSplitViewController // delegate is our controller
if let sideBar = controller?.splitViewItems.first { // now observe for state
observer = sideBar.observe(\.isCollapsed, options: [.new]) { [weak self] _, change in
if let value = change.newValue {
self?.sideCollapsed?.wrappedValue = value // << here !!
}
}
}
and usage like
SideViewInsideNavigationView()
.background(SplitViewAccessor(sideCollapsed: $collapsed)) // << inject finder !!
Complete findings and code is here
Collapse sidebar in SwiftUI (Xcode 12)
This worked for me -
https://developer.apple.com/forums/thread/651807
struct SwiftUIView: View {
var body: some View {
NavigationView{
}.toolbar {
ToolbarItem(placement: .navigation) {
Button(action: toggleSidebar, label: {
Image(systemName: "sidebar.left")
})
}
}
}
}
func toggleSidebar() {
NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
}
struct SwiftUIView_Previews: PreviewProvider {
static var previews: some View {
SwiftUIView()
}
}
It shows in the views on iOS so you will need some conditions for macOS only.
Sidebar Menu for macOS in SwiftUI
You need to make your enum case-iterable to use it as model in ForEach
, like
enum HostingBarCategories: Hashable, CaseIterable {
case Screen1
case Screen2
case Screen3
case Screen4
case Screen5
var string: String { String(describing: self) }
}
struct macOS_NavigationView: View {
@State private var selectedTab: HostingBarCategories = .Screen1
var body: some View {
NavigationView {
// SideBar Menu
List {
ForEach(HostingBarCategories.allCases, id: \.self) { screen in
NavigationLink(destination:
Text(screen.string)
.frame(maxWidth: .infinity, maxHeight: .infinity)
) {
Text("Link \(screen.string)")
}
}
}
.listStyle(SidebarListStyle())
// Primary View
Text("Select a menu...")
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
Related Topics
Explit Conformance to Codable Removes Memberwise Initializer Generation on Structs
Swift 3: Safe Way to Decode Values with Nscoder
Nsinvocationoperation' Is Unavailable in Xcode 6.1
How to Switch an Xcode Project to Use Swift Version 1.2 in the Xcode 7 Beta
How to Add Detect Button Presses in Tvos
How to Submit Swift 2.2 App with Xcode 7.3 When iOS 10 Is Released
How to Call a Selector-Based Timer Method on a Swift Struct
Read Static Property from Object
How to Cast from Uint16 to Nsnumber
Is There an Kotlin Equivalent 'With' Function in Swift
Using @Discardableresult for Closures in Swift
Classes in Swift Files Inside Folder References Not Seen by Xcode 10's Compiler
Change the Font of a Datepicker