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.
Related
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
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 am following an old tutorial about how to make a tag flow system on collection view. I have managed to display the full labels with different sizes each, but I am struggling to make the cells resize themselves. I am a beginner in Swift/Xcode so the error must be silly, but I can't figure it out.
I have followed this tutorial but mine doesn't work. I have changed a little bit according to what Xcode recommended because the syntax there is a bit old. I think I might have misunderstood something.
I have uploaded the project to GitHub, but what I think is the issue is the following:
This global variable is supposed to be a cell for an overwritten function to check the size:
var sizingCell: TagCell?
On the viewDidLoad() I grab the cell with
self.sizingCell = (cellNib.instantiate(withOwner: nil, options: nil) as NSArray).firstObject as! TagCell
And on the extension
extension ViewController: UICollectionViewDataSource{
...
}
I have the following functions to adjust the size of the cell (from the tutorial)
func configureCell(cell: TagCell, forIndexPath indexPath: IndexPath){
let tag = TAGS[indexPath.row]
cell.tagName.text = tag
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize{
self.configureCell(cell: self.sizingCell!, forIndexPath: indexPath)
return self.sizingCell!.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
}
I expected for different rows to be able to have different amount of tags (like in a flow layout) and each tag cell with a different width. Instead, every cell has the size size, but the tags are allowed to have different sizes.
I'm working on a UICollectionView that needs to display two pieces of information. First it needs to display an image, which I've done by setting it to the background view.
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell:UICollectionViewCell=collectionView.dequeueReusableCellWithReuseIdentifier("CellIdentifier", forIndexPath: indexPath) as UICollectionViewCell;
cell.backgroundView = UIImageView(image: introViewCollectionArray[indexPath.row].image)
return cell;
}
I have a string I also need to display above the image. The string is different for each image, but can be accessed using
introViewCollectionArray[indexPath.row].note
It looks like setting up a header for the entire UICollectionView is possible, but I don't see a way to have a header for each cell with the text content. Have I missed something, or do I need to approach this differently?
You need to add a supplementaryView or a decorationView to each cell, you do this by subclassing a collectionView layout or, more probably, a UICollectionViewFlowLayout
You may have a look at Apple Flow Layout documention and this decorationView tutorial
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
}