使用 ObservedObject 更新文本,但不是我的自定义视图

Posted

技术标签:

【中文标题】使用 ObservedObject 更新文本,但不是我的自定义视图【英文标题】:Text updating with ObservedObject but not my custom View 【发布时间】:2021-08-15 03:55:43 【问题描述】:

我正在尝试将棋盘游戏 Splendor 编码为 SwiftUI 中的一个简单编码项目。我有点笨,所以如果我把这一切都弄错了,请指出我正确的方向。我也在尝试使用 MVVM 范例。

游戏板上有两叠代币,一叠用于无人认领的游戏代币,另一叠用于活跃玩家的代币。棋盘上有一个按钮,玩家可以一次领取一个代币。

标记在自定义视图中绘制 - TokenView() - 它绘制了一个简单的 Circle() 偏移量少量以形成堆栈。圆圈的数量与标记的数量相匹配。在每个令牌堆栈下面是一个 Text(),它打印令牌的数量。

按下按钮时,只有 Text() 正确更新,绘制的标记数保持不变。

我知道我的问题与我正在混合 @ObservedObject 和静态 Int 的事实有关。我不知道如何不使用 Int,因为 TokenView 不知道它是在绘制棋盘的令牌集合还是活跃玩家的令牌集合。如何将令牌计数作为@ObservedObject 传递?为什么 Text() 会正确更新?

型号:

struct TheGame 
    var tokenCollection: TokenCollection
    var players: [Player]
    
    init() 
        self.tokenCollection = TokenCollection()
        self.players = [Player()]
    


struct Player 
    var tokenCollection: TokenCollection
    
    init() 
        self.tokenCollection = TokenCollection()
    


struct TokenCollection 
    var count: Int
    
    init() 
        self.count = 5
    

视图模型:

class MyGame: ObservableObject 
    @Published private (set) var theGame = TheGame()
    
    func collectToken() 
        theGame.tokenCollection.count -= 1
        theGame.players[0].tokenCollection.count += 1
    

游戏板视图:

struct GameBoardView: View 
    @StateObject var myGame = MyGame()

    var body: some View 
        VStack
            TokenStackView(myGame: myGame, tokenCount: myGame.theGame.tokenCollection.count)
                .frame(width: 100, height: 200, alignment: .center)
            Button 
                myGame.collectToken()
             label: 
                Text ("Collect Token")
            
            TokenStackView(myGame: myGame, tokenCount: myGame.theGame.players[0].tokenCollection.count)                .frame(width: 100, height: 200, alignment: .center)
        
    

TokenStackView:

struct TokenStackView: View 
    @ObservedObject var myGame: MyGame
    
    var tokenCount: Int
    
    var body: some View 
        VStack 
            ZStack 
                ForEach (0..<tokenCount)  index in
                    Circle()
                        .stroke(lineWidth: 5)
                        .offset(x: CGFloat(index * 10), y: CGFloat(index * 10))
                
            
            Spacer()
            Text("\(tokenCount)")
        
    

【问题讨论】:

如果您希望您的问题得到解答,您需要让专家尽快获得具有可重现问题的工作样本。完美地,我应该将您的代码粘贴到我的示例项目中,并在我运行它时尽快看到问题。在您的情况下,GemType 和其他类是未知的。还有说明太长了。而且我不是要求添加这些类,我要求的是[最小可重现示例][***.com/help/minimal-reproducible-example],因此您需要删除所有与问题无关的代码,在代码和问题描述中本地化您的问题 谢谢菲利普。我试图做到这一点。您可以复制并粘贴我的 sn-ps 代码,它应该可以工作。 谢谢,这样好多了???? 【参考方案1】:

如果您查看控制台,您会看到错误:

ForEach<Range<Int>, Int, OffsetShape<_StrokedShape<Circle>>> count (2) != its initial count (1). `ForEach(_:content:)` should only be used for *constant* data. Instead conform data to `Identifiable` or use `ForEach(_:id:content:)` and provide an explicit `id`!

在这种情况下修复很简单,只需添加id,如下所示:

ForEach(0..<tokenCount, id: \.self) 

【讨论】:

谢谢!!我花了三个小时宝贵的婴儿睡眠时间,就这么简单!你能告诉我为什么 tokenCount 作为“已发布”变量之一,当它是一个静态 Int 时?根据我(明显错误)的理解,MyGame,包括它的所有属性,是@StateObject。当我调用 TokenStackView 并将 tokenCount 传递给它时,我正在执行传递一个整数值而不是发布的对象。我确实了解结构/类的所有属性都在更新时发布,但我认为我正在将一个值复制到 tokenCount 中,而不是实际传递一个引用。 @user1357607 不客气! tokenCount 已发布。您的@ObservedObject var myGame: MyGame 触发GameBoardView 重新计算并将新值传递给TokenStackView,就是这样

以上是关于使用 ObservedObject 更新文本,但不是我的自定义视图的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI ObservedObject 不更新父视图

SwiftUI - 尽管使用了 @Published 和 @ObservedObject,但视图不会更新

SwiftUI - 使用 @ObservedObject 作为同一结构中 @FetchRequest 的结果来强制更新 UI

StateObject 属性不会更新视图,但 ObservedObject 会

成功登录后,SwiftUI ObservedObject 未更新视图

如何通过 Binding 和 ObservedObject 更新 UIViewRepresentable 地图