Collection View terminates after cellForItemAtIndex - swift

import UIKit
import Photos
class GalleryController: UICollectionViewController,
UIImagePickerControllerDelegate,
UINavigationControllerDelegate {
var Receipts = [UIImage?]()
//Number of Views
override func numberOfSections(in collectionView: UICollectionView) -> Int {
print("return1")
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print("self.receipts.count")
return self.Receipts.count
}
This code below is not working as it should and everytime I run the app it comes up with this error -
SmartReceipts[738:137366] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'the collection view's data source did not return a valid cell from -collectionView:cellForItemAtIndexPath: for index path {length = 2, path = 0 - 0}'
*** First throw call stack:
(0x186196364 0x1853dc528 0x186196238 0x186b317f4 0x1901010b0 0x18f6d7124 0x18f6d1de0 0x18f674f00 0x18a1d9998 0x18a1ddb20 0x18a14a36c 0x18a171b90 0x18f66a5c8 0x18613dedc 0x18613b894 0x18613be50 0x18605be58 0x187f08f84 0x18f6db67c 0x104484668 0x185b7856c)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
Any help with this would be greatly appreciated.
And the code that needs fixing is the code below:
func collectionView(_ collectionView: UICollectionView, cellForItemAtindexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "receipt", for: indexPath as IndexPath) as? PhotoCell
cell?.imageView.image = self.Receipts[indexPath.row]
print("Assigned to cell and should come up")
return cell!
}
}
It now errors as this error and i'm not sure how it's doing it? Because it is sending the image to an array but it's not showing up in the UICollectionViewCell?

Please use Xcode's code completion to ensure you are providing the correct method signatures.
The method needs to be:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
not:
func collectionView(_ collectionView: UICollectionView, cellForItemAtindexPath indexPath: NSIndexPath) -> UICollectionViewCell
Note cellForItemAtindexPath should be cellForItemAt and NSIndexPath should be IndexPath.
With that, your whole becomes:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "receipt", for: indexPath) as! PhotoCell
cell.imageView.image = self.Receipts[indexPath.row]
print("Assigned to cell and should come up")
return cell
}
See how you should force-cast the cell type. You want this to crash early on in development if your have your cell setup incorrectly.

Check your ViewController in main.storyBoard that any kind of warning that affect normal flow of working. right click on PhotoCell you have created in GalleryController, a dialogbox will appear, if there is any kind of warning. remove it by clicking close button. or else right click on top left icon of GallerViewController,you will get a popup and check any kind of yellow Warning is there.if so then remove it by clicking close button.
for example see the below images.
Use cellForItemAt instead of cellForItemAtindexPath
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "receipt", for: indexPath as IndexPath) as? PhotoCell
cell?.imageView.image = self.Receipts[indexPath.row]
print("Assigned to cell and should come up")
return cell!
}

Related

Xcode Swift UICollectionView: cellForItemAt IndexPath not called but numberOfItemsInSection does

I am currently programming an app with UICollectionView only when I start the app the CollectionView is not displayed. In my app I do not use the StoryBoard but only the ViewController. That's why I inherit from UICollectionViewController in my app. But when I start the app, the UICollectionView is simply not displayed. Through print commands I found out that the numberOfItemsInSection method is called, but the cellForItemAt indexPath is not. I also know that this will probably be a double question, but I still haven't found a solution for 2 hours. CollectionView.delegateand collectionView.dataSource are both set to = self and there are actually no problems with the constrains because I can turn the background of the UICollectionView to black, for example, and everything is displayed correctly there.
I hope someone can help me.
Best regards
// MARK: - Collectionview
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print("Item wurde erstellt")
return messages.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! ChatMessageCollectionViewCell
let message = messages[indexPath.item]
cell.textView.text = message.message
setUpCell(cell, message)
cell.bubbleViewWidthAnchor?.constant = estimateFrameForText(text: message.message!).width + 32
print("Celle wird erstellt")
return cell
}
I think you have missed the height and width for the cell. You should also check the message array count, perhaps it is zero.
extension YouClass: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return messages.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! ChatMessageCollectionViewCell
let message = messages[indexPath.item]
cell.textView.text = message.message
setUpCell(cell, message)
cell.bubbleViewWidthAnchor?.constant = estimateFrameForText(text: message.message!).width + 32
print("Celle wird erstellt")
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collBanner.frame.width, height: 140)
}
}

CollectionView indexPath.row is not correct? (Inside TableViewCell)

I have a collection view that I want the indexPath.row to. However, whenever I try to print/display the indexPath.row number, it's off. It's a horizontal collection view, so when I try to page right, it goes from: 0, 2, 3, 4, 5 and going backwards it jumps to 5,3,2,1,1,1. It's also varies each time.
Currently the collectionView is inside a tableViewCell. The data is getting loaded off of Firebase database.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return userList.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! FeedCollectionViewCell
pageController.currentPage = indexPath.row
pageController.numberOfPages = userList.count
return cell
}
Setting Prefetching enabled to false in storyboard worked.

unexpected non-void return value in void function (swift)

I'm getting the following error from the code below. I don't know how to fix this error and I can't really find a helpful answer.
unexpected non-void return value in void function
private func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath){
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "a11", for: indexPath)
return cell ;
}
func UICollectionViewCell() {
}
Your function definition of func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) is not defining a return value but your function is returning a UICollectionViewCell. This is the cause of the problem. Also, you probably wouldn't want to make cellForItemAt a private method either. You should update it to
func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "a11", for: indexPath)
return cell ;
}
you need to add to your function return value and remove a private keyword
func collectionView( _ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView
.dequeueReusableCell(withReuseIdentifier: "a11", for: indexPath)
return cell
}
Remove this code
func UICollectionViewCell() {
}
Check if cell this ID "a11" is registered. (In storyboard or in code, depending on your implementation).
Can you provide the text of Error, please?

didDeselectItemAt For UIcollectionView is not called but didSelectItemAt is called?

I am trying to implement a multi selection for UICollectionView. The delegate function didSelectItemAt is called but didDeselectItemAt is NEVER called and I do not know why? I am not even sure how it works. If I click on a cell - didSelectItemAt is called. So if I click once again on the same cell is didDeselectItemAt supposed to be called?
My UIViewController inherits and conforms to all of the following: UICollectionViewDelegate ,UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
viewdidLoad():
collectionView.allowsSelection = true
collectionView.allowsMultipleSelection = true
Delegate functions:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("didSelectItemAt")
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
print("DESELECT")
}
Please see this open issue on Github.
It could be that you have a tap gesture recognizer on a view in the same hierarchy that contains your collection view. Remove the tap gesture and see if that works. This happened to be my issue.
let tap = UITapGestureRecognizer(...) // your tap gesture recognizer
view.addGestureRecognizer(tap) // what you already have
tap.cancelsTouchesInView = false
I'm going to guess that your code is different from what you have shown us, and in that in your real code, you have not given the correct signature for didDeselect. Here's why. Look carefully at the code you have shown:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("didSelectItemAt")
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
print("DESELECT")
}
Now ask yourself: Why did the compiler permit the second func to stand, even though you forgot to say override? I'm guessing it's because it is not an override. There is something wrong with the signature, so it's just a meaningless function that doesn't conform to UICollectionViewDelegate.
Try using code completion to re-enter this function. If all goes well, it will be an override and it will start working.
To illustrate more precisely: This compiles, but the second method will never be called:
class CV : UICollectionViewController {
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("didSelectItemAt")
}
func colectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
print("DESELECT")
}
}
But this doesn't compile, because the signature is correct but we forgot override:
class CV : UICollectionViewController {
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("didSelectItemAt")
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
print("DESELECT")
}
}
But in that second one, if we do say override, it compiles and works.
It works like this:
if collectionView.allowsMultipleSelection = false // default then you have to tap on a different cell in order to deselect the previous one.
if collectionView.allowsMultipleSelection = true // only tapping on the same cell triggers deselect delegate method
I'm not sure if this changed recently, but with iOS13 it seems that simply specifying that a collection view cell is selected when the cell is dequeued is not enough for the collection view to allow you to deselect the cell. You need to also manually tell the collection view to select the cell.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: photoCellIdentifier, for: indexPath) as! YourCell
cell.isSelected = true
if cell.isSelected {
collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .left)
}
}
I figured this out because i was able to select and deselect cells, but was not able to deselect a cell that was initially selected.

UICollectionView Cell not changing upon selecting

In my project, I have a UICollectionView. In the UICollectionView, I have a custom cell.
I am able to print the cell value when it is selected within "didSelectItemAt", however, if I try to edit the cell in any way within this method, it does not change.
I'm sure I'm missing something, any help would be appreciated!
#IBOutlet weak var collectionView: UICollectionView!
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return statValues.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "customCollectionViewCell", for: indexPath) as! customCollectionViewCell
cell.statLabel.text = statHeaders[indexPath.row]
cell.statLabel.textColor = UIColor(red:0.31, green:0.31, blue:0.31, alpha:1.0)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "customCollectionViewCell", for: indexPath) as! customCollectionViewCell
print(cell.statLabel.text)
cell.backgroundColor = UIColor.yellow
collectionView.reloadData()
}
When user selects a cell, the code is correctly printing the value of the indexPath, however the backgroundColor does not change.
My guess would be that you are creating a new instance of cell instead of using the one in the collectionView
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// Change this line
let cell = collectionView.cellForItemAtIndexPath(indexPath: indexPath)
print(cell.statLabel.text)
cell.backgroundColor = UIColor.yellow
collectionView.reloadData()
}
Also, you should probably keep an external data model for your source of truth. If you have enough collectionViews that requires scrolling, when you scroll offscreen, your cells will be reused in a random order causing cells that you did not click to be yellow.
Create a seperate array such as
var selectedStatHeaders: Set<Int>()
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "customCollectionViewCell", for: indexPath) as! customCollectionViewCell
cell.statLabel.text = statHeaders[indexPath.row]
cell.statLabel.textColor = UIColor(red:0.31, green:0.31, blue:0.31, alpha:1.0)
// Reset/configure cell each reload
if selectedStatHeaders.contains(indexPath.row) { // Can also make this into a ternary
cell.backgroundColor = UIColor.yellow
} else {
cell.backgroundColor = UIColor.whit
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedStatHeaders.insert(indexPath.row)
collectionView.reloadItemsAtIndexPath(indexPath: indexpath)
}
Hmm... Doesn't make sense if the code is able to print but the background doesn't change color. Do you mean changing back from yellow to white? Anyway, just a hunch but I suspect it's because you are calling collectionView.reloadData() after your set the backgroundColor change.
https://developer.apple.com/documentation/uikit/uicollectionview/1618078-reloaddata
This causes the collection view to discard any currently visible
items (including placeholders) and recreate items based on the current
state of the data source object.