I have a class that implements the Equatable protocol, and it uses a UUID field for comparison:
class MemberViewModel {
private static var entities: [MemberViewModel] = []
private var entity: Member
let id = UUID()
init(_ entity: Member) {
self.entity = entity
}
static func members() -> [MemberViewModel] {
entities.removeAll()
try? fetch().forEach { member in
entities.append(MemberViewModel(member))
}
return entities
}
}
extension MemberViewModel: Equatable {
static func == (lhs: MemberViewModel, rhs: MemberViewModel) -> Bool {
return lhs.id == rhs.id
}
}
I then have a view that creates icons that when tapped should display a stroke to denote it was "selected":
struct MyView: View {
@State var selectedMember: MemberViewModel? = nil
var body: some View {
let members = MemberViewModel.members()
ScrollView(.horizontal, showsIndicators: true) {
HStack(alignment: .top, spacing: 4) {
ForEach (members, id: \.id) { member in
var isSelected: Bool = selectedMember == member
Circle()
.fill(Color(.red))
.frame(width: 48, height: 48)
.overlay() {
if isSelected {
Circle()
.stroke(Color(.black), lineWidth: 2)
}
}
.onTapGesture { selectedMember = member }
}
}
}
}
}
I have tried setting isSelected
multiple ways, including the following from another SO question:
let isSelected = Binding<Bool>(get: { self.selectedMember == member }, set: { _ in })
When debugging using breakpoints, the value of isSelected
is always false.
I'm using XCode Version 14.0.1 (14A400), and Swift 5.7.
CodePudding user response:
Put let members = MemberViewModel.members()
just before
var body: some View {...}
, not inside it, like this:
let members = MemberViewModel.members() // <-- here
var body: some View {
...
}
Due to your weird code structure, let members = MemberViewModel.members()
gets re-done evey time the body
is refreshed. And since the UUID is re-generated .... you can guess that the ids now are all over the place and not equal to the selectedMember
.
In other words, the object comparison is working, but you are not comparing what you think.
CodePudding user response:
Use the sample below hope you get the idea.
Model
struct Member {
let id = UUID()
}
extension Member: Equatable {
static func == (lhs: Member, rhs: Member) -> Bool {
return lhs.id == rhs.id
}
}
class MemberViewModel: ObservableObject {
// each time you update the entities, view vill be updated
@Published var entities: [Member] = []
func fetchMembers() {
entities.removeAll()
// your code to fetch members
try? fetch().forEach { member in
entities.append(member)
}
}
}
MyView
struct MyView: View {
@StateObject private var modelData = MemberViewModel()
@State private var selectedMember: Member? = nil
var body: some View {
ScrollView(.horizontal, showsIndicators: true) {
HStack(alignment: .top, spacing: 4) {
ForEach (modelData.entities, id: \.id) { member in
Circle()
.fill(Color(.red))
.frame(width: 48, height: 48)
.overlay() {
if selectedMember == member {
Circle()
.stroke(Color(.black), lineWidth: 2)
}
}
.onTapGesture { selectedMember = member }
}
}
}.onAppear {
modelData.fetchMembers()
}
}
}