使用切换子视图和选择手势列出项目
List Item with Toggle subview and selection gesture
我有一个包含 Toggle
视图的列表和项目。我想分别处理选择和切换状态。未选择列表项时应禁用切换。
问题是:
- 第一次点击 - 选择列表项。 (
selected = true
)
- 点击切换 - 设置
selection = false
在 UIKit 上,Button/Switch/etc...捕获 'tap' 并且不传递到 TableView 选择状态。
struct ListView: View {
@State var selected = Set<Int>()
let items = (1...10).map { ItemDataModel(id: [=10=]) }
var body: some View {
List(items) { item in
ListItemView(dataModel: item)
.onTapGesture { if !(selected.remove(item.id) != .none) { selected.insert(item.id) }}
}
}
}
struct ListItemView: View {
@ObservedObject var dataModel: ItemDataModel
var body: some View {
HStack {
Text(dataModel.title)
Spacer()
Toggle(
isOn:Binding<Bool>(get: {dataModel.isOn}, set: {dataModel.isOn = [=10=]}),
label: { Text("toggle") })
}
}
}
class ItemDataModel: Hashable, Identifiable, ObservableObject {
let id: Int
let title: String
@Published var isOn: Bool = false
init(id: Int) {
self.id = id
title = "item \(id)"
}
func hash(into hasher: inout Hasher) {
hasher.combine(title)
}
static func == (lhs: ItemDataModel, rhs: ItemDataModel) -> Bool {
return lhs.id == rhs.id
}
}
[请尽量忽略语法错误(如果存在)并关注手势问题]
首先,您的 [ItemDataModel]
数组应该移到正文之外,这样就不会每次都重新创建:
struct ListView: View {
@State var selected = Set<Int>()
let items = (1...10).map(ItemDataModel.init) // move outside the `body`
var body: some View {
VStack {
Text(String(describing: selected))
List(items) { item in
ListItemView(dataModel: item)
.onTapGesture { if !(selected.remove(item.id) != .none) { selected.insert(item.id) }}
}
}
}
}
然后,确保 ListItemView
中的 Toggle
不会占用所有 space(这是默认行为)并附加 onTapGesture
以覆盖parent的手势:
struct ListItemView: View {
@ObservedObject var dataModel: ItemDataModel
var body: some View {
HStack {
Text(dataModel.title)
// Text("toggle") // if necessary add Toggle's label as `Text`
Spacer()
Toggle("", isOn: $dataModel.isOn) // use another initialiser
.fixedSize() // limit Toggle's width
.background(Color.red) // debug-only, to see the real frame
.onTapGesture {} // override tap gestures
}
.contentShape(Rectangle()) // make empty space *clickable*
}
}
我有一个包含 Toggle
视图的列表和项目。我想分别处理选择和切换状态。未选择列表项时应禁用切换。
问题是:
- 第一次点击 - 选择列表项。 (
selected = true
) - 点击切换 - 设置
selection = false
在 UIKit 上,Button/Switch/etc...捕获 'tap' 并且不传递到 TableView 选择状态。
struct ListView: View {
@State var selected = Set<Int>()
let items = (1...10).map { ItemDataModel(id: [=10=]) }
var body: some View {
List(items) { item in
ListItemView(dataModel: item)
.onTapGesture { if !(selected.remove(item.id) != .none) { selected.insert(item.id) }}
}
}
}
struct ListItemView: View {
@ObservedObject var dataModel: ItemDataModel
var body: some View {
HStack {
Text(dataModel.title)
Spacer()
Toggle(
isOn:Binding<Bool>(get: {dataModel.isOn}, set: {dataModel.isOn = [=10=]}),
label: { Text("toggle") })
}
}
}
class ItemDataModel: Hashable, Identifiable, ObservableObject {
let id: Int
let title: String
@Published var isOn: Bool = false
init(id: Int) {
self.id = id
title = "item \(id)"
}
func hash(into hasher: inout Hasher) {
hasher.combine(title)
}
static func == (lhs: ItemDataModel, rhs: ItemDataModel) -> Bool {
return lhs.id == rhs.id
}
}
[请尽量忽略语法错误(如果存在)并关注手势问题]
首先,您的 [ItemDataModel]
数组应该移到正文之外,这样就不会每次都重新创建:
struct ListView: View {
@State var selected = Set<Int>()
let items = (1...10).map(ItemDataModel.init) // move outside the `body`
var body: some View {
VStack {
Text(String(describing: selected))
List(items) { item in
ListItemView(dataModel: item)
.onTapGesture { if !(selected.remove(item.id) != .none) { selected.insert(item.id) }}
}
}
}
}
然后,确保 ListItemView
中的 Toggle
不会占用所有 space(这是默认行为)并附加 onTapGesture
以覆盖parent的手势:
struct ListItemView: View {
@ObservedObject var dataModel: ItemDataModel
var body: some View {
HStack {
Text(dataModel.title)
// Text("toggle") // if necessary add Toggle's label as `Text`
Spacer()
Toggle("", isOn: $dataModel.isOn) // use another initialiser
.fixedSize() // limit Toggle's width
.background(Color.red) // debug-only, to see the real frame
.onTapGesture {} // override tap gestures
}
.contentShape(Rectangle()) // make empty space *clickable*
}
}