Reputation: 61774
This is my sample, completely possible to test example:
import SwiftUI
struct Category: Identifiable {
var name: String
var color: Color
var id = UUID()
init(name: String, color: Color) {
self.name = name
self.color = color
}
}
struct CategoryWrapper: Identifiable {
var id: UUID
let category: Category
let isSelected: Bool
init(category: Category, isSelected: Bool) {
self.category = category
self.isSelected = isSelected
self.id = category.id
}
}
class ViewModel: ObservableObject {
@Published var wrappers = [CategoryWrapper]()
var selectedIdentifier = UUID()
private var categories: [Category] = [
Category(name: "PURPLE", color: .purple),
Category(name: "GRAY", color: .gray),
Category(name: "YELLOW", color: .yellow),
Category(name: "BROWN", color: .brown),
Category(name: "green", color: .green),
Category(name: "red", color: .red),
]
init() {
reload()
}
func reload() {
wrappers = categories.map { CategoryWrapper(category: $0, isSelected: $0.id == selectedIdentifier) }
}
}
typealias CategoryAction = (Category?) -> Void
struct CategoryView: View {
var category: Category
@State var isSelected: Bool = false
private var action: CategoryAction?
init(category: Category, isSelected: Bool, action: @escaping CategoryAction) {
self.category = category
self.isSelected = isSelected
self.action = action
}
var body: some View {
Button {
isSelected.toggle()
action?(isSelected ? category : nil)
} label: {
Text(category.name)
.font(.caption)
.foregroundColor(.white)
.background(isSelected ? category.color : .clear)
.frame(width: 150, height: 24)
.cornerRadius(12)
}
}
}
struct ContentView: View {
@StateObject private var viewModel = ViewModel()
var body: some View {
ScrollView {
Text("Categories")
ForEach(viewModel.wrappers) { wrapper in
CategoryView(
category: wrapper.category,
isSelected: wrapper.isSelected
) { category in
viewModel.selectedIdentifier = category?.id ?? UUID()
viewModel.reload()
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
and the result is:
Every row view is tappable. Every tap should select current category and deselect the others. Why doesn't it work? When I tap on gray or yellow, previously selected rows are not deselected. Why?
Upvotes: 0
Views: 123
Reputation: 5084
There are many problems with your code:
selectedIdentifier
should be optional;CategoryView
, you don't need to mark isSelected
with @State
because the list will reload itself after setting selectedIdentifier
& calling reload()
:struct Category: Identifiable {
var name: String
var color: Color
var id = UUID()
init(name: String, color: Color) {
self.name = name
self.color = color
}
}
struct CategoryWrapper: Identifiable {
var id: UUID
let category: Category
let isSelected: Bool
init(category: Category, isSelected: Bool) {
self.category = category
self.isSelected = isSelected
self.id = category.id
}
}
class ViewModel: ObservableObject {
@Published var wrappers = [CategoryWrapper]()
var selectedIdentifier: UUID?
private var categories: [Category] = [
Category(name: "PURPLE", color: .purple),
Category(name: "GRAY", color: .gray),
Category(name: "YELLOW", color: .yellow),
Category(name: "BROWN", color: .brown),
Category(name: "green", color: .green),
Category(name: "red", color: .red),
]
init() {
reload()
}
func reload() {
wrappers = categories.map { CategoryWrapper(category: $0, isSelected: $0.id == selectedIdentifier) }
}
}
typealias CategoryAction = (Category?) -> Void
struct CategoryView: View {
var category: Category
var isSelected: Bool = false
private var action: CategoryAction?
init(category: Category, isSelected: Bool, action: @escaping CategoryAction) {
self.category = category
self.isSelected = isSelected
self.action = action
}
var body: some View {
Button {
action?(!isSelected ? category : nil)
} label: {
Text(category.name)
.font(.caption)
.foregroundColor(.white)
.frame(width: 150, height: 24)
.background(isSelected ? category.color : .clear)
.cornerRadius(12)
}
}
}
struct ContentView: View {
@StateObject private var viewModel = ViewModel()
var body: some View {
ScrollView {
Text("Categories")
ForEach(viewModel.wrappers) { wrapper in
CategoryView(
category: wrapper.category,
isSelected: wrapper.isSelected
) { category in
viewModel.selectedIdentifier = category?.id
viewModel.reload()
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Upvotes: 1