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()
}
}
}
