In my match app Xcode project I have an error stating:
Use of unresolved identifier 'cell'.
My Code:
import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
}
#IBOutlet weak var collectionView: UICollectionView!
var model = CardModel()
var cardArray:[Card] = []
override func viewDidLoad() {
super.viewDidLoad()
//call the getCards method of the card model
cardArray = model.getCards()
collectionView.delegate = self
collectionView.dataSource = self
}
//mark: -UICollectionView Protocol Methods
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return cardArray.count
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
//get a cardCollectionViewcell object
let cell =
collectionView.dequeueReusableCell(withReuseIdentifier: "CardCell", for: indexPath) as! CardCollectionViewCell
//get card that
let card = cardArray[indexPath.row]
cell.setCard(card)
return
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
_ = collectionView.cellForItem(at: indexPath) as!CardCollectionViewCell
//flip the card
cell.flip() <--------- THIS IS THE ERROR
Once the error is fixed I am supposed to be able run a match app example on the fake iPhone. It would allow me to flip the cards on a click.
I think you forgot to set reusable identifier
I hope this will be resolve your issue.
To access that cell method you have to declare variable
let cell = collectionView.cellForItem(at: indexPath) as!CardCollectionViewCell
First of all you are misusing collectionView:willDisplay:forItemAt. Move everything into collectionView:cellForItemAt, replace return with return cell and delete willDisplay:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CardCell", for: indexPath) as! CardCollectionViewCell
//get card that
let card = cardArray[indexPath.row]
cell.setCard(card)
return cell
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { ... }
The error is pretty clear: You never declared cell is the scope of didSelectItemAt. Change
let cell = collectionView.cellForItem(at: indexPath) as! CardCollectionViewCell
Related
#IBOutlet weak var collectionView: UICollectionView!
var model = CardModel()
//Kepp track of the cards veiwed
var cardArray = [Card]()
override func viewDidLoad() {
super.viewDidLoad()
//Call the getCards method of the card model
cardArray = model.getCards()
collectionView.delegate = self
collectionView.dataSource = self
}
// MARK: -UICOlecctionView Protocol Methods
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return cardArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// Get a card 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) {
print("Cell is taped: \(indexPath.row)")
let cell = collectionView.cellForItem(at: indexPath) as! CardCollectionViewCell
// Flip the card
cell.flip()
}
}
I have an issue when I tap on the card to flip. The did select function is not getting called as I tested it for a print function and it shows that it's not tapping at all. Can some help please?
Have an issue where:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
is called when an item is selected, but I cannot change any cell properties from this method.
I created a new project with a stripped down UICollectionViewController to change the background color of a cell when selected. It also doesn't work. Here it is:
import UIKit
private let reuseIdentifier = "Cell"
class CollectionViewController: UICollectionViewController {
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
cell.backgroundColor = UIColor.blue
return cell
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = self.collectionView(self.collectionView, cellForItemAt: indexPath)
cell.backgroundColor = UIColor.green
}
}
The only thing I did in Storyboard is delete the standard View Controller and replace it with a UICollectionViewController, create a subclass of UICollectionViewController and set the controller in the storyboard as that class.
Also, I can confirm that the cell's index path is returned from this method when called from inside the didSelectItemAt method:
self.collectionView.indexPathsForSelectedItems
You are using the wrong API. Never call the delegate method collectionView(_ cellForItemAt:, use cellForItem(at:
if let cell = collectionView.cellForItem(at: indexPath) {
cell.backgroundColor = UIColor.green
}
But be aware that this change is not persistent. When the user scrolls the color will change back to blue.
You can achieve that as below,
ViewController
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ColoursViewCell", for: indexPath) as! ColoursViewCell
return cell
}
UICollectionViewCell
class ColoursViewCell: UICollectionViewCell {
#IBOutlet var photoImageView: UIImageView?
override var bounds: CGRect {
didSet {
self.layoutIfNeeded()
}
}
override var isSelected: Bool{
didSet{
if self.isSelected{
self.photoImageView?.backgroundColor = UIColor.random
}else{
self.photoImageView?.backgroundColor = UIColor.lightGray
}
}
}
}
you can have sample projects from this link I have in GitHub
https://github.com/hadanischal/RandomColors/tree/develop
My keyboard extensions which consists of a UICollectionView of UIImages keeps crashesing on iOS12 after exceeding 53MB of RAM
collectionView:willDisplay cell is called on all cells, regardless if they are shown
NOTE: didEndDisplaying gets never called
func collectionView(_ collectionView: UICollectionView, willDisplay c: UICollectionViewCell, forItemAt indexPath: IndexPath)
can i bring back the behaviour of collectionView:willDisplay so its called only when the cell is really displayed?
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: EmojiCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "EmojiCollectionViewCell", for: indexPath) as! EmojiCollectionViewCell
return cell
}
func collectionView(_ collectionView: UICollectionView, willDisplay c: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let cell = c as! EmojiCollectionViewCell
let imageName = String(format:"%#-%#", categories[selectedCategory], NSNumber(value: indexPath.row + 1));
let emojiImage = UIImage(named: imageName)
cell.setupWithEmojiImage(image: emojiImage)
}
func collectionView(_ collectionView: UICollectionView, didEndDisplaying c: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let cell = c as! EmojiCollectionViewCell
cell.setupWithEmojiImage(image: nil)
}
You're probably calling reloadData() or something within a tableview datasource method, causing an infinite loop. what is the code in setupWithEmojiImage(image: nil)? I think you should place the following in cellForRow instead of willDisplay:
let imageName = String(format:"%#-%#", categories[selectedCategory], NSNumber(value: indexPath.row + 1));
let emojiImage = UIImage(named: imageName)
cell.setupWithEmojiImage(image: emojiImage)
Also avoid using the bang operator (!) to force unwrapping, this leads to crashing if the object doesn't exist. Do this:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell: EmojiCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "EmojiCollectionViewCell", for: indexPath) as? EmojiCollectionViewCell {
return cell
}
return UITableViewCell()
}
I have a collectionView of images and I want to show when multiple of these cells are selected.
I use a simple cell.myUIImage.isHidden = false but nothing seems to work.
Multiple select is enabled in the Interface Builder and is set in viewDidLoad(), this is what I have:
#IBOutlet weak var myUIImage: UIImageView!
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoItemView", for: indexPath) as! PhotoFeed
cell.myUIImage.isHidden = false
print ("cell was selected at:", indexPath.item)
photoCollection.reloadData()
}
You can not access UICollectionViewCell with dequeueReusableCell other than cellForItemAt indexPath method.
To access cell in didSelectItemAt indexPath use:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let collectionCell = collectionView.cellForItem(at: indexPath) as? PhotoFeed {
collectionCell.myUIImage.isHidden = false
print ("cell was selected at:", indexPath.item)
photoCollection.reloadData()
}
}
But again photoCollection.reloadData() will reload the cell and myUIImage will be hidden again.
So you need to maintain the selected indexes and show/hide in cellForItemAt indexPath. With the help of selectedIndexes you can have the functionality like show image on selection and hide image on select again.
You should have an array of selectedIndexes. Say
var selectedIndexes = [IndexPath]()
Add in didSelectItemAt indexPath, Update method as:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if selectedIndexes.contains(indexPath) {
if let index = selectedIndexes.index(of: indexPath) {
selectedIndexes.remove(at: index)
}
} else {
selectedIndexes.append(indexPath)
}
photoCollection.reloadData()
}
In cellForItemAt indexPath:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {\
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoItemView", for: indexPath) as? PhotoFeed else {
fatalError("cell not found")
}
if selectedIndexes.contains(indexPath) {
cell.myUIImage.isHidden = false
} else {
cell.myUIImage.isHidden = true
}
return cell
}
You should create #IBOutlet for myUIImage in collectionviewcell:
class PhotoFeed: UICollectionViewCell {
#IBOutlet weak var myUIImage: UIImageView!
}
In ViewController:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoItemView", for: indexPath) as! PhotoFeed
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! PhotoFeed
cell.myUIImage.isHidden = !cell.myUIImage.isHidden
}
I am using Swift4 and Xcode9.
I create my collectionView cells by using the delegate method below;
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "mediaItemCell", for: indexPath) as! MediaItemCollectionViewCell
cell.mediaItemCellImageView.image = mediaPlayer?.item(at: indexPath.row)?.image
return cell
}
Then, I want to replace the image of selected cell with a "stop" image. To achieve this, I have tried to use didSelectItemAt method;
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if (mediaPlayer?.isPlaying == true) {
// stop the player
mediaPlayer?.stopPlayingMedia()
} else {
// change the image and start playing
let cell = collectionView.cellForItem(at: indexPath) as! MediaItemCollectionViewCell // CRASH
cell.mediaItemCellImageView.image = UIImage(named: "stop.png")
...
...
}
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
// restore the original image
let cell = collectionView.cellForItem(at: indexPath) as! MediaItemCollectionViewCell
cell.mediaItemCellImageView.image = mediaPlayer?.item(at: indexPath.row)?.image
// stop playing
mediaPlayer?.stopPlayingMedia()
}
Now, when I select the cell which is already selected (hence the original image has already been replaced with "stop.png") the code above works; i.e. the original image is restored, which is done by stopPlayingMedia() method.
When I select a cell different than the currently selected one, the App crashes. I tried to move the didSelectItemAt call out of the delegate method, but it did not work.
Surprisingly, the logical check if collectionView.cellForItem(at: indexPath) is MediaItemCollectionViewCell succeeds when I select the currently selected cell, and fails when I select another cell.
I want to change the image of the cell, hence need to cast the variable type but it fails. Why does to cast fail when I select a different cell than the currently selected one?
Thanks
Edit:
I use collectionView.reloadData() to restore all the images to originals (I mentioned this in the OP - when stopPlayingMedia() is invoked). It appears that, if I add a private var index:IndexItem? to my ViewController class and update its value within didSelectItemAt (to save the last selected cell) and use the code below, the code does not crash
extension ViewController:MediaPlayerControllerDelegate {
// this delegate method is invoked when stopPlayingMedia() is called
func mediaPlayerControllerDidFinishPlaying() {
// restore the UI
// collectionView.reloadData() -> this causes the crash, instead, I only restore one cell
let cell = collectionView.cellForItem(at: index!) as! MediaItemCollectionViewCell
cell.mediaItemCellImageView.image = mediaPlayer?.item(at: (index?.row)!)?.image
timeLabel.text = "00:00"
progressBar.progress = 0.0
}
}
I tried the following to create cell and changing the image inside cell on selection. Not crashing at all. Please check the implementation, It might help you.
import UIKit
class ViewController: UIViewController {
private var isPlayingModel = [false, false, false, false, false] // initially all are stop, not playing
#IBOutlet weak var collectionView: UICollectionView! {
didSet{
collectionView.delegate = self
collectionView.dataSource = self
}
}
}
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return isPlayingModel.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "mediaItemCell", for: indexPath) as! MediaItemCollectionViewCell
cell.imageVw.image = isPlayingModel[indexPath.row] ? #imageLiteral(resourceName: "start") : #imageLiteral(resourceName: "stop")
return cell
}
}
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
isPlayingModel[indexPath.row] = !isPlayingModel[indexPath.row]
let cell = collectionView.cellForItem(at: indexPath) as! MediaItemCollectionViewCell
cell.imageVw.image = isPlayingModel[indexPath.row] ? #imageLiteral(resourceName: "start") : #imageLiteral(resourceName: "stop")
}
}
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.size.width, height: 60)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 4
}
}
class MediaItemCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var imageVw: UIImageView!
}
Check the implementation here.
You are not passing indexPath correctly to load a cell, that's why it is crashing.Edit your didSelectItemAt: method as follows:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedCell = IndexPath(item: indexPath.item, section: indexPath.section)
let cell = collectionView.cellForItem(at: selectedCell) as! ACollectionViewCell
cell.imgView.image = UIImage(named: "3.jpg")
}
Collection view indexPath has element item, and not row
Try this and see: (let me know what does it print)
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
// restore the original image
if let cell = collectionView.cellForItem(at: indexPath) as? MediaItemCollectionViewCell {
if let cellImage = mediaPlayer?.item(at: indexPath.item)?.image { // Collection view indexPath has element `item`, and not `row`
cell.mediaItemCellImageView.image = cellImage
} else {
print("cellImage not found at indexPath - \(indexPath)")
}
} else {
print("collection view cell `MediaItemCollectionViewCell` not found at indexPath -\(indexPath) ")
}
// stop playing
mediaPlayer?.stopPlayingMedia()
}