【问题标题】:SwiftUI on macOS: list with detail view and multiple selectionmacOS 上的 SwiftUI:带有详细视图和多选的列表
【发布时间】:2021-01-20 15:06:12
【问题描述】:

TL;DR:

我无法在 macOS 上拥有包含详细视图和多项选择的列表。

更详细:

为了演示我的问题,我制作了一个小示例项目。 UI 如下所示:

这是启动时的“应用程序”,顶部有一个列表,下面有一个详细表示。因为我使用的是列表的初始化程序init(_:selection:rowContent:),其中selection 根据Apple 的文档是Binding<SelectionValue?>? 类型,所以我可以免费使用键盘箭头键选择项目。

这是完整的代码:

import SwiftUI

@main
struct UseCurorsInLisstApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(ViewModel())
        }
    }
}

class ViewModel: ObservableObject {
    @Published var items = [Item(), Item(), Item(), Item(), Item()]
    @Published var selectedItem: Item? = nil
}

struct Item: Identifiable, Hashable {
    let id = UUID()
}

struct ContentView: View {
    @EnvironmentObject var vm: ViewModel
    
    var body: some View {
        VStack {
            
            List(vm.items, id: \.self, selection: $vm.selectedItem) { item in
                VStack {
                    Text("Item \(item.id.uuidString)")
                    Divider()
                }
            }
            
            Divider()
            
            Group {
                if let item = vm.selectedItem {
                    Text("Detail item \(item.id.uuidString)")
                } else {
                    Text("No selection…")
                }
            }
            .frame(minHeight: 200.0, maxHeight: .infinity)
            
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

现在,到目前为止,在这方面取得了成功,我认为能够选择多行会很有用,所以我仔细研究了List(_:selection:rowContent:),其中selection 的类型为Binding<Set<SelectionValue>>?。为了能够获得详细视图,我只是对

进行了一些小改动

ViewModel:

class ViewModel: ObservableObject {
    @Published var items = [Item(), Item(), Item(), Item(), Item()]
    @Published var selectedItem: Item? = nil
    
    @Published var selectedItems: Set<Item>? = nil {
        didSet {
            if selectedItems?.count == 1, let item = selectedItems?.first {
                selectedItem = item
            }
        }
    }
}

还有ContentView:

struct ContentView: View {
    @EnvironmentObject var vm: ViewModel
    
    var body: some View {
        VStack {
            
            List(vm.items, id: \.self, selection: $vm.selectedItems) { item in
                VStack {
                    Text("Item \(item.id.uuidString)")
                    Divider()
                }
            }
            
            Divider()
            
            Group {
                if vm.selectedItems?.count == 1, let item = vm.selectedItems?.first {
                    Text("Detail item \(item.id.uuidString)")
                } else {
                    Text("No or multiple selection…")
                }
            }
            .frame(minHeight: 200.0, maxHeight: .infinity)
            
        }
    }
}

现在的问题是我无法再选择行中的项目,无论是通过单击还是通过箭头键。这是我遇到的限制还是我“持有错误”?

【问题讨论】:

    标签: macos swiftui swiftui-list


    【解决方案1】:

    使用按钮并将其插入集合中。键盘选择也适用于 shift +(向上/向下箭头)

    class ViewModel: ObservableObject {
        @Published var items = [Item(), Item(), Item(), Item(), Item()]
        @Published var selectedItem: Item? = nil
        
        @Published var selectedItems: Set<Item> = []
    }
    
    struct ContentView: View {
        @EnvironmentObject var vm: ViewModel
        
        var body: some View {
            VStack {
                
                List(vm.items, id: \.self, selection: $vm.selectedItems) { item in
                    Button {
                        vm.selectedItem = item
                        vm.selectedItems.insert(item)
                    } label: {
                        VStack {
                            Text("Item \(item.id.uuidString)")
                            Divider()
                        }
                    }
                    .buttonStyle(PlainButtonStyle())
                }
                
                Divider()
                
                Group {
                    if let item = vm.selectedItem {
                        Text("Detail item \(item.id.uuidString)")
                    } else {
                        Text("No or multiple selection…")
                    }
                }
                .frame(minHeight: 200.0, maxHeight: .infinity)
                
            }
        }
    }
    

    添加删除:

    Button {
        vm.selectedItem = item
        if vm.selectedItems.contains(item) {
            vm.selectedItems.remove(item)
        } else {
            vm.selectedItems.insert(item)
        }
    }
    

    编辑 简单来说需要给一个空白的默认值来设置。因为在 nil 中它永远不会追加到设置需要初始化。

    @Published var selectedItems: Set<Item> = [] {
    

    【讨论】:

    • 这是一个想法。您可以为添加删除添加自己的逻辑。
    • 它甚至比这简单得多——但你绝对把我推向了正确的方向,对此我更加感激!我的错误是使集合成为可选的,一旦我删除了一个以空集合开头的集合,它就会按预期工作。
    • @ASSeeger 只需提供空白默认值即可。因为在 nil 中它永远不会追加到 set need init。
    【解决方案2】:

    实际上我的错误非常愚蠢——将selectedItems-set 设为可选会阻止列表正常工作。向@Raja Kishan 致敬,他的提议将我推向了正确的方向。

    这是完整的工作代码:

    import SwiftUI
    
    @main
    struct UseCurorsInLisstApp: App {
        
        var body: some Scene {
            WindowGroup {
                ContentView()
                    .environmentObject(ViewModel())
            }
        }
    }
    
    class ViewModel: ObservableObject {
        @Published var items = [Item(), Item(), Item(), Item(), Item()]
        @Published var selectedItems = Set<Item>()
    }
    
    struct Item: Identifiable, Hashable {
        let id = UUID()
    }
    
    struct ContentView: View {
        @EnvironmentObject var vm: ViewModel
        
        var body: some View {
            VStack {
                List(vm.items, id: \.self, selection: $vm.selectedItems) { item in
                    VStack {
                        Text("Item \(item.id.uuidString)")
                        Divider()
                    }
                }
                
                Divider()
                
                Group {
                    if vm.selectedItems.count == 1, let item = vm.selectedItems.first {
                        Text("Detail item \(item.id.uuidString)")
                    } else {
                        Text("No or multiple selection…")
                    }
                }
                .frame(minHeight: 200.0, maxHeight: .infinity)
            }
        }
    }
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-08-10
      • 1970-01-01
      • 2020-11-20
      • 2017-07-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-13
      相关资源
      最近更新 更多