【问题标题】:SwiftUI ChildView Not recalculated on state changeSwiftUI ChildView 未在状态更改时重新计算
【发布时间】:2021-02-28 05:07:13
【问题描述】:

所以这很可能是一个新手 SwiftUI 问题,我有一个父视图,它采用 @ObservedObject(例如 viewModel),视图模型似乎正确地将模型中的更改发布到 CollectionView,并且视图正确地重新计算正文.但是,子视图的 (EmojiCell) 正文似乎没有重新计算?以下是相关代码,感谢任何见解。 如果我将 Card 更改为 ObservableClass 并将其 isFaceUp 设置为 @Published ,我可以让它工作,但这显然不是正确的解决方案!

struct CollectionView: View {
  @ObservedObject var viewModel: EmojiMemoryGameViewModel
 
  init(viewModel: EmojiMemoryGameViewModel) {
    self.viewModel = viewModel
  }
  
  var body: some View {
    // This is called on tap as expected so the viewModel is indeed
    // correctly notifying the view of it's change, the body is
    // recalculated, BUT EmojiCell's body doesn't get called!
    print("CollectionView recalc")
    
    return HStack {
      ForEach(viewModel.cards) { card in
        // This doesn't
        EmojiCell(card: card).onTapGesture {
          viewModel.choose(card: card)
        }
        
        // This works!
//        return  ZStack {
//          if card.isFaceUp {
//            RoundedRectangle(cornerRadius: 10)
//              .fill(Color.white)
//            RoundedRectangle(cornerRadius: 10)
//              .stroke(lineWidth: 3)
//            Text(card.content)
//          }
//          else {
//            RoundedRectangle(cornerRadius: 10).fill()
//          }
//        }
//          .onTapGesture {
//            self.viewModel.choose(card: card)
//          }
      }
    }
    .padding()
    .foregroundColor(.orange)
    .font(.largeTitle)
    
  }
}


typealias EmojiCard = MemoryGame<String>.Card

struct EmojiCell: View {
  
  let card: EmojiCard
  
  var body: some View {
    print("Cell recalc")
    return ZStack {
      if card.isFaceUp {
        RoundedRectangle(cornerRadius: 10)
          .fill(Color.white)
        RoundedRectangle(cornerRadius: 10)
          .stroke(lineWidth: 3)
        Text(card.content)
      }
      else {
        RoundedRectangle(cornerRadius: 10).fill()
      }
    }
  }
}

// Other relevant code
  struct Card: Identifiable, Hashable {
    var id: Int
    var isFaceUp = true
    var isMatched = false
    var content: CardContent
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
  }
  

extension MemoryGame.Card: Equatable {
  static func == (lhs: MemoryGame<CardContent>.Card, rhs: MemoryGame<CardContent>.Card) -> Bool {
    return lhs.id == rhs.id
  }
}

class EmojiMemoryGameViewModel: ObservableObject {
  
  @Published private var model: MemoryGame<String> = EmojiMemoryGameViewModel.createMemoryGame()
  
  static func createMemoryGame() -> MemoryGame<String> {
    let listOfCards = ["????", "????", "????"]
    return MemoryGame(numberOfPairsOfCards: listOfCards.count) { index in
      return listOfCards[index]
    }
  }
  
  var cards: [MemoryGame<String>.Card] {
    model.cards
  }
  
 
  func choose(card: MemoryGame<String>.Card) {
    model.choose(card)
  }
}



【问题讨论】:

  • 如果没有Minimal Reproducible Example,就不可能帮助您编写代码。看来您对ObservableObjects 的细节不熟悉,可能会发生很多事情@Published private var 不应该是private,这就是SwiftUI 应该引用的内容,将listOfCards 放在那里,而不是引用一个方法。在 EmojiCell EmojiCard 应该是 ObservableObject 它永远不会被告知重新加载。看Apple SwiftUI tutorials
  • 感谢您抽出宝贵时间回复,我发现我的代码有问题,实际上不是您指出的任何点(我认为这不是有效点)。

标签: ios swift swiftui


【解决方案1】:

这对我来说是一个愚蠢的错误。问题实际上是 Equatable 的不成熟实现,从 Swift 4.1+ 开始实际上不再需要它(对于 Hashable 也是如此)。我最初添加它是为了确保我可以比较 Cards(需要 index(of:) 方法),但是,由于我只检查比较 id,它与 SwiftUI 用于重绘的内部比较算法混淆了。 SwiftUI 也在使用我的 Equatable 实现,并认为卡片实际上并没有改变!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-11-09
    • 1970-01-01
    • 2022-06-17
    • 2021-04-16
    • 2023-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多