Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //MemoGameViewModel.Swift
- import SwiftUI
- class MemoGameViewModel: ObservableObject {
- @Published private(set) var model: MemoGameModel<String>
- let theme1Symbols = ["🥷", "🥼", "🙉", "🏀", "🤿", "♌️", "❤️🩹", "🦊"]
- let theme2Symbols = ["🍎", "🍏", "🍋", "🍇", "🍓", "🍉", "🍒", "🍌"]
- let theme3Symbols = ["🚗", "🚕", "🚙", "🚌", "🚑", "🚒", "🚓", "🚜"]
- enum Theme {
- case theme1, theme2, theme3
- }
- @Published var selectedTheme: Theme = .theme1 {
- didSet {
- updateModel() // Aktualizacja modelu po zmianie motywu
- }
- }
- var mainCard: MemoGameModel<String>.Card? {
- model.cards.randomElement() // Losowa karta z obecnego modelu
- }
- var selectedThemeColor: Color {
- switch selectedTheme {
- case .theme1: return .blue
- case .theme2: return .green
- case .theme3: return .red
- }
- }
- init() {
- model = MemoGameViewModel.createGameModel(using: theme1Symbols)
- }
- public var currentThemeSymbols: [String] {
- switch selectedTheme {
- case .theme1: return theme1Symbols
- case .theme2: return theme2Symbols
- case .theme3: return theme3Symbols
- }
- }
- private static func createGameModel(using symbols: [String]) -> MemoGameModel<String> {
- return MemoGameModel<String>(numberOfCards: symbols.count) { index in
- if index < symbols.count {
- return symbols[index]
- } else {
- return "??" // Wartość błędna, gdy indeks jest poza zakresem
- }
- }
- }
- func changeTheme(to theme: Theme) {
- selectedTheme = theme
- updateModel() // Aktualizujemy model przy zmianie tematu
- }
- func updateModel() {
- model = MemoGameViewModel.createGameModel(using: currentThemeSymbols)
- model.resetCards() // Resetowanie stanu kart
- }
- func adjustCardNumber(by offset: Int) {
- let newCardCount = model.cards.count + offset
- if newCardCount >= 1 && newCardCount <= currentThemeSymbols.count { // Minimalna liczba kart to 1
- model = MemoGameViewModel.createGameModel(using: Array(currentThemeSymbols.prefix(newCardCount)))
- model.resetCards() // Resetujemy karty po zmianie liczby kart
- }
- }
- func shuffleCards() {
- model.shuffleCards() // Wywołanie tasowania kart w modelu
- objectWillChange.send() // Wymuszenie odświeżenia widoku
- model.resetCards()
- }
- func choose(_ card: MemoGameModel<String>.Card) {
- model.choose(card) // Obsługa wyboru karty
- }
- }
- //MemoGameModel.swift
- struct MemoGameModel<CardContent> where CardContent: Equatable {
- private(set) var cards: [Card]
- private(set) var mainCard: Card? // Zmienna dla głównej karty
- init(numberOfCards: Int, cardContentFactory: (Int) -> CardContent) {
- cards = []
- for index in 0..<numberOfCards {
- let content = cardContentFactory(index)
- cards.append(Card(id: "\(index)", content: content)) // Każdy `content` jest unikalny
- }
- shuffleCards()
- mainCard = cards.randomElement() // Ustawienie losowej karty jako głównej
- }
- mutating func setMainCard(_ card: Card) {
- mainCard = card
- }
- mutating func choose(_ card: Card) {
- // Znajdź indeks wybranej karty
- guard let chosenIndex = cards.firstIndex(where: { $0.id == card.id }),
- !cards[chosenIndex].isMatched else {
- return
- }
- // Jeśli karta jest już odkryta, nie robimy nic
- if cards[chosenIndex].isFaceUp {
- return
- }
- // Zakrywamy wszystkie karty, z wyjątkiem głównej karty
- for index in cards.indices {
- // Jeśli karta nie jest dopasowana i nie jest główną kartą, ustawiamy ją na zakrytą
- if !cards[index].isMatched && cards[index].id != mainCard?.id {
- cards[index].isFaceUp = false
- }
- }
- // Odkrywamy tylko wybraną kartę
- cards[chosenIndex].isFaceUp = true
- // Sprawdzamy, czy mamy już odkrytą kartę
- if let faceUpIndex = cards.firstIndex(where: { $0.isFaceUp && !$0.isMatched }) {
- let faceUpCard = cards[faceUpIndex]
- // Sprawdzamy, czy karty pasują
- if faceUpCard.content == cards[chosenIndex].content {
- // Karty pasują, oznaczamy je jako dopasowane
- cards[chosenIndex].isMatched = true
- cards[faceUpIndex].isMatched = true
- }
- }
- }
- mutating func resetCards() {
- for index in cards.indices {
- cards[index].isFaceUp = false
- cards[index].isMatched = false
- }
- // Ustawienie głównej karty jako odkrytej
- if let mainCard = mainCard, let index = cards.firstIndex(where: { $0.id == mainCard.id }) {
- cards[index].isFaceUp = true
- }
- }
- mutating func shuffleCards() {
- cards.shuffle()
- // Przywróć główną kartę na początek, aby była odkryta
- if let mainCard = mainCard, let index = cards.firstIndex(where: { $0.id == mainCard.id }) {
- cards.remove(at: index)
- cards.insert(mainCard, at: 0)
- }
- // Ustawiamy wszystkie karty na zakryte, oprócz głównej
- for index in cards.indices {
- cards[index].isFaceUp = false
- cards[index].isMatched = false
- }
- // Ustawienie głównej karty jako odkrytej
- if let mainCard = mainCard, let index = cards.firstIndex(where: { $0.id == mainCard.id }) {
- cards[index].isFaceUp = true
- }
- }
- func index(of card: Card) -> Int? {
- cards.firstIndex { $0.id == card.id }
- }
- func indexOfFacedUpCard(excluding card: Card) -> [Int] {
- cards.indices.filter { cards[$0].isFaceUp && cards[$0] != card }
- }
- mutating func changeVisible() {
- for index in cards.indices {
- if !cards[index].isMatched {
- cards[index].visible.toggle()
- }
- }
- }
- func getNumberCard(card: Card) -> String {
- String(card.id.dropLast())
- }
- struct Card: Identifiable, Equatable {
- let id: String // Typ String
- var isFaceUp = false
- var isMatched = false
- let content: CardContent
- var visible: Bool = true
- }
- }
- //ContentView.swift (ma Card!)
- import SwiftUI
- // 6.6 - poprawki (fix: przy tasowaniu pierwsza karta zawsze odkryta,
- // caly czas sie roluje glowna, moze byc wiecej niz jedna odkryta)
- struct ContentView: View {
- @StateObject var viewModel = MemoGameViewModel() // Adnotacja @ObservedObject
- var columns: [GridItem] {
- [GridItem(.adaptive(minimum: 85), spacing: -30)] // Modyfikator adaptive z minimum 85, spacing 0
- }
- var body: some View {
- VStack {
- Text("Memo")
- .font(.largeTitle)
- .padding(.top, 20)
- .foregroundColor(viewModel.selectedThemeColor) // Kolor zgodny z motywem
- //Spacer()
- mainCard // Wyświetlanie głównej karty
- ScrollView {
- cardDisplay
- }
- HStack {
- ThemeButton(title: "Motyw 1", icon: "🥎", action: { viewModel.changeTheme(to: .theme1) }, color: viewModel.selectedThemeColor)
- Spacer()
- ThemeButton(title: "Motyw 2", icon: "🏀", action: { viewModel.changeTheme(to: .theme2) }, color: viewModel.selectedThemeColor)
- Spacer()
- ThemeButton(title: "Motyw 3", icon: "⚽️", action: { viewModel.changeTheme(to: .theme3) }, color: viewModel.selectedThemeColor)
- }
- .padding(.horizontal)
- HStack {
- adjustCardNumberButton(by: -1, symbol: "minus", isEnabled: viewModel.model.cards.count > 1)
- Spacer()
- adjustCardNumberButton(by: 1, symbol: "plus", isEnabled: viewModel.model.cards.count < 8)
- Spacer()
- shuffleButton // Przycisk do tasowania kart
- }
- .padding(.horizontal)
- }
- .onAppear {
- viewModel.updateModel()
- }
- }
- var cardDisplay: some View {
- LazyVGrid(columns: columns, spacing: 0) { // Spacing 0 dla siatki
- ForEach(viewModel.model.cards) { card in
- CardView(card: card, cardColor: .constant(viewModel.selectedThemeColor))
- .onTapGesture {
- viewModel.choose(card)
- }
- .padding(5) // Padding dla każdej karty
- }
- }
- .padding()
- }
- var mainCard: some View {
- if let mainCard = viewModel.mainCard {
- var mainCardCopy = mainCard
- mainCardCopy.isFaceUp = true // Zawsze ustawiamy główną kartę jako odkrytą
- return AnyView(
- CardView(card: mainCardCopy, cardColor: .constant(viewModel.selectedThemeColor))
- .padding()
- .frame(width: 180, height: 270)
- .aspectRatio(2 / 3, contentMode: .fit)
- )
- } else {
- return AnyView(EmptyView())
- }
- }
- private func adjustCardNumberButton(by offset: Int, symbol: String, isEnabled: Bool) -> some View {
- Button(action: {
- viewModel.adjustCardNumber(by: offset)
- }) {
- Image(systemName: symbol == "plus" ? "plus" : "minus")
- .font(.title)
- .padding(10)
- .background(Color.white)
- .overlay(
- RoundedRectangle(cornerRadius: 8)
- .stroke(viewModel.selectedThemeColor, lineWidth: 2)
- )
- }
- .disabled(!isEnabled)
- .foregroundColor(isEnabled ? viewModel.selectedThemeColor : .gray)
- }
- private var shuffleButton: some View { // Przycisk do tasowania kart
- Button(action: {
- viewModel.shuffleCards() // Poprawione wywołanie metody
- }) {
- Text("Tasuj")
- .font(.headline)
- .padding()
- .background(Color.white)
- .cornerRadius(8)
- .overlay(
- RoundedRectangle(cornerRadius: 8)
- .stroke(viewModel.selectedThemeColor, lineWidth: 2)
- )
- }
- .foregroundColor(viewModel.selectedThemeColor)
- }
- }
- //CardView.swift
- import SwiftUI
- struct CardView: View {
- let card: MemoGameModel<String>.Card // Stała karta zdefiniowana w modelu
- @Binding var cardColor: Color
- var body: some View {
- if card.visible {
- ZStack {
- RoundedRectangle(cornerRadius: 12, style: .continuous)
- .fill(card.isFaceUp ? Color.white : cardColor)
- .frame(height: 100)
- .aspectRatio(2 / 3, contentMode: .fit)
- if card.isFaceUp {
- Text(card.content)
- .font(.system(size: 200)) // Ustawienie dużego rozmiaru czcionki
- .minimumScaleFactor(0.01) // Zmniejszenie rozmiaru w razie potrzeby
- .aspectRatio(contentMode: .fit) // Dopasowanie proporcji
- .foregroundColor(cardColor)
- RoundedRectangle(cornerRadius: 12, style: .continuous)
- .stroke(cardColor, lineWidth: 2)
- }
- }
- .opacity(card.isFaceUp ? 1 : 0.7) // Przezroczystość dla zakrytych kart
- .padding()
- .animation(.default, value: card.isFaceUp)
- }
- }
- }
- //Themes.swift
- import SwiftUI
- struct Theme {
- var color: Color
- static let theme1 = Theme(color: Color.blue)
- static let theme2 = Theme(color: Color.green)
- static let theme3 = Theme(color: Color.red)
- }
- struct ThemeButton: View {
- let title: String
- let icon: String
- let action: ()->Void
- let color: Color
- var body: some View {
- Button(action: action) {
- VStack {
- Text(icon) // Ikona
- .font(.largeTitle) // Rozmiar czcionki dla ikony
- .padding() // Padding wokół ikony
- .background(Color.white) // Tło przycisku
- .clipShape(Circle()) // Kształt okręgu
- .overlay(
- Circle() // Obramowanie
- .stroke(color, lineWidth: 2)
- )
- Text(title) // Tekst przycisku
- .font(.caption) // Mniejszy rozmiar czcionki dla tekstu
- .foregroundColor(.black) // Kolor tekstu
- }
- }
- }
- }
- //Item.swift
- import Foundation
- import SwiftData
- @Model
- final class Item {
- var timestamp: Date
- init(timestamp: Date) {
- self.timestamp = timestamp
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement