Reputation: 9190
I'm using a NavigationView to display a SidebarView and DetailView in the window of a Mac app:
import SwiftUI
private let fruits = ["🍎 Apple", "🥥 Coconut", "🥭 Mango", "🥝 Kiwi"]
struct SidebarView: View {
@Binding var selectedFruit: String?
var body: some View {
List(fruits, id: \.self, selection: $selectedFruit) { fruit in
Text("\(fruit)")
}
.listStyle(SidebarListStyle())
.frame(minWidth: 180, idealWidth: 200, maxWidth: 300)
}
}
struct DetailView: View {
@Binding var fruit: String?
var body: some View {
Text("\(fruit ?? "Default Fruit")")
.font(.headline)
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
struct ContentView: View {
@State private var selectedFruit: String?
var body: some View {
NavigationView {
SidebarView(selectedFruit: $selectedFruit)
DetailView(fruit: $selectedFruit)
}
.frame(width: 500, height: 400)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
The width of the SidebarView can be adjusted by dragging the center divider left or right in the window. I would like to have an initial width of 200 defined for the sidebar along with a minimum width of 180 and a max width of 300. I attempted this by setting the frame of the List which does indeed set the max width but the sidebar can still be completely collapsed. Also, the initial width of the sidebar seems to be defined by minWidth
not idealWidth
.
Upvotes: 13
Views: 5256
Reputation: 375
Having been inspired by the answer from MScottWaller, I just added some calculations to the min size for the detailview. This seems to pin the SideBar to between 220 and 380 Works on macOS 126.1/Xcode 14.0.1
The critical piece is the private func minDetailWidth()
struct ItemListView: View {
struct ItemRow: View {
struct ItemView: View {
let item: Item
var body: some View {
VStack {
Image(systemName: item.imageName)
.clipShape(Circle())
.overlay(Circle().stroke(Color.white, lineWidth: 2))
.padding(2)
.overlay(Circle().strokeBorder(Color.black.opacity(0.1)))
.shadow(radius: 3)
.padding(4)
Text(item.name)
.font(.largeTitle)
}
}
}
let item: Item
var body: some View {
NavigationLink(destination: ItemView(item: item)) {
HStack {
Image(systemName: item.imageName)
.resizable()
.frame(width: 50, height: 50)
.clipShape(Circle())
.overlay(Circle().stroke(Color.white, lineWidth: 2))
.padding(2)
.overlay(Circle().strokeBorder(Color.black.opacity(0.1)))
.shadow(radius: 3)
.padding(4)
Text(item.name)
.font(.largeTitle)
}
}
}
}
let items: [Item]
var body: some View {
List(items) { fruit in
ItemRow(item: fruit)
}
}
}
struct Item: Identifiable {
let id = UUID()
let name: String
let imageName: String
let color: Color
}
class Model: ObservableObject {
@Published var transportation: [Item] = loadTransportation()
@Published var vegetables: [Item] = loadVegetables()
static func loadTransportation() -> [Item] {
return [
Item(name: "Airplane", imageName: "airplane", color: .orange),
Item(name: "Car", imageName: "car", color: .blue),
Item(name: "Bus", imageName: "bus.fill", color: .red),
Item(name: "Tram", imageName: "tram.fill", color: .pink),
Item(name: "Ferry", imageName: "ferry", color: .purple),
Item(name: "Bicycle", imageName: "bicycle", color: .green),
]
}
static func loadVegetables() -> [Item] {
return [
Item(name: "Spinach", imageName: "spinach", color: .red),
Item(name: "Lettuce", imageName: "lettuce", color: .orange),
Item(name: "Tomatoes", imageName: "tomatoes", color: .green),
Item(name: "Onion", imageName: "onion", color: .blue),
]
}
}
@ObservedObject private var model = Model()
private var sideBarMaxWidth = 380.0
/**
window width minus the sideBarMaxWidth
*/
private func minDetailWidth(_ proxy: GeometryProxy) -> Double {
let rv_ = proxy.size.width - sideBarMaxWidth
let rv = max(rv_, 480)
// Log4swift[Self.self].info("\(rv)")
return rv
}
var body: some View {
GeometryReader { proxy in
NavigationView {
List {
NavigationLink(
"Fruits",
destination:
ItemListView(items: self.model.transportation)
.frame(minWidth: minDetailWidth(proxy), idealWidth: 840, maxWidth: .infinity)
)
NavigationLink(
"Vegetables",
destination:
ItemListView(items: self.model.vegetables)
.frame(minWidth: minDetailWidth(proxy), idealWidth: 840, maxWidth: .infinity)
)
}
.frame(minWidth: 220, maxWidth: sideBarMaxWidth)
Text("Nothing Selected")
.frame(minWidth: minDetailWidth(proxy), maxWidth: .infinity)
}
}
}
}
struct ExampleView_Previews: PreviewProvider {
static var previews: some View {
ExampleView()
// .background(Color(NSColor.windowBackgroundColor))
.environment(\.colorScheme, .light)
ExampleView()
// .background(Color(NSColor.windowBackgroundColor))
.environment(\.colorScheme, .dark)
}
}
Upvotes: 0
Reputation: 9
Maybe you can set the minWidth
for DetailView
. I tried it works well.
Upvotes: -2
Reputation: 3583
For anyone who finds this and is wanting to know how to set the maxWidth of a sidebar in macOS, you really don't. If you do, the view will stop growing and then be caught in the middle of the sidebar, which will keep expanding.
It took me a minute to figure this out, but what you do is set the minWidth of the other view, the detail view. And the sidebar will grow until it can't. You'll notice that even the sidebar of Finder grows and grows until it hits the minWidth of the detail, so to speak.
Upvotes: 13