为什么模型的变化不会反映在子视图中?

huangapple go评论65阅读模式
英文:

Why does change in the model not reflect in child views?

问题

这段代码创建了一个包含两个SubItem结构的VStack,每个SubItem结构都包含一个名称和一个数字。当点击方框时,数字的值应该通过调用控制器的处理程序@Published var model增加1。

问题在于数字值的更改没有显示出来。

我初始化了每个视图以查看传递的值。似乎一直正常工作,直到ItemsDisplay:View。

但是似乎该视图的body没有被调用,因此ItemDisplay:View永远不会显示新值。

我不明白为什么当使用正确更改的项目调用init时,它的body不会被调用。

有任何想法吗?

当我点击box1时,会打印出以下内容:

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

英文:
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

答案1

得分: 0

这个

static func == (lhs: SubItem, rhs: SubItem) -> Bool {
     lhs.id == rhs.id
}

是告诉 SwiftUI 仅在 id 改变时重新加载视图。

移除它。

保留 Equatable 的原始实现。

SwiftUI 依赖于 Equatable、Identifiable 和 Hashable。除非你有充分的理由,不要覆盖默认的实现。

英文:

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.

huangapple
  • 本文由 发表于 2023年5月7日 20:17:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/76193877.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定