【发布时间】:2021-08-16 04:12:32
【问题描述】:
我正在失去理智,请帮忙
我正在关注斯坦福的 iOS 教程,我正在尝试完成创建纸牌游戏的任务,我有 3 个模型,游戏、纸牌、主题和主题:
Game和Card负责主要的游戏逻辑
import Foundation
struct Game {
var cards: [Card]
var score = 0
var isGameOver = false
var theme: Theme
var choosenCardIndex: Int?
init(theme: Theme) {
cards = []
self.theme = theme
startTheme()
}
mutating func startTheme() {
cards = []
var contentItems: [String] = []
while contentItems.count != theme.numberOfPairs {
let randomElement = theme.emojis.randomElement()!
if !contentItems.contains(randomElement) {
contentItems.append(randomElement)
}
}
let secondContentItems: [String] = contentItems.shuffled()
for index in 0..<theme.numberOfPairs {
cards.append(Card(id: index*2, content: contentItems[index]))
cards.append(Card(id: index*2+1, content: secondContentItems[index]))
}
}
mutating func chooseCard(_ card: Card) {
print(card)
if let foundIndex = cards.firstIndex(where: {$0.id == card.id}),
!cards[foundIndex].isFaceUp,
!cards[foundIndex].isMatchedUp
{
if let potentialMatchIndex = choosenCardIndex {
if cards[foundIndex].content == cards[potentialMatchIndex].content {
cards[foundIndex].isMatchedUp = true
cards[potentialMatchIndex].isMatchedUp = true
}
choosenCardIndex = nil
} else {
for index in cards.indices {
cards[index].isFaceUp = false
}
}
cards[foundIndex].isFaceUp.toggle()
}
print(card)
}
mutating func endGame() {
isGameOver = true
}
mutating func penalizePoints() {
score -= 1
}
mutating func awardPoints () {
score += 2
}
struct Card: Identifiable, Equatable {
static func == (lhs: Game.Card, rhs: Game.Card) -> Bool {
return lhs.content == rhs.content
}
var id: Int
var isFaceUp: Bool = false
var content: String
var isMatchedUp: Bool = false
var isPreviouslySeen = false
}
}
主题用于建模不同类型的内容,主题用于跟踪当前正在使用的内容并获取新内容
import Foundation
import SwiftUI
struct Theme: Equatable {
static func == (lhs: Theme, rhs: Theme) -> Bool {
return lhs.name == rhs.name
}
internal init(name: String, emojis: [String], numberOfPairs: Int, cardsColor: Color) {
self.name = name
self.emojis = Array(Set(emojis))
if(numberOfPairs > emojis.count || numberOfPairs < 1) {
self.numberOfPairs = emojis.count
} else {
self.numberOfPairs = numberOfPairs
}
self.cardsColor = cardsColor
}
var name: String
var emojis: [String]
var numberOfPairs: Int
var cardsColor: Color
}
import Foundation
struct Themes {
private let themes: [Theme]
public var currentTheme: Theme?
init(_ themes: [Theme]) {
self.themes = themes
self.currentTheme = getNewTheme()
}
private func getNewTheme() -> Theme {
let themesIndexes: [Int] = Array(0..<themes.count)
var visitedIndexes: [Int] = []
while(visitedIndexes.count < themesIndexes.count) {
let randomIndex = Int.random(in: 0..<themes.count)
let newTheme = themes[randomIndex]
if newTheme == currentTheme {
visitedIndexes.append(randomIndex)
} else {
return newTheme
}
}
return themes.randomElement()!
}
mutating func changeCurrentTheme() -> Theme {
self.currentTheme = getNewTheme()
return self.currentTheme!
}
}
这是我的虚拟机:
class GameViewModel: ObservableObject {
static let numbersTheme = Theme(name: "WeirdNumbers", emojis: ["1", "2", "4", "9", "20", "30"], numberOfPairs: 6, cardsColor: .pink)
static let emojisTheme = Theme(name: "Faces", emojis: ["????", "????", "????", "????", "????", "????", "????", "????"], numberOfPairs: 8, cardsColor: .blue)
static let carsTheme = Theme(name: "Cars", emojis: ["????", "????️", "????", "????", "????", "????", "????", "????"], numberOfPairs: 20, cardsColor: .yellow)
static let activitiesTheme = Theme(name: "Activities", emojis: ["????", "????️", "????♂️", "????", "????♂️", "????️", "????♂️"], numberOfPairs: -10, cardsColor: .green)
static let fruitsTheme = Theme(name: "Fruits", emojis: ["????", "????", "????", "????", "????", "????", "????", "????"], numberOfPairs: 5, cardsColor: .purple)
static var themes = Themes([numbersTheme, emojisTheme, carsTheme, fruitsTheme])
static func createMemoryGame() -> Game {
Game(theme: themes.currentTheme!)
}
@Published private var gameController: Game = Game(theme: themes.currentTheme!)
func createNewGame() {
gameController.theme = GameViewModel.themes.changeCurrentTheme()
gameController.startTheme()
}
func choose(_ card: Game.Card) {
objectWillChange.send()
gameController.chooseCard(card)
}
var cards: [Game.Card] {
return gameController.cards
}
var title: String {
return gameController.theme.name
}
var color: Color {
return gameController.theme.cardsColor
}
}
这是我的观点:
struct ContentView: View {
var columns: [GridItem] = [GridItem(.adaptive(minimum: 90, maximum: 400))]
@ObservedObject var ViewModel: GameViewModel
var body: some View {
VStack {
HStack {
Spacer()
Button(action: {
ViewModel.createNewGame()
}, label: {
VStack {
Image(systemName: "plus")
Text("New game")
.font(/*@START_MENU_TOKEN@*/.caption/*@END_MENU_TOKEN@*/)
}
})
.font(/*@START_MENU_TOKEN@*/.title/*@END_MENU_TOKEN@*/)
.padding(.trailing)
}
Section {
VStack {
Text(ViewModel.title)
.foregroundColor(/*@START_MENU_TOKEN@*/.blue/*@END_MENU_TOKEN@*/)
.font(/*@START_MENU_TOKEN@*/.title/*@END_MENU_TOKEN@*/)
}
}
ScrollView {
LazyVGrid(columns: columns ) {
ForEach(ViewModel.cards, id: \.id) { card in
Card(card: card, color: ViewModel.color)
.aspectRatio(2/3, contentMode: .fit)
.onTapGesture {
ViewModel.choose(card)
}
}
}
.font(.largeTitle)
}
.padding()
Text("Score")
.frame(maxWidth: .infinity, minHeight: 30)
.background(Color.blue)
.foregroundColor(/*@START_MENU_TOKEN@*/.white/*@END_MENU_TOKEN@*/)
Spacer()
HStack {
Spacer()
Text("0")
.font(.title2)
.bold()
Spacer()
}
}
}
}
struct Card: View {
let card: Game.Card
let color: Color
var body: some View {
ZStack {
let shape = RoundedRectangle(cornerRadius: 10)
if card.isFaceUp {
Text(card.content)
shape
.strokeBorder()
.accentColor(color)
.foregroundColor(color)
}
else {
shape
.fill(color)
}
}
}
}
基本上问题出在
.onTapGesture {
ViewModel.choose(card)
}
在 View 中,当有人点击卡片时,卡片的 isFaceUp 属性会更改为 true,但这不会反映在 UI 中。 如果我通过更改主题和添加新卡片来生成新视图,则可以。
Button(action: {
ViewModel.createNewGame()
}, label: {
VStack {
Image(systemName: "plus")
Text("New game")
.font(/*@START_MENU_TOKEN@*/.caption/*@END_MENU_TOKEN@*/)
}
})
但是当我尝试翻转卡片时它不起作用,游戏模型中的值发生了变化,但视图上没有更新
点击后 ViewModel 调用选择方法
func choose(_ card: Game.Card) {
gameController.chooseCard(card)
}
这通过调用chooseCard方法改变了Game.swift文件中Model的值
mutating func chooseCard(_ card: Card) {
print(card)
if let foundIndex = cards.firstIndex(where: {$0.id == card.id}),
!cards[foundIndex].isFaceUp,
!cards[foundIndex].isMatchedUp
{
if let potentialMatchIndex = choosenCardIndex {
if cards[foundIndex].content == cards[potentialMatchIndex].content {
cards[foundIndex].isMatchedUp = true
cards[potentialMatchIndex].isMatchedUp = true
}
choosenCardIndex = nil
} else {
for index in cards.indices {
cards[index].isFaceUp = false
}
}
cards[foundIndex].isFaceUp.toggle()
}
print(card)
}
值改变但视图不变,GameViewModel的gameController变量有@Published状态,它指向一个Game模型结构体的实例
@Published private var gameController: Game = Game(theme: themes.currentTheme!)
它使用 @ObservedObject 属性访问此 GameViewModel 的视图
@ObservedObject var ViewModel: GameViewModel
我以为我做的一切都是对的,但我想不是,哈哈,我到底做错了什么?如果我在 ViewModel 上使用已发布和可观察的对象,为什么无法更新我的视图?哈哈
【问题讨论】:
-
您在换卡之前先发送更改
-
@Paulw11 我不明白,你是什么意思?
-
你打电话给
objectWillChange.send()然后然后你换卡。尝试颠倒choose(_ card:)中这两行的顺序 -
我这样做了,但它不起作用,实际上我认为甚至不需要 objectWillChange.send() 因为我的控制器具有在更改时应该更新的数据数组视图有@Published 关键字,类有ObservableObject 协议,所以idk,还有什么问题?
-
我看过 Stanford 的视频,看到他的 app 可以工作,但是我不知道怎么做,因为 @Published 属性和视图之间没有绑定;你所看到的正是我所期望的