【问题标题】:how to add a toolbar divider for a three column view in swiftUI life cycle如何在 swiftUI 生命周期中为三列视图添加工具栏分隔符
【发布时间】:2021-04-05 14:15:24
【问题描述】:

我正在寻找一种方法来实现像 Mail.app 这样的三列布局的工具栏。此外,Notes.app 使用几乎相同的工具栏,两个应用程序之间的唯一重要区别是 Notes.app 看起来像 WindowStyleHiddenTitleBarWindowStyle 而 Mail.app 看起来像 Default|TitleBarWindowStyle

以下应该是正确的:

  1. 如果侧边栏是折叠的,则会有一个列表和详细视图
  2. 将列表与详细视图分开的分隔线一直向上穿过工具栏。 (这可以通过HiddenTitleBarWindowStyle 来实现)
  3. 如果 Title 太长而无法放入导航列表,垂直分割线会被打破:列表仍然像以前一样从 Detail View 中分割出来,但现在 Toolbar 看起来像一个DefaultWindowStyle,只有一个小工具栏中的Divider()-like 行。

我需要WindowStyleWindowToolbarStyle.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


【解决方案1】:

默认的标题栏样式很好,你只需要将工具栏项附加到顶部 NavigationView 的子视图,例如:

var body: some View {
    NavigationView {
        
        List {
            ...
        }
        .listStyle(SidebarListStyle())
        .toolbar {
            ToolbarItem {
                Button(action: { }, label: {
                    Image(systemName: "sidebar.right")
                })
            }
        }
        
        List {
            ...
        }
        .listStyle(InsetListStyle())
        .toolbar {
            ToolbarItem {
                Button(action: { }, label: {
                    Image(systemName: "plus.circle")
                })
            }
        }
        
        Text("\(destination) – \(detail)")
    }
    .navigationTitle(destination)
    .navigationSubtitle(detail)
}

我没有将任何工具栏项附加到第三列(Text),但您可以 — 只需确保将相同的工具栏项附加到您的 DetailViews,因为它的工具栏将在用户导航时替换文本的工具栏. (如果你不确定我的意思,试试看,你很快就会明白我在说什么:)

【讨论】:

  • 你知道 GIR 是什么……我花了好几天的时间才弄清楚这一点。根据您的见解,只需两分钟即可尝试,直到我得到我想要的东西。将工具栏项添加到第二个列表中,DetailView 正是我正在寻找的拆分。非常感谢您,祝您新年快乐!
猜你喜欢
  • 2023-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-12
  • 2020-10-15
相关资源
最近更新 更多