我正在寻找一种实现类似于 Mail.app 的三列布局的工具栏的方法。同时,Notes.app 几乎使用相同的工具栏,两个应用程序之间唯一重要的区别是 Notes.app 窗口样式为 HiddenTitleBarWindowStyle,而 Mail.app 窗口样式为 Default|TitleBarWindowStyle。
以下内容应该是正确的:带有
这个示例将生成一个使用
这个示例将生成一个
以下内容应该是正确的:
- 如果侧边栏被折叠,将显示列表和详细视图。
- 分隔列表和详细视图的分割线一直延伸到工具栏上方。(这可以通过使用
HiddenTitleBarWindowStyle
实现) - 如果标题太长而无法适应导航列表,则垂直分隔线将被打破:列表仍然与详细视图分开,但现在工具栏看起来像一个只有小的类似于
Divider()
的线条的DefaultWindowStyle
。
我需要哪种组合的 WindowStyle
、WindowToolbarStyle
和 .toolbar
配置才能实现这个设置?
编辑
我注意到无法移除Notes.app显示的分隔线。但我在文档中没有找到任何相关元素的参考。代码示例
我将问题简化为一个主要包含工具栏内容的简单应用程序。在我的原始代码中,我使用了两个嵌套的NavigationView
,而在示例中,我只使用了一个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
看起来似乎永远无法达到目标,因为假标题对我来说完全错误。 - Enie