Reputation: 519
struct ContentView: View {
@StateObject var cntrl=ContentViewController2()
var body: some View {
VStack {
ItemsDisplay(items: cntrl.model.items, handler: cntrl.updateItem)
}
}
}
struct ItemsDisplay:View {
let items:[SubItem]
let handler:(SubItem)->Void
init(items: [SubItem], handler: @escaping (SubItem) -> Void) {
self.items = items
self.handler = handler
print("init of ItemsDisplay")
for item in items {
print("\(item.id) \(item.value)")
}
}
var body: some View {
VStack {
ForEach(items, id: \.id) {item in
ItemDisplay(item: item, handler: handler)
}
}
}
}
struct ItemDisplay:View {
let item:SubItem
let handler:(SubItem)->Void
init(item: SubItem, handler: @escaping (SubItem) -> Void) {
self.item = item
self.handler = handler
print("init of ItemDisplay: \(item.value)")
}
var body: some View {
VStack {
Text("\(item.id)")
Text("\(item.value)")
}
.padding(.all)
.border(.black, width:5)
.onTapGesture {
print("tapped")
handler(item)
}
}
}
struct SubItem:Equatable {
static func == (lhs: SubItem, rhs: SubItem) -> Bool {
lhs.id == rhs.id
}
let id:String
var value:Int
mutating func changeValue(_ newval:Int) {
print("changing value to: \(newval)")
value=newval
}
}
struct ContentViewModel2 {
var items:[SubItem]
mutating func updateItem(_ item:SubItem) {
if let i=items.firstIndex(of: item) {
let newValue=item.value+1
print("newValue for item: \(item.id) should be: \(newValue)")
items[i].changeValue(newValue)
print("newValue of item: \(items[i].id) is: \(items[i].value)")
}
}
}
class ContentViewController2:ObservableObject {
@Published var model:ContentViewModel2
init() {
model=ContentViewModel2(items: [SubItem(id: "box1", value: 1), SubItem(id: "box10", value: 10)])
}
func updateItem(_ item:SubItem) {
if let i=model.items.firstIndex(of: item) {
print("updating: \(item.id)")
model.updateItem(item)
}
// objectWillChange.send()
}
}
The code above creates a VStack of 2 SubItem structs as a box with a name and a number. When the box is clicked, the value of the number displayed should increment by 1 by calling a handler of the controller that holds the "source of truth" in @Published var model.
The problem is that the change in value does not show.
I init each View to see what values are passed. It seems to work all the way down to ItemsDisplay:View. But it seems that the body of this View is not called, thus the ItemDisplay:View never gets to display the new value.
I don't understand why when the init is called with items that have changed correctly, it's body is not called.
Any ideas?
When I click on the box1, this prints out:
tapped
updating: box1
newValue for item: box1 should be: 2
changing value to: 2
newValue of item: box1 is: 2
init of ItemsDisplay
box1 2
box10 10
Upvotes: 0
Views: 54
Reputation: 29614
This
static func == (lhs: SubItem, rhs: SubItem) -> Bool {
lhs.id == rhs.id
}
Is telling SwiftUI to only reload views when the id
changes.
Remove it.
Keep the original implementation of Equatable
SwiftUI is dependent on Equatable, Identifiable and Hashable. Don’t override default implementations unless you have a really good reason.
Upvotes: 0