feat(view, viewmodel): 修改 veiwmodel 发送用户点击事件,修改 card 状态,完成 card match 逻辑

点击 card 修改 card.isFaceUp 属性,当两个 card.content 相同时,将 card 隐藏

Signed-off-by: Ching <loooching@gmail.com>
This commit is contained in:
Ching 2023-02-12 20:50:41 +08:00
parent 87b6465e68
commit 189bfc0bb8
4 changed files with 65 additions and 38 deletions

View File

@ -8,56 +8,47 @@
import SwiftUI import SwiftUI
struct ContentView: View { struct ContentView: View {
var emojis = ["🚌", "🚙", "🚗", "🚕", "🏎", "🚎", "🚓"] @ObservedObject var viewModel: EmojiMemoryGame
@State var emojiCount = 2
var body: some View { var body: some View {
VStack {
ScrollView { ScrollView {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 65))]) { LazyVGrid(columns: [GridItem(.adaptive(minimum: 65))]) {
ForEach(emojis[0 ..< emojiCount], id: \.self) { ForEach(viewModel.cards) {
emoji in CardView(content: emoji).aspectRatio(2 / 3, contentMode: .fit) card in CardView(card: card).aspectRatio(2 / 3, contentMode: .fit)
.onTapGesture {
viewModel.choose(card)
}
} }
} }
} }
.foregroundColor(/*@START_MENU_TOKEN@*/ .red/*@END_MENU_TOKEN@*/) .foregroundColor(/*@START_MENU_TOKEN@*/ .red/*@END_MENU_TOKEN@*/)
.padding(.horizontal) .padding(.horizontal)
.font(.largeTitle)
}
.padding(.horizontal)
} }
} }
struct CardView: View { struct CardView: View {
@State var isFaceUp: Bool = true let card: MemoryGame<String>.Card
var content: String
var body: some View { var body: some View {
ZStack { ZStack {
let shape = RoundedRectangle(cornerRadius: 20.0) let shape = RoundedRectangle(cornerRadius: 20.0)
if isFaceUp { if card.isFaceUp {
shape shape.fill().foregroundColor(.white)
.fill() shape.strokeBorder(lineWidth: 3)
.foregroundColor(.white) Text(card.content).font(.largeTitle)
shape } else if card.isMatched {
.strokeBorder(lineWidth: 3) shape.opacity(0)
Text(content) } else {
.font(.largeTitle) shape.fill()
} }
else {
shape
.fill()
}
}
.onTapGesture {
isFaceUp = !isFaceUp
} }
} }
} }
struct ContentView_Previews: PreviewProvider { struct ContentView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
ContentView() let game = EmojiMemoryGame()
ContentView(viewModel: game)
.preferredColorScheme(.dark) .preferredColorScheme(.dark)
ContentView() ContentView(viewModel: game)
.preferredColorScheme(.light) .preferredColorScheme(.light)
} }
} }

View File

@ -8,16 +8,22 @@
import SwiftUI import SwiftUI
// viewmodel // viewmodel
class EmojiMemoryGame { class EmojiMemoryGame: ObservableObject{
static let emojis = ["🚌", "🚙", "🚗", "🚕", "🏎", "🚎", "🚓"] static let emojis = ["🚌", "🚙", "🚗", "🚕", "🏎", "🚎", "🚓"]
static func createMemoryGame() -> MemoryGame<String> { static func createMemoryGame() -> MemoryGame<String> {
MemoryGame<String>(numberOfPairsOfCards: 5) { pairIndex in EmojiMemoryGame.emojis[pairIndex] } MemoryGame<String>(numberOfPairsOfCards: 5) { pairIndex in EmojiMemoryGame.emojis[pairIndex] }
} }
private var model: MemoryGame<String> = createMemoryGame() @Published private var model: MemoryGame<String> = createMemoryGame()
var cards: [MemoryGame<String>.Card] { var cards: [MemoryGame<String>.Card] {
return model.cards return model.cards
} }
// MARK: - Intent(s)
func choose(_ card: MemoryGame<String>.Card) {
model.choose(card)
}
} }

View File

@ -9,9 +9,10 @@ import SwiftUI
@main @main
struct MemorizeApp: App { struct MemorizeApp: App {
let game = EmojiMemoryGame()
var body: some Scene { var body: some Scene {
WindowGroup { WindowGroup {
ContentView() ContentView(viewModel: game)
} }
} }
} }

View File

@ -8,24 +8,53 @@
import Foundation import Foundation
// model // model
struct MemoryGame<CardContent> { struct MemoryGame<CardContent> where CardContent: Equatable {
private(set) var cards: [Card] private(set) var cards: [Card]
private var indexOfTheOneAndOnlyFaceUpCard: Int?
func choose(_ card: Card) {} mutating func choose(_ card: Card) {
if let chosenIndex = cards.firstIndex(where: { $0.id == card.id }), !cards[chosenIndex].isFaceUp, !cards[chosenIndex].isMatched {
if let potentialMatchIndex = indexOfTheOneAndOnlyFaceUpCard {
if cards[chosenIndex].content == cards[potentialMatchIndex].content {
cards[chosenIndex].isMatched = true
cards[potentialMatchIndex].isMatched = true
}
indexOfTheOneAndOnlyFaceUpCard = nil
} else {
for index in cards.indices {
if cards[index].isFaceUp {
cards[index].isFaceUp.toggle()
}
}
indexOfTheOneAndOnlyFaceUpCard = chosenIndex
}
cards[chosenIndex].isFaceUp.toggle()
}
}
func index(of card: Card) -> Int? {
for index in 0 ..< cards.count {
if cards[index].id == card.id {
return index
}
}
return nil
}
init(numberOfPairsOfCards: Int, createCardContent: (Int) -> CardContent) { init(numberOfPairsOfCards: Int, createCardContent: (Int) -> CardContent) {
cards = [Card]() cards = [Card]()
// //
for pairIndex in 0 ..< numberOfPairsOfCards { for pairIndex in 0 ..< numberOfPairsOfCards {
let content: CardContent = createCardContent(pairIndex) let content: CardContent = createCardContent(pairIndex)
cards.append(Card(content: content)) cards.append(Card(content: content, id: pairIndex * 2))
cards.append(Card(content: content)) cards.append(Card(content: content, id: pairIndex * 2 + 1))
} }
} }
struct Card { struct Card: Identifiable {
var isFaceUp: Bool = false var isFaceUp: Bool = false
var isMatched: Bool = false var isMatched: Bool = false
var content: CardContent var content: CardContent
var id: Int
} }
} }