If I am using UICollectionViewController with diffableDataSource and tapping on cell - it highlights, but then gets unhighlighted while I'm still holding finger on it.
If to use UIViewController + collectionView with diffableDataSource - then everything working as intended, and cell keeps highlighted until I release finger from it. The same if to use UICollectionViewController with standard dataSource - everything working good.
Has anyone noticed this problem as well? Any advice or thoughts would be appreciated, maybe I'm just missing something, but for now it feels more like a bug from Apple side
You can see the example here:
https://github.com/ashishbl86/MockAppStore/blob/0ea8e74a4823c8c80bd7e8d5c6a9514958fbe459/MockAppStore/CollectionViewController.swift
Just add to CollectionViewController.swift file these methods:
override func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath) {
print("cell highlighted")
}
override func collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath) {
print("cell unhighlighted")
}
and you'll see that "cell unhighlighted" is printed while your finger is still on a cell
okay as it turned out we need to set
installsStandardGestureForInteractiveMovement = false
this variable installs a standard gesture recogniser for UICollectionViewController to drive the reordering process.
It is set to true by default, but in order to make things work the collectionView dataSource must declare its support for reordering items by implementing the appropriate methods.
In old good dataSource methods moveItemAt are marked as optional, while in diffableDataSource it is not declared as optional any more, which is the reason for this variable to cause the behaviour described in the issue above.
More info about this variable available in docs:
https://developer.apple.com/documentation/uikit/uicollectionviewcontroller/1623979-installsstandardgestureforintera
Related
My UICollectionViewCell is supposed to change its appearance when it comes into view (just a simple alpha/opacity change on one of the subviews). My code works fine when I scroll (vertically) slowly, but my attempt to access the cell (using cellForItemAt) returns nil when I scroll very quickly. Debugging + research reveals that cellForItem returns nil when the cell object isn't visible yet (even if the object is created), which is what happens when I scroll quickly.
What's the best way to to access the cell object when it's not visible yet?
From your description I'd say that the ideal way to access those cells is in function collectionView:willDisplayCell:forItemAtIndexPath:
That function tells you that the specified cell is just about to be displayed in the collection view.
So you can use it like this:
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
guard let cell = cell as? YourCell else { return }
// change alpha etc...
}
I found something strange while working with a collection view. I created a very simple collection view of customs cells with just one text label. If I populate the text labels with something simple like...
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
(cell as? CustomCell)?.textLabel = "ASDASDLAKJD"
}
then the collection view is scrolls smooth no matter how fast I scroll it.
However, I noticed that certain unique characters cause the scroll to lagg. Here is one character/symbol that I found laggs significantly
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
(cell as? CustomCell)?.textLabel = "ᴥ"
}
Now when I scroll, the flow is significantly choppy and almost unscrollable. Is there anything known with ios having trouble processing certain unique characters? BTW the fonts are systemFont if that matters. Thanks!
I was also fighting with this issue for quite some time. Perfect scroll for English localization. Horrible lags when scrolling with Chinese or Japanese localization.
The problem is view clipping. Seems to be a bug in UIKits font rendering engine.
Check your view hierarchy for views that activate clipsToBounds. For example:
UICollectionViewCell -> ViewA -> ViewB -> ViewC -> UILabel
where ViewB.clipsToBounds (or ViewB.layer.masksToBounds which is the same) is set to true. (A common case for settings this to true, would be for round corners)
Disable clipsToBounds for all child views of your UICollectionViewCell and try again. If your designer still wants to have round corners somewhere in your cells, you will have to get creative.
I am working on a grid based game (like minesweeper). It is a 2d grid that I am displaying using a UICollectionView that contains a set of custom UICollectionViewCell cells.
I want to be able to listen to touch events on individual cells. Is it fine to add an UITapGestureRecognizer() to each of the cells (on a 20x20 board, for example). Or is there a better way?
I understand from Ahmad's reply that using this works for single taps:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(indexPath.row)
}
However, I want different event handlers for single vs double taps on the cells.
If you want to let the base container as a UICollectionView (which I find it a good idea), I doubt that you need to add UITapGestureRecognizer to it, all you have to do is to let your class (Controller) to conforms to UICollectionViewDelegate its Delegate and implement collectionView:didSelectItemAtIndexPath:.
Also, the benefit that you'll get when implementing this method is you can easily determine which cell has been selected, by checking what is the indexPath.row of the selected cell, as follows:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(indexPath.row)
}
Don't forget to:
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource { //...
EDIT:
In case of you want to add a double tap functionality to your collection view, I suggest to check this answer to achieve it.
Hope this helped.
So I have been struggling with this for a while now. I have a UICollectionView that I am using as a menu. The cells are the options to switch to another page. The menu functions exactly how it should except that when you press a cell, say cell 0, it should pop to the next view. What I am finding is that the cell is registering the touch but when I try and determine which cell was pressed is when it falls apart. I have tried debugging it and to me it looks like indexPath has no value! I am using the didSelectItemAtIndexPath function, no it is not didDeselect (I checked that already from my searches on how to fix this). I will post the code but this one has really stumped me. Any help would be greatly appreciated!
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
{
NSLog("Pressed Cell")
if(indexPath == 0)
{
self.navigationController?.popToViewController(profileViewController, animated: true)
}
}
NSIndexPath comprises both a section and an item, which you can access as indexPath.item and indexPath.section. Assuming you have only one section (so its value is irrelevant), you can change your code to:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
{
NSLog("Pressed Cell")
if(indexPath.item == 0)
{
self.navigationController?.popToViewController(profileViewController, animated: true)
}
}
I am sure normally call the delegate method but if have add any Gesture to the view. Make sure before you add your gesture recognizers to you're view, make sure the boolean property "cancelTouchesInView" is set to false. This could be because the gesture is recognized so it ignores passing touches to the view causing the cell selection method not to be called. Link to property.
I'm having a UICollectionView that holds pictures as elements in its datastore.
I want to load a high resolution pic into the element only when its corresponding UICollectionViewCell is currently showing on screen. Later, when the UICollectionViewCell goes off screen, I want to return the element's UIImage into the low resolution version.
My question is, how can I detect when a UICollectionViewCell is going off screen?
(I tried using the prepareForReuse method but I can't predict when it will be called).
I'm currently using a piece of code that sits in scrollViewDidScroll, and every time that the view scrolls I'm checking the self.collectionView.visibleCells to see which cells has scrolled off screen.
It seems a bit of an overhead and I wonder if there is a method called on the UICollectionViewCell itself whenever it is being scrolled of screen ?
The collectionView:didEndDisplayingCell:forItemAtIndexPath: method on UICollectionViewDelegate should do what you want.
From Documentation. collectionView:didEndDisplayingCell is called right after it finishes displaying, not when it goes off screen
Use this method to detect when a cell is removed from a collection view, as opposed to monitoring the view itself to see when it disappears
collectionView:didEndDisplayingCell:forItemAtIndexPath: is the correct method to detect when a cell has gone from screen.
Meanwhile, I think it's more correct not to perform cleanup in collectionView:didEndDisplayingCell:forItemAtIndexPath: but to tell your cell to perform cleanup itself:
func collectionView(_ collectionView: UICollectionView,
didEndDisplaying cell: UICollectionViewCell,
forItemAt indexPath: IndexPath) {
cell.prepareForReuse()
}
With this approach your UICollectionViewDelegate doesn't have to know any implementation details of your UICollectionViewCell subclass. In the cell we'll override prepareForReuse method:
override func prepareForReuse() {
super.prepareForReuse()
imageView.image = lowResolutionImage
highResolutionImage = nil
}