SwiftUI 中具有不同列的侧边栏

Posted

技术标签:

【中文标题】SwiftUI 中具有不同列的侧边栏【英文标题】:Sidebar with different columns in SwiftUI 【发布时间】:2020-07-19 09:40:27 【问题描述】:

我正在使用 SwiftUI 2 附带的新侧边栏以及在具有三列的大屏幕中导航的可能性。可以在此处找到有关其工作原理的示例:https://www.hackingwithswift.com/quick-start/swiftui/how-to-add-a-sidebar-for-ipados

它工作正常,但我想向前迈出一步,在我的主菜单中设置一些选项,显示三列,但其他选项只有两列。

这里是一些演示代码的示例。

import SwiftUI

struct ContentView: View 
    var body: some View
        NavigationView
            List
                Section(header: Text("Three columns"))
                    NavigationLink(
                        destination: ItemsView(),
                        label: 
                            Label("Animals",systemImage: "tortoise")
                        )
                    NavigationLink(
                        destination: ItemsView(),
                        label: 
                            Label("Animals 2",systemImage: "hare")
                        )
                
                Section(header: Text("Two columns"))
                    NavigationLink(
                        destination: Text("I want to see here a single view, without detail"),
                        label: 
                            Label("Settings",systemImage: "gear")
                        )
                    NavigationLink(
                        destination: Text("I want to see here a single view, without detail"),
                        label: 
                            Label("Settings 2",systemImage: "gearshape")
                        )
                
            
            .listStyle(SidebarListStyle())
            .navigationBarTitle("App Menu")
            
            ItemsView()
            DetailView(animal: "default")
            
        
    


struct ItemsView: View
    let animals = ["Dog", "Cat", "Lion", "Squirrel"]
    var body: some View
        List
            ForEach(animals, id: \.self) animal in
                NavigationLink(
                    destination: DetailView(animal: animal))
                    Text(animal)
                
            
        
        .listStyle(PlainListStyle())
        .navigationTitle("Animals")
    


struct DetailView: View
    var animal: String
    var body: some View
        VStack
            Text("????")
                .font(.title)
                .padding()
            Text(animal)
        
    

struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        ContentView()
            .previewLayout(.sizeThatFits)
    

如果您在 iPad Pro(12.9 英寸)中以横向模式运行代码,您可以看到树列。首先是应用程序菜单(侧边栏)。如果您单击前两个选项之一(动物和动物 2),您可以看到动物列表(第二列),当您单击某些动物时,您会到达第三列(详细视图)。 但是,当我单击菜单的最后两个选项(设置和设置 2)时,我希望只有两列。任何线索如何实现它?

如果未选择菜单中的某些第一个选项(使用 NavigationLink 中的 selected 参数),我尝试隐藏该部分,但没有运气。似乎不可能(或者我不知道)知道在侧边栏中选择了哪个选项。

欢迎提出任何想法!

【问题讨论】:

我有一个类似的问题,并通过创建两个 NavigationViews 来解决它:一个有两个条目,一个有三个。这些 NavigationView 由 if-then-else 嵌入,这取决于侧边栏的当前选择。效果很好... 感谢@Hardy,您的解决方案是一个很好的解决方法,但它不能 100% 正常工作。显示选择的选项时出现问题,在某些情况下,返回按钮无法按预期工作。我联系了 Apple 支持,但该功能不受官方支持。希望在下一个 SwiftUI 大版本中得到支持。 @jarnaez,你知道这个功能是否包含在 SwiftUI 3.0 中吗?能够根据主菜单选择在 2 或 3 列之间进行切换将是非常棒的。 嗨@Peanutsmasher,我暂时没有时间检查 SwiftUI 3 中的所有新功能和更改,但是从我所做的快速阅读中,我没有看到任何关于此功能的信息 :( 【参考方案1】:

我花了几天时间才弄明白。

在不同的 iPad 设备和多任务模式下测试,一切正常。(ios14+,尚未在 iOS13 上测试)

小例子:

extension UISplitViewController 
    open override func viewDidLoad() 
        super.viewDidLoad()
        self.show(.primary) // show sidebar, this is the key, toke me days to find this...
        self.showsSecondaryOnlyButton = true
    


struct ContentView: View 
    @State var column: Int = 3
    var body: some View 
        switch column 
        case 3: // Triple Column
            NavigationView 
                List 
                    HStack 
                        Text("Triple")
                    
                    .onTapGesture 
                        column = 3
                    
                    HStack 
                        Text("Double")
                    
                    .onTapGesture 
                        column = 2
                    
                
                Text("Supplementary View")
                Text("Detail View")
            
        default: // Double Column
            NavigationView 
                List 
                    HStack 
                        Text("Triple")
                    
                    .onTapGesture 
                        column = 3
                    
                    HStack 
                        Text("Double")
                    
                    .onTapGesture 
                        column = 2
                    
                
                Text("Supplementary View")
            
        
    

我的另一个答案:设置侧边栏默认选中项。

结合这两个解决方案,我已经构建了一个2&3列共存样式的应用程序。

SwiftUI, selecting a row in a List programmatically

【讨论】:

【参考方案2】:

这是一个使用自定义ViewModifier 的解决方案。它适用于 iOS 14.2、15.0 和 15.2。由于您使用的是SidebarListStyleLabel,因此我没有测试以前的版本。

测试项目:

enum Item: Hashable 
    case animals, animals2, settings, settings2

    static var threeColumns = [Item.animals, .animals2]
    static var twoColumns = [Item.settings, .settings2]

    var title: String 
        switch self 
        case .animals:
            return "animals"
        case .animals2:
            return "animals2"
        case .settings:
            return "settings"
        case .settings2:
            return "settings2"
        
    

    var systemImage: String 
        switch self 
        case .animals:
            return "tortoise"
        case .animals2:
            return "hare"
        case .settings:
            return "gear"
        case .settings2:
            return "gearshape"
        
    


struct ContentView: View 

    @State var selectedItem: Item?

    var body: some View 
        NavigationView 
            List 
                Section(header: Text("Three columns")) 
                    ForEach(Item.threeColumns, id: \.self)  item in
                        NavigationLink(tag: item, selection: $selectedItem) 
                            ItemsView()
                         label: 
                            Label(item.title.capitalized, systemImage: item.systemImage)
                        
                    
                
                Section(header: Text("Two columns")) 
                    ForEach(Item.twoColumns, id: \.self)  item in
                        NavigationLink(
                            destination: Text("I want to see here a single view, without detail"),
                            label: 
                                Label(item.title, systemImage: item.systemImage)
                            )
                    
                
            
            .listStyle(SidebarListStyle())
            .navigationBarTitle("App Menu")
        
    


struct ItemsView: View 
    let animals = ["Dog", "Cat", "Lion", "Squirrel"]
    var body: some View 
        List 
            ForEach(animals, id: \.self)  animal in
                NavigationLink(
                    destination: DetailView(animal: animal)) 
                        Text(animal)
                    
            
        
        .listStyle(PlainListStyle())
        .navigationTitle("Animals")
    


struct DetailView: View 
    var animal: String
    var body: some View 
        VStack 
            Text("?")
                .font(.title)
                .padding()
            Text(animal)
        
    

struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        ContentView()
            .previewLayout(.sizeThatFits)
    


private struct ColumnModifier: ViewModifier 
    let item: Item?

    func body(content: Content) -> some View 
        if item == .settings || item == .settings2 
            content
            EmptyView()
         else 
            content
            EmptyView()
            DetailView(animal: "default")
        
    


【讨论】:

以上是关于SwiftUI 中具有不同列的侧边栏的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI - Catalyst 半透明侧边栏

在 UISplitViewController 中使用 SwiftUI 列表侧边栏

Vue.js - 使用 vuex 打开/关闭动态侧边栏思想不同的组件(导航栏到侧边栏)

wordpress不同页面显示不同的侧边栏,怎么设置

如何为一个 flexdashboard 页面提供不同的侧边栏?

导航视图 - 侧边栏等注释 - 切换按钮