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

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.

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)
}
}

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.

Calling func(didSelectItemAt) while UIcollectionview is on scrolling

Currently I'm using UICollectionView with several cells in it. UICollectionView scrolls horizontally to move between the cells.
My question is : Is there any way to call func(didSelectItemAt) by clicking the cell while its UICollectionView is on scrolling?
Here is gif.
Right after dragging cell to left for scrolling, I click the cell continuously to call func(didSelectItemAt) but it get call only when scrolling is finished. Is it possible to func(didSelectItemAt) to be called instantly right after clicking cell while on scrolling?
Here is my code for collectionView delegate & dataSource.
extension CardViewController : UICollectionViewDelegate,UICollectionViewDataSource {
public func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return cardCount
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = cardCollectionView.dequeueReusableCell(withReuseIdentifier: "card", for: indexPath) as! CardCell
cell.isFront = cellsSelectedStatus[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = cardCollectionView.cellForItem(at: indexPath) as! CardCell
UIView.transition(with: cell, duration: 0.3, options: [.transitionFlipFromLeft,.allowUserInteraction], animations: nil, completion: nil)
cellsSelectedStatus[indexPath.row] = !(cellsSelectedStatus[indexPath.row])
cell.isFront = cellsSelectedStatus[indexPath.row]
print("did select")
}
Collectionview’s dataSource&delegate are called in viewDidLoad.
I know there is allowUserInteraction from UIViewAnimationOptions which allows to click view while its being animated. Is there smilier code for func(didSelectItemAt)?
There’s no networking & UIGestureRecognizers is attached to the cell or collectionview.
So far I've set
isUserInteractionEnabled,
isMultipleTouchEnabled,
allowsMultipleSelection to true for collectionView,
but it still behaves the same.
I’m using Swift3, Xcode9.
Thanks in advance.

Images In a Collection View

I am coding in Swift 3.2, and inside of a TableView Controller have a TableView (scrolling vertically), and each row of the table holds a CollectionView (scrolling horizontally).
I am not sure if it is acceptable coding practice, but I made my ViewController class inherit UITableViewController and UICollectionViewDelegate and UICollectionViewDataSource.
For now, I have a hard coded array of image names and would like three images to scroll horizontally in each collection view, with each row having distinct images. However, as of now each row shows the same three images ("11.png", "12.png", "13.png"), and I was wondering how to fix this.
Below is my ViewController class.
//for the tableView
let event = generateRandomData()
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return event.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
return cell
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
guard let tableViewCell = cell as? TableViewCell else { return }
tableViewCell.setCollectionViewDataSourceDelegate(self, forRow: indexPath.row)
}
//for the collectionView
var images: [String] = [
"11.png", "12.png", "13.png",
"21.png", "22.png", "23.png",
"31.png", "32.png", "33.png",
"41.png", "42.png", "43.png",
"51.png", "52.png", "53.png",
"61.png", "62.png", "63.png",
"71.png", "72.png", "73.png",
"81.png", "82.png", "83.png",
"91.png", "92.png", "93.png",
"101.png", "102.png", "103.png",
]
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
collectionView.isPagingEnabled = true; //makes the horizontal scrolling have no bounce and relatively have a better snap
let myCell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath as IndexPath) as! CollectionViewCell
let myCellImage = UIImage(named: images[indexPath.row])
myCell.eventImage.image = myCellImage
return myCell
}
I am an amateur coder, so any help is greatly appreciated!
I am sure this problem already have an answer somewhere, but since I can't find that I'll just give one here.
Your mistake is here:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
/...
let myCellImage = UIImage(named: images[indexPath.row]) //<< indexPath.row will return 0,1 and 2 for each of your collection view
/...
}
Each collection view in your table view does not communicate amongst themselves to form a larger collective view. Since all of the are provided the int of 3 for numberOfItemsInSection and their numberOfSections defaulted to 1(you did not set any), each of them will request for item at indexpath (0,0), (0,1) and (0,2) when trying to populate their data.
Your hardcoded array however, is an array with 30 items. So each collection view when requesting for cellForItemAt will only get 0,1 and 2 for images[indexPath.row]. Also you should use indexPath.item for collection view, indexPath.row is for table views, it may or may not work if used wrongly.
Solution
You can just set the tag property of the collection view to mark it's row:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
/...
//Make sure you have a subclass for UITableViewCell that have an IBOutlet that connects to the collection view
cell.collectionView.tag = indexPath.row //Note that this only works in your situation where the table view have only 1 section.
/...
}
Then split your array into a 2D array:
var images = [
["11.png", "12.png", "13.png"],
["21.png", "22.png", "23.png"],
["31.png", "32.png", "33.png"],
["41.png", "42.png", "43.png"],
["51.png", "52.png", "53.png"],
["61.png", "62.png", "63.png"],
["71.png", "72.png", "73.png"],
["81.png", "82.png", "83.png"],
["91.png", "92.png", "93.png"],
["101.png", "102.png", "103.png"]
]
Finally read the array in the collection view like:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
/...
let myCellImage = UIImage(named: images[myCell.tag][indexPath.item])
/...
}

Collection View terminates after cellForItemAtIndex

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!
}