You could get a NavigationView easily in SwiftUI. However, in macOS, there is an issue. If you collapse the sidebar and release your mouse, you cannot expand it again. What even worse, the state is also be remembered by the system and you could not get the sidebar even if you restart the app.
Here is a sample.
import SwiftUI
struct SidebarSwiftUIView: View {
@State private var shouldShowPurple = false
var body: some View {
NavigationView {
List {
NavigationLink(
destination: DestinationPageView(color: .purple),
isActive: $shouldShowPurple
) {
Text("Purple Page")
}
NavigationLink(
destination: DestinationPageView(color: .pink)
) {
Text("Pink Page")
}
NavigationLink(
destination: DestinationPageView(color: .orange)
) {
Text("Orange Page")
}
}
.frame(minWidth: 180, idealWidth: 200, maxWidth: 250, alignment: .leading)
.navigationTitle("Colors")
Text("Select a color page from the links.")
}
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(3)) {
shouldShowPurple = true
}
}
}
}
struct SidebarSwiftUIView_Previews: PreviewProvider {
static var previews: some View {
SidebarSwiftUIView()
}
}
struct DestinationPageView: View {
var color: Color
var body: some View {
Text("Destination Page")
.font(.title)
.foregroundColor(color)
.frame(minWidth: 300, idealWidth: 450, maxWidth:.infinity)
}
}
It runs like this.
The issue:
Hybrid Programming With AppKit
Hybrid SwiftUI with AppKit is easy. It takes 2 steps.
- Create a NSHostingViewController in Storyboard.
- Create HostingViewController.swift and init it with the SwiftUIView.
import Cocoa
import SwiftUI
class HostingController: NSHostingController<SidebarSwiftUIView> {
@objc required dynamic init?(coder: NSCoder) {
super.init(coder: coder, rootView: SidebarSwiftUIView())
}
}
Sidebar
Unless in iOS/iPadOS, there is no sidebar for navigation bar in macOS. So you have to do it yourself. Just simply add a toolbar in WindowController.
Make sure to set the tool item as Navigational in Position.
Toggle the Sidebar
SwiftUI will create a NSSplitViewController automatically. So we use respond chain to collapse/expand the sidebar.
import Cocoa
@main
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
@IBAction private func showSideBar(sender:Any?) {
NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
}
}
Bind the action to first responder.
Final
Now the app will look like this.