SwiftUI:点击时更改列表中项目的背景颜色
SwiftUI: change background color of item in a list when it's tapped on
我有一个带有项目列表的水平滚动视图,我想在用户点击它时更改项目的背景颜色。这是我的代码,但是当我 运行 它并点击项目时没有任何反应。
struct HorizontalList: View {
var list = ["item 1", "item 2", "item 3", "item 4", "item 5", "item 6", "item 7", "item 8", "item 9", "item 10"]
@State var selectedIndex = 0
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 10) {
ForEach(0..<list.count) { index in
ListItem(isSelected: selectedIndex == index, label: list[index])
.listRowBackground(Color.blue)
.onTapGesture {
selectedIndex = index
}
}
}
}
}
}
struct ListItem: View {
@State var isSelected: Bool
@State var label: String
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 8)
.foregroundColor(isSelected ? Color.blue : Color.clear)
.frame(minHeight: 16, idealHeight: 16, maxHeight: 16)
Text(label)
}
}
}
您需要使用 .simultaneousGesture
,因为列表在幕后使用 DragGesture
。
.simultaneousGesture(
TapGesture()
.onEnded({ _ in
selectedIndex = index
})
)
编辑:
查看代码中的注释。
struct HorizontalList: View {
var listItems = Array(1...10).map( { ListItem(text: "Item \([=11=])") })
@State var selectedIndex = 0
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 10) {
// It is good practice to always supply a ForEach with an identifiable. If you were
// to try to delete an element in the array, you can cause a crash without this.
// What Array(zip()) does is takes listItems and listItems.indices and put them together
// into an array of pairs, requiring two arguments in the ForEach, one for the item and
// one for the index. You can then use both in the loop. However, the ForEach tracks them by
// the identifiable element, not the index which is what the id: \.0 does.
ForEach(Array(zip(listItems, listItems.indices)), id: \.0) { item, index in
ListItemView(isSelected: selectedIndex == index, label: item.text)
.listRowBackground(Color.blue)
.simultaneousGesture(
TapGesture()
.onEnded({ _ in
selectedIndex = index
})
)
}
}
}
}
}
struct ListItemView: View {
// There is no reason for these to be an @State var unless this view changes them. If it does,
// it really should be a Binding to pass the data back.
let isSelected: Bool
let label: String
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 8)
.foregroundColor(isSelected ? Color.blue : Color.clear)
.frame(minHeight: 16, idealHeight: 16, maxHeight: 16)
Text(label)
}
}
}
// This becomes the item you itereate on
struct ListItem:Hashable, Identifiable {
let id = UUID()
var text: String
}
我有一个带有项目列表的水平滚动视图,我想在用户点击它时更改项目的背景颜色。这是我的代码,但是当我 运行 它并点击项目时没有任何反应。
struct HorizontalList: View {
var list = ["item 1", "item 2", "item 3", "item 4", "item 5", "item 6", "item 7", "item 8", "item 9", "item 10"]
@State var selectedIndex = 0
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 10) {
ForEach(0..<list.count) { index in
ListItem(isSelected: selectedIndex == index, label: list[index])
.listRowBackground(Color.blue)
.onTapGesture {
selectedIndex = index
}
}
}
}
}
}
struct ListItem: View {
@State var isSelected: Bool
@State var label: String
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 8)
.foregroundColor(isSelected ? Color.blue : Color.clear)
.frame(minHeight: 16, idealHeight: 16, maxHeight: 16)
Text(label)
}
}
}
您需要使用 .simultaneousGesture
,因为列表在幕后使用 DragGesture
。
.simultaneousGesture(
TapGesture()
.onEnded({ _ in
selectedIndex = index
})
)
编辑: 查看代码中的注释。
struct HorizontalList: View {
var listItems = Array(1...10).map( { ListItem(text: "Item \([=11=])") })
@State var selectedIndex = 0
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 10) {
// It is good practice to always supply a ForEach with an identifiable. If you were
// to try to delete an element in the array, you can cause a crash without this.
// What Array(zip()) does is takes listItems and listItems.indices and put them together
// into an array of pairs, requiring two arguments in the ForEach, one for the item and
// one for the index. You can then use both in the loop. However, the ForEach tracks them by
// the identifiable element, not the index which is what the id: \.0 does.
ForEach(Array(zip(listItems, listItems.indices)), id: \.0) { item, index in
ListItemView(isSelected: selectedIndex == index, label: item.text)
.listRowBackground(Color.blue)
.simultaneousGesture(
TapGesture()
.onEnded({ _ in
selectedIndex = index
})
)
}
}
}
}
}
struct ListItemView: View {
// There is no reason for these to be an @State var unless this view changes them. If it does,
// it really should be a Binding to pass the data back.
let isSelected: Bool
let label: String
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 8)
.foregroundColor(isSelected ? Color.blue : Color.clear)
.frame(minHeight: 16, idealHeight: 16, maxHeight: 16)
Text(label)
}
}
}
// This becomes the item you itereate on
struct ListItem:Hashable, Identifiable {
let id = UUID()
var text: String
}