macOS 上的 SwiftUI:带有详细视图和多项选择的列表
SwiftUI on macOS: list with detail view and multiple selection
TL;DR:
我无法在 macOS 上获得包含详细视图和多个 selection 的列表。
更详细:
为了演示我的问题,我制作了一个小示例项目。 UI 看起来如下:
这是启动时的“应用程序”,顶部是一个列表,下面是详细表示。因为我正在使用 List 的初始化程序 init(_:selection:rowContent:)
,根据 Apple 的文档,其中 selection
的类型为 Binding<SelectionValue?>?
,所以我可以免费使用键盘箭头键 selecting 项目。
完整代码如下:
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()
}
}
现在,到目前为止取得了成功,我认为能够 select 不止一行会很有用,所以我仔细研究了 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)
}
}
}
现在的问题是我无法再select 行中的项目,既不能通过单击,也不能通过箭头键。这是我 运行 的限制还是我“持有错误”?
使用按钮并将其插入集合。键盘选择也适用于 shift + (up/down arrow)
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> = [] {
实际上我的错误非常愚蠢——将 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)
}
}
}
TL;DR:
我无法在 macOS 上获得包含详细视图和多个 selection 的列表。
更详细:
为了演示我的问题,我制作了一个小示例项目。 UI 看起来如下:
这是启动时的“应用程序”,顶部是一个列表,下面是详细表示。因为我正在使用 List 的初始化程序 init(_:selection:rowContent:)
,根据 Apple 的文档,其中 selection
的类型为 Binding<SelectionValue?>?
,所以我可以免费使用键盘箭头键 selecting 项目。
完整代码如下:
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()
}
}
现在,到目前为止取得了成功,我认为能够 select 不止一行会很有用,所以我仔细研究了 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)
}
}
}
现在的问题是我无法再select 行中的项目,既不能通过单击,也不能通过箭头键。这是我 运行 的限制还是我“持有错误”?
使用按钮并将其插入集合。键盘选择也适用于 shift + (up/down arrow)
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> = [] {
实际上我的错误非常愚蠢——将 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)
}
}
}