【发布时间】:2021-04-05 14:15:24
【问题描述】:
我正在寻找一种方法来实现像 Mail.app 这样的三列布局的工具栏。此外,Notes.app 使用几乎相同的工具栏,两个应用程序之间的唯一重要区别是 Notes.app 看起来像 WindowStyle 是 HiddenTitleBarWindowStyle 而 Mail.app 看起来像 Default|TitleBarWindowStyle。
以下应该是正确的:
- 如果侧边栏是折叠的,则会有一个列表和详细视图
- 将列表与详细视图分开的分隔线一直向上穿过工具栏。 (这可以通过
HiddenTitleBarWindowStyle来实现) - 如果 Title 太长而无法放入导航列表,垂直分割线会被打破:列表仍然像以前一样从 Detail View 中分割出来,但现在 Toolbar 看起来像一个
DefaultWindowStyle,只有一个小工具栏中的Divider()-like 行。
我需要WindowStyle、WindowToolbarStyle 和.toolbar 配置的哪种组合来实现此设置?
编辑
我注意到无法删除 Notes.app 显示的分隔线。不过,我还没有在文档中找到对任何此类元素的引用。
代码示例
我已将问题提炼为一个简单的应用程序,其中大部分是工具栏内容。在我的原始代码中,我使用了两个嵌套的NavigationViews,而对于示例,我只使用了一个NavigationView 和两个列表。但是Toolbar 的结果是一样的。
带有DefaultWindowStyle 的工具栏
import SwiftUI
@main
struct ToolbarTestApp: App {
var body: some Scene {
WindowGroup {
ContentView(titleBarIsHidden: true)
}
.windowToolbarStyle(UnifiedWindowToolbarStyle())
.commands {
SidebarCommands()
}
}
}
struct ContentView: View {
@State var destination: String = "Toolbar Test"
@State var detail: String = ""
var body: some View {
NavigationView {
List {
Button(action: {self.destination = "Item with the identifier: 1"}, label: {
Text("Item 1")
})
.buttonStyle(DefaultButtonStyle())
Button(action: {self.destination = "Item 2"}, label: {
Text("Item 2")
})
.buttonStyle(DefaultButtonStyle())
}
.listStyle(SidebarListStyle())
List {
NavigationLink(
destination: DetailView(text: "\(destination) – \(detail)").onAppear{self.detail = "Detail 1"},
label: {
Text("\(destination) – Detail 1")
})
NavigationLink(
destination: DetailView(text: "\(destination) – \(detail)").onAppear{self.detail = "Detail 2"},
label: {
Text("\(destination) – Detail 2")
})
}
.listStyle(InsetListStyle())
Text("\(destination) – \(detail)")
}
.navigationTitle(destination)
.navigationSubtitle(detail)
.toolbar(id: "nav") {
ToolbarItem(id: "plus", placement: ToolbarItemPlacement.principal, showsByDefault: true) {
HStack {
Button(action: {print("pressed")}, label: {
Image(systemName: "plus.circle")
})
}
}
ToolbarItem(id: "spacer", placement: ToolbarItemPlacement.confirmationAction, showsByDefault: true) {
HStack {
Spacer()
}
}
ToolbarItem(id: "sidebar.end", placement: ToolbarItemPlacement.confirmationAction, showsByDefault: true) {
Button(action: {print("pressed")}, label: {
Image(systemName: "sidebar.right")
})
}
}
}
}
此示例将生成一个Toolbar,它永远不会显示将整个Toolbar 分成两部分的分隔线。第一个ToolbarItem 也位于Toolbar 的中心。我尝试了所有ToolbarItemPlacement,但没有一个导致项目移动到靠近标题的最左侧。
带有HiddenTitleBarWindowStyle 的工具栏
@main
struct ToolbarTestApp: App {
var body: some Scene {
WindowGroup {
ContentViewForHiddenTitleBar()
}
.windowStyle(HiddenTitleBarWindowStyle()) // added hidden title style
.windowToolbarStyle(UnifiedWindowToolbarStyle())
.commands {
SidebarCommands()
}
}
}
struct ContentViewForHiddenTitleBar: View {
@State var destination: String = "Toolbar Test"
@State var detail: String = ""
var body: some View {
NavigationView {
List {
Button(action: {self.destination = "Item with the identifier: 1"}, label: {
Text("Item 1")
})
.buttonStyle(DefaultButtonStyle())
Button(action: {self.destination = "Item 2"}, label: {
Text("Item 2")
})
.buttonStyle(DefaultButtonStyle())
}
.listStyle(SidebarListStyle())
// add geometry reader to trim title width in toolbar
GeometryReader { geometry in
List {
NavigationLink(
destination: DetailView(text: "\(destination) – \(detail)").onAppear{self.detail = "Detail 1"},
label: {
Text("\(destination) – Detail 1")
})
NavigationLink(
destination: DetailView(text: "\(destination) – \(detail)").onAppear{self.detail = "Detail 2"},
label: {
Text("\(destination) – Detail 2")
})
}
// there is no title anymore so let's fake it.
.toolbar(id: "list navigation") {
ToolbarItem(id: "title", placement: ToolbarItemPlacement.navigation, showsByDefault: true) {
VStack(alignment: .leading) {
Text(destination)
.font(.headline)
.frame(maxWidth: .infinity, alignment: .leading)
Text(detail)
.font(.subheadline)
.opacity(0.6)
.frame(maxWidth: .infinity, alignment: .leading)
}
.frame(width: geometry.size.width)
}
}
}
.listStyle(InsetListStyle())
Text("\(destination) – \(detail)")
}
.navigationTitle(destination)
.navigationSubtitle(detail)
.toolbar(id: "nav") {
// primary action will place the item next to the divider line.
ToolbarItem(id: "plus", placement: ToolbarItemPlacement.primaryAction, showsByDefault: true) {
HStack {
Button(action: {print("pressed")}, label: {
Image(systemName: "plus.circle")
})
}
}
ToolbarItem(id: "spacer", placement: ToolbarItemPlacement.confirmationAction, showsByDefault: true) {
HStack {
Spacer()
}
}
ToolbarItem(id: "sidebar.end", placement: ToolbarItemPlacement.confirmationAction, showsByDefault: true) {
Button(action: {print("pressed")}, label: {
Image(systemName: "sidebar.right")
})
}
}
}
}
此示例将生成一个始终显示全高分隔线的Toolbar。即使标题太长。因此添加了GeometryReader。这很好,直到侧边栏崩溃。 ToolbarItems 的位置将不正确。此外,在自定义 Toolbar 时,可能会删除标题,但这是不可能的。
【问题讨论】:
-
你尝试了哪个?每个有什么问题?你的代码在哪里?
-
我用代码示例更新了问题。到目前为止,这可能不是我尝试过的所有组合,但这些是看起来最有希望的组合。
HiddenTitleBarWindowStyle似乎永远无法达到目标,因为伪造的标题对我来说似乎完全错误。
标签: macos swiftui toolbar macos-big-sur swiftui-navigationview