SwiftUI不会在子视图中更新ObservedObject

时间:2020-06-10 09:11:56

标签: swift swiftui

因此,我尝试从Stanford CS193p学习SwiftUI。这很好,但是,我无法理解为什么它不起作用。我和老师的观点完全相同:

struct ContentView: View {

    @ObservedObject var viewModel: EmojiMemoryGame

    var body: some View {
        HStack {
            ForEach(self.viewModel.cards) { card in
                CardView(card: card).onTapGesture {
                    self.viewModel.chooseCard(card: card)
                }
            }
            .aspectRatio(2/3, contentMode: .fit)
        }
            .foregroundColor(.orange)
            .padding()
            .font(viewModel.numberOfPairsOfCards >= 5 ? .callout : .largeTitle)
    }
}

struct CardView: View {

    var card: MemoryGame<String>.Card

    var body: some View {
        VStack {
            ZStack {
                if card.isFaceUp {
                    RoundedRectangle(cornerRadius: 10.0).fill(Color.white)
                    RoundedRectangle(cornerRadius: 10.0).stroke(lineWidth: 3)
                    Text(card.content)
                } else {
                    RoundedRectangle(cornerRadius: 10.0).fill(Color.orange)
                }
            }
        }
    }
}

问题在于,这不会更新视图,就好像没有将来自模型的发布信息向下传递到层次结构中一样。我知道它是可行的,因为如果我将代码更改为此:

struct ContentView: View {

    @ObservedObject var viewModel: EmojiMemoryGame

    var body: some View {
        HStack {
            ForEach(self.viewModel.cards) { card in
                ZStack {
                    if card.isFaceUp {
                        RoundedRectangle(cornerRadius: 10.0).fill(Color.white)
                        RoundedRectangle(cornerRadius: 10.0).stroke(lineWidth: 3)
                        Text(card.content)
                    } else {
                        RoundedRectangle(cornerRadius: 10.0).fill(Color.orange)
                    }
                }
                    .onTapGesture {
                        self.viewModel.chooseCard(card: card)
                    }
            }
            .aspectRatio(2/3, contentMode: .fit)
        }
            .foregroundColor(.orange)
            .padding()
            .font(viewModel.numberOfPairsOfCards >= 5 ? .callout : .largeTitle)
    }
}

一切正常。 感谢所有帮助!

class EmojiMemoryGame: ObservableObject {

    @Published private var game: MemoryGame<String> = EmojiMemoryGame.createMemoryGame()

    static private func createMemoryGame() -> MemoryGame<String> {
        let emojis = ["?", "?", "?", "?", "?"]
        return MemoryGame(numberOfPairsOfCards: Int.random(in: 2...5)) { emojis[$0] }
    }

    //MARK: - Access to the Model
    var cards: Array<MemoryGame<String>.Card> {
        game.cards
    }

    var numberOfPairsOfCards: Int {
        game.cards.count / 2
    }

    //MARK: - Intents

    func chooseCard(card: MemoryGame<String>.Card) {
        game.choose(card)
    }
}

struct MemoryGame<CardContent> {
    var cards: Array<Card>

    mutating func choose(_ card: Card) {
        if let indexOfCard = cards.firstIndex(of: card) {
            cards[indexOfCard].isFaceUp.toggle()
        }
    }

    init(numberOfPairsOfCards: Int, cardContentFactory: (Int) -> CardContent) {
        cards = Array<Card>()
        for pairIndex in 0..<numberOfPairsOfCards {
            let content = cardContentFactory(pairIndex);
            cards.append(Card(content: content, id: pairIndex * 2))
            cards.append(Card(content: content, id: pairIndex * 2 + 1))
        }
        cards.shuffle()
    }

    struct Card: Identifiable, Equatable {

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

        var isFaceUp = true
        var isMatched = false
        var content: CardContent
        var id: Int

    }
}

1 个答案:

答案 0 :(得分:1)

Card结构中的相等运算符的实现仅比较ID。 CardView未更新,因为SwiftUI推断出卡未更改。

请注意,您可能还需要检查card的其他属性(CardContent必须符合Equatable)。

struct Card: Identifiable, Equatable {

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

        var isFaceUp = true
        var isMatched = false
        var content: CardContent
        var id: Int

    }