Match card game Lesson 8 and 9

in lesson 08 and 09 , you mentioned this line of code at CardCollectionViewCell model:
// Keep track of the card that gets passed in
self.card = card,

basically we didn’t use that tracker we used the card which was passed to the func, it will change a lot of things actually , I don’t know maybe you can explain , but remaking it with // won’t change anything in the program. !!! correct if am wrong.
thank you Chris in advance and really love your courses a lot.

Hi @Ammar,

The “self.card” is the optional variable of type Card which has been declared in the CardCollectionViewCell class.

The “card” variable is the value given to the function configureCell().

You could have also rename a variable for better understanding:

    var theMainCard:Card?
    
    func configureCell(card:Card) {
        
        //Keep track of the card this cell represent
        self.theMainCard = card
        
        // Set the front image
        frontImageView.image = UIImage(named: card.imageName)

@Ammar

Please move this post to the App Development Category.

Requests and Feedback is really about the videos for things you want to see added to the videos

I understand that , even if I change it to self.theMainCard it won’t do anything ! even if I mark it with // as a comment , it won’t change anything, because its never used through the program , Chris said we need to have a property to keep track of which card the cell is suppose to display !, what am not getting is that it was never used ! through the code! you can have a look on the code in .zip file at that lesson and see by color as well , we used the card which has been passed to the function. see it was use on this line self.theMainCard = card
and that’s it no other place and we didn’t use it as property of the function in any other class !
you

//var theMainCard:Card?
func setCard(_ card:Card) {
    
    // Keep track of the card that gets passed in
   // self.theMainCard = card

still the whole application works perfectly !!!

If you comment (//) the “self.theMainCard = card” line, the application stop working as you cannot return the card tapping the cell… I don’t think your code is correct

did you try it ? I just downloaded the zip file on lesson 9 and commented the two line I mentioned with // and run the application the whole process and logic were working fine.!!!
I wish I could say its my code , but its the code attached with the lesson…
have a thorough look into it and try it and let me know !!

I have tried with mine (I followed the tutorial and I could have done something wrong too!) and it failed

just download the zip file and comment that two lines and you will see
plus you can know which variable you are using by looking into the code color !!

Which version are you at?

From the file given by Chris for the beginning of lesson 9 (M3L09 Lesson Source File.zip), if you comment the line

//self .card = card

the card flip but you don’t see the front card (which is normal as you don have the value of the card)

You can see the colour when you select the variable and for bigger projects, you can also right click on the variable and select “Refactor > Rename…” from the menu. It helps sometimes too.

am using Xcode Version 11.4.1 , i think I will record a video and send the link to show you.
even the flip card work fine I can see both of them when its match …
but have u noticed the color of the code … that is another clue that we are not using theMainCard variable !

No. I think you get confused where I am starting only now to understand how its all work… I will try to explain what I understand…

.
.
.

  1. We declare a variable:
var card = Card?

variable “card” of type “Card” as optional (?).

Card is the class created in “Card.swift”

class Card {
    var imageName: String = ""
    var isMatch: Bool = false
    var isFlipped: Bool = false
}

therefore, we have:

card.imageName = ""
card.isMatch = false
card.isFlipped = false

(until now, we were talking about the variable “card” that I have renamed for your understanding “theMainCard” above)

.
.
.

  1. In the method “collectionView” delegate Methods (willDisplay cell forItemAt indexPath), we are calling the function “configureCell()” with the value “cardsArray[indexPath.row]” as “card:” parameter
let cardCell = cell as? CardCollectionViewCell
cardCell?.configureCell(card: cardsArray[indexPath.row])

“indexPath.row” is a kind of Array of Integer which denominate a cell [Int] = [0, 1, 2, …]

.
.
.
3) We have to keep in mind that “cardArray” is a Dictionary containing 16 times a “Card”

var cardsArray = [Card]()
cardsArray = model.getCards()

So “cardArray” should look like something like that:

var cardArray = [
     card[0] -> [ imageName = "card6",
                  isMatch = false,
                  isFlipped = false
                ],
     card[1] -> [ imageName = "card12",
                  isMatch = false,
                  isFlipped = false
                ],
     card[2] -> [ imageName = "card6",
                  isMatch = false,
                  isFlipped = false
                ],

(...)

     card[15] -> [ imageName = "card4",
                  isMatch = false,
                  isFlipped = false
                ]
]

and the “indexPath.row” will loop requesting one of the index of “cardArray” (i.e. cardArray[0])
.
.
.

  1. When the the function “configureCell()” with the parameter “card” is called, we are “copying” one of the “Card” from "cardArray’ in the above variable “card” (that we have declared in point 1 as an optional empty Card - and I called in the previous thread “theMainCard”).
    We have to use the prefix “self.” as it has been set outside of the function.
func configureCell(card:Card) {
        self.card = card
}

Now, our variable “card” from point 1 is equal to our first “Card” from “cardsArray” (cardArray[0] if indexPath.row = 0 in my example).

card.imageName = "card6",
card.isMatch = false,
card.isFlipped = false

.
.
.
5.) We then check

if card.isMatch == true {
return
}

but as “card.isMatch” is false, it exit the function “configureCell()” (with “return”) and the second cell (IndexPath.row = 1), then the third… until they get recycled!

I hope this will help you to understand how it works and that I didn’t make too many terminology mistakes - I am a newbie too!

Maybe Chris (or someone from his team) can correct where I am wrong if so.

Now that we have demonstrate that the value is used, could you maybe share your project so we can have a lot why it is acting weird?

heres an easy explanation

the code
func configureCell(card:Card) {

    // Keep track of the card this cell represents
    self.card = card
    
    // Set the front image view to the image that represents the card
    frontImageView.image = UIImage(named: card.imageName)
    
    // Reset the state of the cell by checking the flipped status of the card and then showing the front or the back imageview accordingly
    
    if card.isFlipped == true {
        // Show the front image view
        flipUp(speed: 0)
    }
    else {
        // Show the back image view
        flipDown(speed: 0)
    }
}

here its true that self.card is not used on the function, however, it is used in the next functions (flipUp, flipDown)

func flipUp(speed:TimeInterval = 0.3) {

    // Flip up animation 
    UIView.transition(from: backImageView, to: frontImageView, duration: speed, options: [.showHideTransitionViews,.transitionFlipFromLeft], completion: nil)
    
    // Set the status of the card
    card?.isFlipped = true
}

where the card (self.card) .isFlipped property is set to true or false (if flipDown)

removing or commenting out the “main” card will give an error that card?.isFlipped = true is using an unresolved identifier

this is based on lesson 9 source code by chris

I commented out cardOne.isFlipped = false in checkForMatch() because flipDown() sets the property via card?.isFlipped = false.

but then any cards outside the ViewController were not getting flipped as they came back into view.

I realise now that the ViewCells are only temporary (recyclable) containers (as Chris explained), and their card properties are copies of the cards in the cardsArray. so every time a cell’s method is called, it has to be reminded which card it is responsible for at that moment, and the current properties of that card.

so, the card property in the CardCollectionViewCell class is not the card object from within the cardsArray property of the ViewController. and the self.card = card statement at the beginning of configureCell() is necessary, if only because the flipUp() and flipDown() functions will need it.

also, when cell?.flipUp() is called from didSelectItemAt in the ViewController, it doesn’t actually set the property of the card in cardsArray, only the property within the cell. there are two sets of objects being tracked here.

I believe this is something that Apple focussed on when they first introduced SwiftUI at WWDC, that with SwiftUI, attached properties could be updated automatically whenever the user interacted with the screen objects.