collectionView cellForItemAtIndexPath called only one time -swift - programmatically - swift

I have a collectionView that I set with this delegate and datasource:
extension CardView : UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let numberOfURLS = cardModel?.urlStrings?.count
return numberOfURLS!
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let videoCell = collectionView.dequeueReusableCell(withReuseIdentifier: "videoCellIdentifier", for: indexPath)
videoCell.backgroundColor = UIColor.random()
return videoCell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return self.bounds.size
}
}
The problem is that the method cellForItemAtIndexpath is called only one time even if the cells returned are more than one.

The collectionView(_:cellForItemAt:) is called only as the cells appear or are about to appear. It won't call it for all of the cells, but only the visible ones (or those about to scroll into view). It is called in a just-in-time manner.
You can also turn on prefetching (which only works if you turn off “Estimate Size” in the collection view’s “Size inspector” IB or manually set the flow layout’s estimatedItemSize to .zero), but that only makes it a tiny bit more “eager” in terms of fetching cells (fetching those that are going to scroll into view a bit sooner than it would otherwise). It will not fetch all of the cells, but just those that the OS determines might possibly scroll into view soon.

Related

How to make a line break in a label in Xib

I am trying to make a line break to my icon description, and I tried with /n and checking the multiple lines option in the interface builder.
Aqui es donde saco los datos de mi Icon
It should be noted that my icon is a CollectionViewCell that is generated inside a tableViewCell:
extension MiniAppsShorcuts : UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.listAr.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AccesosRapidos", for: indexPath) as? AccesosRapidos else {return UICollectionViewCell()}
cell.labelAR.text = listAr[indexPath.row].name
cell.fetchImage(urlString: listAr[indexPath.row].urlImage)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
delegate?.alert()
}
}
Make sure to set your cell.labelAR numberOfLines to zero and lineBreakMode to byWordWrapping. The default value is byTruncatingTail. That’s why it is truncating your label. Make sure your label and your cell heights are large enough to have multiple lines.

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

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.

How to keep collection view cell selected when presenting and dismissing modal view controller?

I have a UICollectionViewController that is also acts as UICollectionViewDelegateFlowLayout to size cells. One cell selection modally presents some custom controls and then returns when completed, but the selected cell is no longer selected when it reappears.
I see delegate method for collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) being called, but not collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath). I also see the layout being asked for the preferred cell size, so I’m thinking they are being redrawn, but the cells already exist.
I can solve the problem by calling relaodData(), and the cell knows it is selected, and is redrawn accordingly, but that seems like a costly and wrong solution. Any suggestions about what I am missing? Thanks.
The default implementation of the following delegate method deselects the cell.
Override it without calling super:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) being called, but not collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath)
I see delegate method for collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) being called, but not collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath). I also see the layout being asked for the preferred cell size, so I’m thinking they are being redrawn, but the cells already exist.
Figured it out.
UICollectionViewController has property clearsSelectionOnViewWillAppear that is automatically set to true.
All I had to do was set it to false.

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.