Reputation: 6430
I have a document-based SwiftUI app. I'd like to make a inspector sidebar like the one in Xcode.
Starting with Xcode's Document App template, I tried the following:
struct ContentView: View {
@Binding var document: DocumentTestDocument
@State var showInspector = true
var body: some View {
HSplitView {
TextEditor(text: $document.text)
if showInspector {
Text("Inspector")
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
.toolbar {
Button(action: { showInspector.toggle() }) {
Label("Toggle Inspector", systemImage: "sidebar.right")
}
}
}
}
Which yielded:
How can I extend the right sidebar to full height like in Xcode?
NavigationView
works for left-side sidebars, but I'm not sure how to do it for right-side sidebars.
Upvotes: 26
Views: 3946
Reputation: 1225
Still in beta but out soon with iOS 17, tvOS17 and macOS14, SwiftUI will provide exactly this type of inspector:
ContentView()
.inspector($showInspector) {
InspectorView()
}
Here is a video from WWDC23: https://developer.apple.com/videos/play/wwdc2023/10161
And a link to the docs: https://developer.apple.com/documentation/corelocationui/locationbutton/4200336-inspector/
Upvotes: 5
Reputation: 8276
Here is some stripped down code that I have used in the past. It has the look and feel that you want.
It uses a NavigationView
with .navigationViewStyle(.columns)
with essentially three panes. Also, the HiddenTitleBarWindowStyle()
is important.
The first (navigation) pane is never given any width because the second (Detail) pane is always given all of the width when there is no Inspector, or all of the width less the Inspector's width when it's present. The ToolBar needs to be broken up and have its contents placed differently depending on whether the Inspector is present or not.
@main
struct DocumentTestDocumentApp: App {
var body: some Scene {
DocumentGroup(newDocument: DocumentTestDocument()) { file in
ContentView(document: file.$document)
}
.windowStyle(HiddenTitleBarWindowStyle())
}
}
struct ContentView: View {
@Binding var document: DocumentTestDocument
@State var showInspector = true
var body: some View {
GeometryReader { window in
if showInspector {
NavigationView {
TextEditor(text: $document.text)
.frame(minWidth: showInspector ? window.size.width - 200.0 : window.size.width)
.toolbar {
LeftToolBarItems(showInspector: $showInspector)
}
Inspector()
.toolbar {
RightToolBarItems(showInspector: $showInspector)
}
}
.navigationViewStyle(.columns)
} else {
NavigationView {
TextEditor(text: $document.text)
.frame(width: window.size.width)
.toolbar {
LeftToolBarItems(showInspector: $showInspector)
RightToolBarItems(showInspector: $showInspector)
}
}
.navigationViewStyle(.columns)
}
}
}
}
struct LeftToolBarItems: ToolbarContent {
@Binding var showInspector: Bool
var body: some ToolbarContent {
ToolbarItem(content: { Text("test left toolbar stuff") } )
}
}
struct RightToolBarItems: ToolbarContent {
@Binding var showInspector: Bool
var body: some ToolbarContent {
ToolbarItem(content: { Spacer() } )
ToolbarItem(placement: .primaryAction) {
Button(action: { showInspector.toggle() }) {
Label("Toggle Inspector", systemImage: "sidebar.right")
}
}
}
}
struct Inspector: View {
var body: some View {
VStack {
Text("Inspector Top")
Spacer()
Text("Bottom")
}
}
}
Upvotes: 4