0

I have a card game, where user guesses two pairs of similar cards by flipping them on click. The issue is when user selects CardType or CardColor in editViewController, there is no any changes in BoardGameController.

My code is based on MVK: Model: Card.swift

import UIKit

// types of figure
enum CardType: CaseIterable {
    case circle
    case transparent
    case cross
    case square
    case fill
}

// Color of card
enum CardColor: CaseIterable {
    case red
    case green
    case black
    case gray
    case brown
    case yellow
    case purple
    case orange
   
}

// Game card
typealias Card = (type: CardType, color: CardColor)

Model: Game.swift

import Foundation


class Game {
    // number of pairs of unique cards
    var cardsCount = 0
    // array of generated cards
    var cards = [Card]()// Array of Card objects instead of [(type: CardType, color: CardColor)]
    
        
    // generation array of random cards
    func generateCards() {
        // generating new array of cards
        var  cards = [Card]()
        for _ in 0...cardsCount {
        let randomElement = (type: CardType.allCases.randomElement()!, color: CardColor.allCases.randomElement()!)
        
            cards.append(randomElement)
        }
        
        self.cards = cards
        // Add selected card type and color to the cards array

    }
    
    // equivalence check of cards
    func checkCards(_ firstCard: Card, _ secondCard: Card) -> Bool {
        if firstCard == secondCard {
            return true
        }
        
        return false
    }
    
}

Controllers: BoardGameViewController

import UIKit

class BoardGameController: UIViewController  {
   
   
    override func viewDidLoad() {
        super.viewDidLoad()

        // adding Start button to the scene
        view.addSubview(startButtonView)
        
        // adding game field on tthe scene
        view.addSubview(boardGameView)
        
        view.addSubview(flippButtonView)
        view.backgroundColor =  .white
        
        // adding Edit button
        view.addSubview(editButtonView)

    }

    // number pair of unique cards
    var cardsPairCounts = 8
    
    
    // essence "Game"
    lazy var game: Game = getNewGame()
    
    

    private func getNewGame() -> Game {
        let game = Game()
        game.cardsCount = self.cardsPairCounts - 1
       
        game.generateCards()
      
        return game
        
    }
    
    
   private var flippedCards = [UIView]()
    
    // Button for lounch and update the gaame
   // lazy  var startButtonView  =  getStartButtonView()
    lazy var startButtonView: UIButton = {
        let button = getStartButtonView()
        button.addTarget(self, action: #selector(startGame(_:)), for: .touchUpInside)
        return button
    }()
    
    func getStartButtonView() -> UIButton {
        // 1
        // creating button
        let button = UIButton(frame: CGRect(x: 0, y: 0, width: 200, height: 50))
        // 2
        // changing position of button
        button.center.x = view.center.x
        
        //getting access to the current window
        let window = UIApplication.shared.windows[0]
        // determine  indent of upp from border of window to the Save Area
        let topPadding = window.safeAreaInsets.top
        // setting coordinates  Y of button according to indent
        button.frame.origin.y = topPadding
        
        //3
        // setting up appearance of button
        
        // set text
        button.setTitle("Start the game", for: .normal)
        // setting color for usual(untouched) state of button
        button.setTitleColor(.black, for: .normal)
        // setting color for touched sttate of button
        button.setTitleColor(.gray, for: .highlighted)
        
        // setting background color
        button.backgroundColor = .systemGray4
        // round  off corners
        button.layer.cornerRadius = 10
        
        
        // Conecting the handler of press putton
        button.addTarget(nil, action: #selector(startGame(_:)), for: .touchUpInside)
        
        return button
        
        
    }
    
    
    // connection to the button touch
    @objc func startGame(_ sender: UIButton) {
     
        game = getNewGame()
       
        let cards = getCardsBy(modelData: game.cards)
        placeCardsOnBoard(cards)
    }
    
   
    
    // Buttton of Flipping all cards
    // Button for lounch and flipp the  all cards
    lazy var flippButtonView  =  getFlippButtonView()
    
    func getFlippButtonView() -> UIButton {
        // 1
        // creating button
        let button = UIButton(frame: CGRect(x: 0, y: 0, width: 200, height: 50))
        // 2
        // changing position of button
        button.center.x = view.center.x
        
        //getting access to the current window
        let window = UIApplication.shared.windows[0]
        // determine  indent of upp from border of window to the Save Area
        let bottomPadding = window.safeAreaInsets.bottom
        // setting coordinates  Y of button according to indent
        //button.frame.origin.y = boardGameView.frame.origin.y + boardGameView.frame.size.height + 10
     
        button.frame.origin.y = UIScreen.main.bounds.height - button.frame.height - bottomPadding
        //3
        // setting up appearance of button
        
        // set text
        button.setTitle("Flipp cards", for: .normal)
        // setting color for usual(untouched) state of button
        button.setTitleColor(.black, for: .normal)
        // setting color for touched sttate of button
        button.setTitleColor(.gray, for: .highlighted)
        
        // setting background color
        button.backgroundColor = .systemGray4
        // round  off corners
        button.layer.cornerRadius = 10
        
        
        // Conecting the handler of press putton
        button.addTarget(nil, action: #selector(flippAllCards(_:)), for: .touchUpInside)
        
        return button
        
        
    }
    
 
    // connection to the flipp button touch
    @objc func flippAllCards(_ sender: UIButton) {
        let shouldFlipAllCards = flippedCards.isEmpty || flippedCards.count == cardViews.count
            
            for card in cardViews {
                let cardView = card as! FlippableView
                
                if shouldFlipAllCards {
                    cardView.flip()
                } else if cardView.isFlipped {
                    cardView.flip()
                }
            }
            
            flippedCards = shouldFlipAllCards ? cardViews : []
    }
    
    
   // Setting Button
    lazy var editButtonView  =  getEditButtonView()

    func getEditButtonView() -> UIButton {
        // 1
        // creating button
        let button = UIButton(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
        // 4. Position the button using frame-based approach
           let window = UIApplication.shared.windows[0]
          
           let rightPadding = window.safeAreaInsets.right
           
           let buttonX = view.frame.width - rightPadding - button.frame.width - 16
           let anotherElementY = boardGameView.frame.origin.y // Adjust this line based on the desired element
           let buttonY = anotherElementY - button.frame.height - 10 // Place the button 10 points above the other element
           
           button.frame.origin = CGPoint(x: buttonX, y: buttonY)
                

        //3
        // setting up appearance of button
        
        // set text
        button.setTitle("Edit", for: .normal)
        // setting color for usual(untouched) state of button
        button.setTitleColor(UIColor(red: 0.30, green: 0.50, blue: 0.40, alpha: 0.3), for: .normal)
        // setting color for touched sttate of button
        button.setTitleColor(UIColor(red: 0.1, green: 0.9, blue: 0.1, alpha: 0.3), for: .highlighted)
        
        // setting background color
        button.backgroundColor = .systemGray4
        // round  off corners
        button.layer.cornerRadius = 10
        
        
        // Conecting the handler of press putton
        button.addTarget(self, action: #selector(editGame(_:)), for: .touchUpInside)
        
        return button
        
        
    }

   // var game1: Game?
    
    @objc func editGame(_ sender: UIButton) {
        
        let targetViewController = EditViewController()
        targetViewController.modalPresentationStyle = .fullScreen
    
        
        targetViewController.numberOfPairsCallback = { [weak self] numberOfPairs in
            self?.cardsPairCounts = numberOfPairs
           
        }
    
        present(targetViewController, animated: true, completion: nil)

    }

    //Game field
    lazy var boardGameView = getBoardGameView()
    
    private func getBoardGameView() -> UIView {
        // indent of game field from closest item
        let margin: CGFloat = 10
        let boardView = UIView()
        
        // indicating coordinates
        // x
        boardView.frame.origin.x = margin
        // y
        let window = UIApplication.shared.windows[0]
        let topPadding = window.safeAreaInsets.top
        boardView.frame.origin.y = topPadding + startButtonView.frame.height + margin
        
       
        // calculating width
        boardView.frame.size.width = UIScreen.main.bounds.width - margin*2
        // calculating height
        // with lowest indent
        let bottomPadding = window.safeAreaInsets.bottom
        //boardView.frame.size.height = UIScreen.main.bounds.height - boardView.frame.origin.y - margin - bottomPadding
        boardView.frame.size.height = UIScreen.main.bounds.height - boardView.frame.origin.y - margin - bottomPadding - 50
        // change style of game field
        boardView.layer.cornerRadius = 5
        boardView.backgroundColor = UIColor(red: 0.1, green: 0.9, blue: 0.1, alpha: 0.3)
        
        
        // Add boardView to the view hierarchy
                view.addSubview(boardView)
                
               
        
        return boardView
        
    }
 
    // pressing button to get arrays of views based on Model
    private func getCardsBy(modelData: [Card]) -> [UIView] {
    
        // storage for views of cards
        var cardViews = [UIView]()
        //  factory of cards
        let  cardViewFactory = CardViewFactory()
        
       

        // sort out array of cards in the Model
                for (index, modelCard) in modelData.enumerated() {
                    // adding first instance of cards
                    let cardOne = cardViewFactory.get(modelCard.type, withSize: cardSize, andColor: modelCard.color)
                    cardOne.tag  = index
                    cardViews.append(cardOne)
                    
                    // adding second instance of cards
                    let  cardTwo = cardViewFactory.get(modelCard.type, withSize: cardSize, andColor: modelCard.color)
                    cardTwo.tag = index
                    cardViews.append(cardTwo)
                    
                }
                

        
        

        
        for  card in cardViews {
            (card as! FlippableView).flipCompletionHandler = { [self] flippedCard in
                // transfer a card  to upp of hierarchy
                flippedCard.superview?.bringSubviewToFront(flippedCard)
                     
                // add or delete cards
                if flippedCard.isFlipped {
                    self.flippedCards.append(flippedCard)
                } else {
                    if let cardIndex = self.flippedCards.firstIndex(of: flippedCard) {
                        self.flippedCards.remove(at: cardIndex)
                    }
                }
  
                // if flippet 2 cards
                if self.flippedCards.count == 2 {
                    // get  cards from data Model
                    let firstCard = game.cards[self.flippedCards.first!.tag]
                    let secondCard = game.cards[self.flippedCards.last!.tag]
                    
                    // if cards are identical
                    if game.checkCards(firstCard, secondCard) {
                        // at first animationaly hidding them
                        UIView.animate(withDuration: 0.3, animations: {
                            self.flippedCards.first!.layer.opacity = 0
                            self.flippedCards.last!.layer.opacity = 0
                            // after that deleting them
                        }, completion: {_  in
                            self.flippedCards.first!.removeFromSuperview()
                            self.flippedCards.last!.removeFromSuperview()
                            self.flippedCards = []
                            
                        })
                        
                        // Otherwise
                    }  else {
                        // flipping cards shirt upp
                            for card in self.flippedCards {
                                (card as! FlippableView).flip()
                            }
                        }
                    }
                }
                
            }
            
            //  adding all
            return cardViews
        }
        
    
    
    // size and positions of cards
    private var cardSize: CGSize {
        CGSize(width: 80, height: 120)
    }
    
    // limit  coordinaates of placement cards
    private var cardMaxXCoordinate: Int {
       Int(boardGameView.frame.width - cardSize.width)
  
    }

    private var cardMaxYCoordinate: Int {
       Int(boardGameView.frame.height - cardSize.height)
        
    }
  
    var cardViews  = [UIView]()
    
    private func placeCardsOnBoard(_ cards: [UIView])  {
        // deleting all aviable cardson the field
        cardViews.forEach{ $0.removeFromSuperview() }
        cardViews = cards
        // sort out cards
        for card in cardViews {
           
            // for each cards generating random coordinates
           let randomXCoordinate = Int.random(in: 0...cardMaxXCoordinate)
           let randomYCoordinate = Int.random(in: 0...cardMaxYCoordinate)
            

            card.frame.origin = CGPoint(x: randomXCoordinate, y: randomYCoordinate)
            
            // placing card on the field
            boardGameView.addSubview(card)
            
        }
        
        
    }
    
   
}


Controller: EditViewController

import UIKit


class EditViewController: UIViewController {
   
   
    var game: Game? // Add a reference to the Game object
    var numberOfPairsCallback: ((Int) -> Void)? // Блок обратного вызова

    
    lazy  var backButtonView  =  getBackButtonView()
    
    func getBackButtonView()  -> UIButton {
        // create "Back" button with symbol Image
        let backButton = UIButton(type: .system)
        backButton.setImage(UIImage(systemName: "chevron.left"), for: .normal)
        backButton.setTitle("Back", for: .normal)
        backButton.addTarget(self, action: #selector(backButtonTapped), for: .touchUpInside)
        view.addSubview(backButton)
        //settin constraintsfor button
        backButton.translatesAutoresizingMaskIntoConstraints  = false
        backButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16).isActive = true
        backButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16).isActive = true
        
        return backButton
    }
    
    
    
    
    // connection to the button touch
    @objc func backButtonTapped() {
        // click handling
        // closing current Scene
        dismiss(animated: true, completion: nil)
        
    }
    
    
    // Metod dlya sohraneniya zadachi
    // Button for lounch and update the gaame
    lazy  var saveButtonView  =  saveButton()
        func saveButton() -> UIButton {
            let saveButton = UIButton(type: .system)
            saveButton.setTitle("Save", for: .normal)
            saveButton.addTarget(self, action: #selector(saveButtonTapped), for: .touchUpInside)
            saveButton.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(saveButton)
            
            saveButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10).isActive = true
            saveButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true
            
            return saveButton
       }

    
    
    
    // connection to the button touch
    @objc func saveButtonTapped() {
        
        dismiss(animated: true, completion: nil)
        
        
    }

    // Button for lounch and update the gaame
    
    let labelOfSelectPairCard: UILabel? = nil
    
    func selectPairCards() -> UILabel {
       
        let selectPair = UILabel()
        selectPair.translatesAutoresizingMaskIntoConstraints = false
        selectPair.text = "Select pair of cards"
        self.view.addSubview(selectPair)
        
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(selectCardPairsButtonTapped(_:)))
        selectPair.isUserInteractionEnabled = true
        selectPair.addGestureRecognizer(tapGesture)
        NSLayoutConstraint.activate([
            selectPair.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20),
            selectPair.topAnchor.constraint(equalTo: backButtonView.topAnchor, constant: 40)
       

                ])
        return selectPair
        
    }

    // Selection pair of alike cards funcs of first label
        @objc func selectCardPairsButtonTapped(_ gestureRecognizer: UITapGestureRecognizer ) {
                let alertController = UIAlertController(title: "Select Card Pairs", message: nil, preferredStyle: .alert)
                alertController.addTextField { textField in
                    textField.placeholder = "Enter the number of pairs"
                    textField.keyboardType = .numberPad
                }
                
                let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
                let okAction = UIAlertAction(title: "OK", style: .default) { [weak self, weak alertController] _ in
                    guard let textField = alertController?.textFields?.first,
                          let text = textField.text,
                          let numberOfPairs = Int(text) else {
                        return
                    }
                    self?.game?.cardsCount = numberOfPairs
                    self?.numberOfPairsCallback?(numberOfPairs)
                   // self?.updateNumberOfPairsLabel()
                    
                }
                
                alertController.addAction(cancelAction)
                alertController.addAction(okAction)
            
                present(alertController, animated: true, completion: nil)
            }
                                            
    // selection shapes of cards
    
    var typeLabel: UILabel?
    var colorLabel: UILabel?
    
    func selectType() -> UILabel {
        let selectType = UILabel()
        selectType.translatesAutoresizingMaskIntoConstraints = false
        selectType.text = "Select type of cards"
        self.view.addSubview(selectType)
        
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(lebelOfSelectTypeTapped(_:)))
        selectType.isUserInteractionEnabled = true
        selectType.addGestureRecognizer(tapGesture)
        
        
            NSLayoutConstraint.activate([
                selectType.leadingAnchor.constraint(equalTo: selectPairCards().leadingAnchor),
                selectType.topAnchor.constraint(equalTo: selectPairCards().bottomAnchor, constant: 20)
                    ])
        
        return selectType
    }
 
    // Selection of card types
            func selectTypeView() {
                let alertController = UIAlertController(title: "Select Card Type", message: nil, preferredStyle: .actionSheet)
                                   
                // Add action for each card type
                for type in CardType.allCases {
                let action = UIAlertAction(title: getTitle(for: type), style: .default) { [weak self] _ in
                // Handle selection
                    // Handle selection
                self?.handleCardTypeSelection(type)
                           // Update the game object's card type
                
           }
                alertController.addAction(action)
         }
                                   
                // Add cancel action
                let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
                                   alertController.addAction(cancelAction)
                                   
                // Present the alert controller
                present(alertController, animated: true, completion: nil)
            }

    // Get the title for the card type based on its enum case
       private func getTitle(for type: CardType) -> String {
           switch type {
           case .circle:
               return "Circle"
           case .transparent:
               return "Transparent"
           case .cross:
               return "Cross"
           case .square:
               return "Square"
           case .fill:
               return "Fill"
           }
       }
    
 
    // Lebel of Select Color
    func selectColor() -> UILabel {
        
        let selectColor = UILabel()
        colorLabel = selectColor // Store the label in the property
        //label4.frame = CGRect(x: 20, y: 250, width: 300, height: 30)
        selectColor.translatesAutoresizingMaskIntoConstraints = false
        selectColor.text = "Select color"
        self.view.addSubview(selectColor)
        
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(selectCardColorButtonTapped))
        selectColor.isUserInteractionEnabled = true
        selectColor.addGestureRecognizer(tapGesture)
    
        NSLayoutConstraint.activate([
                
                selectColor.leadingAnchor.constraint(equalTo: selectPairCards().leadingAnchor),
                selectColor.topAnchor.constraint(equalTo: selectType().bottomAnchor, constant: 20)
            ])
        
        return selectColor
        
       
         }
                                  
    // Selection of card colors
        func selectColorView() {
           let alertController = UIAlertController(title: "Select Card Color", message: nil, preferredStyle: .actionSheet)
                                                      
            // Add action for each color
            for color in CardColor.allCases {
            let action = UIAlertAction(title: getTitle(for: color), style: .default) { [weak self] _ in
            // Handle selection
            self?.handleCardColorSelection(color)
        }
            alertController.addAction(action)
        }
                                                      
            // Add cancel action
            let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
                                                      alertController.addAction(cancelAction)
                                                      
            // Present the alert controller
            present(alertController, animated: true, completion: nil)
        }
 
    // Get the title for the card color based on its enum case
    private func getTitle(for color: CardColor) -> String {
        switch color {
        case .red:
            return "Red"
        case .green:
            return "Green"
        case .black:
            return "Black"
        case .gray:
            return "Gray"
        case .brown:
            return "Brown"
        case .yellow:
            return "Yellow"
        case .purple:
            return "Purple"
        case .orange:
            return "Orange"
        }
    }

    // Handle card type selection
        func handleCardTypeSelection(_ type: CardType) {
            // Update the card type in the game object
  
        }
 
    // Handle card color selection
        func handleCardColorSelection(_ color: CardColor) {
    
        }
                                         
      @objc func lebelOfSelectTypeTapped(_ gestureRecognizer: UITapGestureRecognizer) {
          selectTypeView()
        }
                                                
    // Connection to the button touch
        @objc func selectCardColorButtonTapped() {
            selectColorView()
            
        }
 
      override func viewDidLoad() {
          super.viewDidLoad()
          
          view.backgroundColor = .white
          view.addSubview(backButtonView)
          view.addSubview(saveButtonView)
          
          
         
          typeLabel = selectType() // Store the label returned by selectType()
          colorLabel = selectColor() // Store the label returned by selectColor()
           
          
      }
   
    
}

Helpers: CardViewFactory

import UIKit

class CardViewFactory {
    func get(_ shape: CardType, withSize size: CGSize, andColor color: CardColor) -> UIView {
        // на основе размеров определяем фрейм
        let frame = CGRect(origin: .zero, size: size)
        // опредяем UI-цвет на основе цвета модели
        let viewColor = getViewColorBy(modelColor: color)
        
        // генерируем и возвращаем карточку
        switch shape {
        case .circle:
            return CardView<CircleShape>(frame: frame, color: viewColor)
        case .cross:
            return CardView<CrossShape>(frame: frame, color: viewColor)
        case .square:
            return CardView<SquareShape>(frame: frame, color: viewColor)
        case .fill:
            return CardView<fillShape>(frame: frame, color: viewColor)
        case .transparent:
            return CardView<TransparentCircle>(frame: frame, color: viewColor)
        }
    }
    
    // преобразует цвет Модели в цвет Представления
    private func getViewColorBy(modelColor: CardColor) -> UIColor {
        switch modelColor {
        case .black:
            return .black
        case .red:
            return .red
        case .green:
            return .green
        case .gray:
            return .gray
        case .brown:
            return .brown
        case .yellow:
            return .yellow
        case .purple:
            return .purple
        case .orange:
            return .orange
        }
    }
}

Monoli
  • 1

0 Answers0