As I get fewer cards, they sometimes change position (shift to the right which occasionally means changing rows), which is weird since we didn’t delete the cars, just made it invisible. I got rid of most of this by eliminating the timer label width (which got rid of the warning that images may be clipped). Any ideas ? (It only happens on my iPhone X not on the simulator )
Check your constraints for the cell
Could you post your code for that view controller?
//
// CardCollectionViewCell.swift
// The Match app
//
// Created by Rick Blumenthal on 11/13/19.
// Copyright © 2019 Richard Blumenthal. All rights reserved.
//
import UIKit
class CardCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var frontImageView: UIImageView!
@IBOutlet weak var backImageView: UIImageView!
var card:Card?
func setCard(_ card:Card){
//Keep track of the card that gets passed
self.card = card
if card.isMatched == true {
//if the cardi amatched make imageView invisible
backImageView.alpha = 0
frontImageView.alpha = 0
return
}
else {
//if the card hasnt been matched make the imageView visible
backImageView.alpha = 1
frontImageView.alpha = 1
}
frontImageView.image = UIImage(named: card.imageName)
//determine if card is flipped up or down
if card.isFlipped == true {
//make sure front is on top
UIView.transition(from: backImageView, to: frontImageView, duration: 0, options: [.transitionFlipFromLeft,.showHideTransitionViews], completion: nil)
}
//make sure back is on top
else {
UIView.transition(from: frontImageView, to: backImageView, duration: 0, options: [.transitionFlipFromRight,.showHideTransitionViews], completion: nil)
}
} //end func setCard
func flip() {
UIView.transition(from: backImageView, to: frontImageView, duration: 0.3, options: [.transitionFlipFromLeft, .showHideTransitionViews], completion: nil)
}
func flipBack(){
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.5) {
UIView.transition(from: self.frontImageView, to: self.backImageView, duration: 0.3, options: [.transitionFlipFromRight, .showHideTransitionViews], completion: nil)
}
}
func remove() {
//Removes both imageviews from being visible
backImageView.alpha = 0
//Animate it no need to animate back image since it will be covered by the front image
UIView.animate(withDuration: 0.3, delay: 0.5, options: .curveEaseOut, animations: {
self.frontImageView.alpha = 0
}, completion: nil)
}
} //UICollectionView End
Actually it does happen on the simulator
copy of the output in the bottom of the screen of the Xcode app
2
8
7
10
5
11
4
1
2019-11-23 22:50:35.669980-0800 The Match app[5611:333601] [plugin] AddInstanceForFactory: No factory registered for id <CFUUID 0x600003fdd340> F8BB1C28-BAE8-11D6-9C31-00039315CD46
2019-11-23 22:50:35.691502-0800 The Match app[5611:333839] [ddagg] AggregateDevice.mm:790 couldn’t get default input device, ID = 0, err = 0!
2019-11-23 22:50:38.876235-0800 The Match app[5611:333841] [aqsrv] AQServer.cpp:68:APIResult: Exception caught in AudioQueueInternalNotifyRunning - error -66671
2019-11-23 22:50:56.485905-0800 The Match app[5611:333840] [aqsrv] AQServer.cpp:68:APIResult: Exception caught in AudioQueueInternalNotifyRunning - error -66671
Message from debugger: Terminated due to signal 15
The CardCollectionViewCell.swift looks good. What about your ViewController.swift file?
If they switch places it’d have to be something with how the collection view is dequeue-ing them
//
// ViewController.swift
// The Match app
//
// Created by Rick Blumenthal on 11/13/19.
// Copyright © 2019 Richard Blumenthal. All rights reserved.
//
import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
@IBOutlet weak var timerLabel: UILabel!
@IBOutlet var collectionView: UICollectionView!
var model = CardModel()
var cardArray = [Card]()
var firstFlippedCardIndex:IndexPath?
var timer: Timer?
var milliseconds:Float = 60*1000 // 10 seconds
override func viewDidLoad() {
super.viewDidLoad()
//Call the getCards method of the cardModel
cardArray = model.getCards()
collectionView.delegate = self
collectionView.dataSource = self
//Create timer
timer = Timer.scheduledTimer(timeInterval: 0.001, target: self, selector: #selector(timerElapsed), userInfo: nil, repeats: true)
RunLoop.main.add(timer!, forMode: .common)
}
override func viewDidAppear(_ animated: Bool) {
SoundManager.playSound(.shuffle)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
//Dispose of any resources that can be created.
}
@objc func timerElapsed() {
milliseconds -= 1
// Convert to seconds
let seconds = String(format: "%.2f", milliseconds/1000)
//Set Label
timerLabel.text = "Time Remaining \(seconds)"
//when timer reaches 0
if milliseconds <= 0 {
timer?.invalidate()
timerLabel.textColor = UIColor.red
//Check if there are any cards unmatched
checkGameEnded()
}
}
// MARK: - UICollectionView Protocol Methods
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return cardArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
//get a CardCollectionViewCell object
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CardCell", for: indexPath) as! CardCollectionViewCell
//get the card that the collection view is trying to display
let card = cardArray[indexPath.row]
//set the card for the cell
cell.setCard(card)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
//Check if there's any time left
if milliseconds <= 0 {
return
}
let cell = collectionView.cellForItem(at: indexPath) as! CardCollectionViewCell
let card = cardArray[indexPath.row]
if card.isFlipped == false && card.isMatched == false {
//flip the card
cell.flip()
//Play the flip sound
SoundManager.playSound(.flip)
//set the status of the card
card.isFlipped = true
//determineif its the first card flipped
if firstFlippedCardIndex == nil {
//this is the first card flipped
firstFlippedCardIndex = indexPath
}
else {
//this is the seccond card
checkForMatches(indexPath)
//TODO: perform matching logic
}
}
} //End the didSelelctItem method
//MARK Game select methods
func checkForMatches(_ secondFlippedCardIndex: IndexPath) {
//Get the cells for the two cards revealed
let cardOneCell = collectionView.cellForItem(at: firstFlippedCardIndex!) as? CardCollectionViewCell
let cardTwoCell = collectionView.cellForItem(at: secondFlippedCardIndex) as? CardCollectionViewCell
//Get the cards for the two cards revealed
let cardOne = cardArray[firstFlippedCardIndex!.row]
let cardTwo = cardArray[secondFlippedCardIndex.row]
//compare the cards
if cardOne.imageName == cardTwo.imageName {
//It's a match
//Play the is correct sound
SoundManager.playSound(.match)
//set the status of the cards
cardOne.isMatched = true
cardTwo.isMatched = true
//Remove the cards from the grid
cardOneCell?.remove()
cardTwoCell?.remove()
//check if there are any cards unmatched
checkGameEnded()
}
else {
//It's not a match
//Play the no match sound
SoundManager.playSound(.nomatch)
//set the status of the cards
cardOne.isFlipped = false
cardTwo.isFlipped = false
//flip the cards back
cardOneCell?.flipBack()
cardTwoCell?.flipBack()
}
//Tell the collectionView to reload the cell of the first card if it is nil
if cardOneCell == nil {
collectionView.reloadItems(at: [firstFlippedCardIndex!])
}
//reset the property that tracks the first card flipped
firstFlippedCardIndex = nil
}
func checkGameEnded () {
//Determine if there are any cards unmatched
var isWon = true
for card in cardArray {
if card.isMatched == false {
isWon = false
break
}
}
//Messaging variable
var title = ""
var message = ""
//If not then user has won, stop the timer
if isWon == true {
if milliseconds > 0 {
timer?.invalidate()
}
title = "Congratualtions!"
message = "You've won"
//Play win sound
SoundManager.playSound(.win)
}
else {
//If there are unmatched cards, check if there's any cards left
if milliseconds > 0 {
return
}
//Display messages
title = "Game Over"
message = "You've lost"
//Play the flip sound
SoundManager.playSound(.lose)
}
//Show won/lost messaging
showAlert(title, message)
}
func showAlert(_ title:String, _ message:String){
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert )
let alertAction = UIAlertAction(title: "Ok", style: .default, handler: nil)
alert.addAction(alertAction)
present(alert, animated: true, completion: nil)
}
} //End ViewController class
I double checked the video with the dequeue and I think I have it exact–can you verify (code is above this for the ViewController.Swift). Also if I can get this to run I am seriously considering the purchased video classes as well
Thanks
you removed the cells from the grid though… try commenting this out
this function is to make the cards invisible when there is a match, but I’ll try when I get home
the invisible was setting the alpha to 0 instead of 1 in the cardviewcell
removing it will remove it entirely from your list not just hiding it
in CardCollectionViewCell.swift the function remove is defined as below ( to set the image alpha to zero, not to remove that actual card)
func remove() {
//Removes both imageviews from being visible
backImageView.alpha = 0
//Animate it no need to animate back image since it will be covered by the front image
UIView.animate(withDuration: 0.2, delay: 0.4, options: .curveEaseOut, animations: {
self.frontImageView.alpha = 0
}, completion: nil)
I also have this issue now and then. I tried doing this and it caused the cards to remain visible even when I matched them.
the remove function is only designed to make them invisible, not to actually remove them. I hope to have an answer from them soon
Hey Rick the ViewController.swift looks good. I’d have to see the entire project to really see what’s wrong.
If you could zip the entire project (swift files and all) I, or anyone else, can look at it to try to help you out!
I’m inclined to think that the issue is somewhere in Storyboard with a setting on the CollectionView. I’ve looked through your code and checked with my version and it looks OK.
Hey Yunxaio, welcome to the community! Would you mind posting your question in a new topic so we don’t get mixed up? Thanks!
I thought I have the same question as the poster had. I am sorry