detecting when an iOS UICollectionCell is going off screen - ios

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
}

Related

UICollectionViewController + diffableDataSource - cell gets unhighlighted while your finger is still on it

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

How do I access a UICollectionViewCell object that isn't yet visible?

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

Reset Scrollview in UICollectionViewCell after scrolling

I'm creating a photo gallery with this concept. For listing the images, I've used UICollectionView. Each image is stored in full screen size custom cells. Inside the cell, I have UIScrollView and inside it I have UIImageView. ScrollView is used to zoom the image. All working fine but, when I zoom in one image and scroll to another cell without zooming out, I want that previous cell's scrollview to be reset.
Inside custom cell class, I set ZoomScale for each cell's scrollview when they are initiated like so:
func configureCell(_ photo: String){
albumPhoto.image = UIImage(named: photo)
scrollView.setZoomScale(1.0, animated: false)
}
I configure each cell in cellForItemAt function before returning the cell.
When I zoom in in the first cell, scroll to second and scroll back to the first cell, the image is still zoomed in. But if I scroll to second and third cell and then return to first cell, the image is zoomed out to default.
How can I achieve that even after scrolling immediately back from second cell to first, the scroll view will be set to default zoom scale.
you need to implement this method: https://developer.apple.com/documentation/uikit/uicollectionviewdelegate/1618087-collectionview
and zoom out the scrollView before displaying the cell. This method will handle your case. Happy coding ;)
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath)
First make your class conforms to
class profViewController: UIScrollViewDelegate
As UICollectionView is a subclass of UIScrollView
Second implement this delegate method
func scrollViewDidScroll(_ scrollView: UIScrollView)
{
let cd = areaSettTable.visibleCells as!
[profTableViewCell]
//////
loop here
}
then loop through this array and call the function that resets the zoom for every cell
note : you should change profTableViewCell name to your cell

Why using SDWebImage in a UITableViewCell triggers ImageViews not to render properly?

I have a UITableViewController containing my own cells that I dequeue in cellForRowAtIndexPath.
After dequeuing, I configure the cell and reload, asynchronously, the image for that cell.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCellWithIdentifier("PeopleCell", forIndexPath: indexPath) as? PeopleListViewCell {
cell.configureCell(headImageUrl)
}
}
In my PeopleListView class,
func configureCell(img:NSURL) {
if headImageView == nil { // to avoid allocation memory if not used
headImageView = UIImageView()
addSubView(headImageView)
}
headImageView.sd_cancelCurrentImageLoad()
headImageView.sd_setImageWithURL(headImageUrl)
}
It works fine at the first loading and also while scrolling.
But when I push another viewController after
didSelectRowAtIndexPath
and come back to the list after the
dismissViewController()
I end up with a weird effect on my UIImageView , it's kind of a stacked or ghost image effect..
I'm having a hard time to figure out where is even triggered as when I m coming back from the viewController, cellForRowAtIndexPath is not called.
This actually has nothing to do with asynchronous image loading.
The images I had was displayed within circles, with a cornerRadius.
Somehow, it was displayed without any problem at first load...
The issue here is I simply forgot the
headImageView.layer.maskToBounds = true
The result I got before setting maskToBounds to true was that I had the feeling that multiple images were located within the headImageView (UIImageView). If you ever have some artefacts like that, I hope this question/answer will help you.

Subview of custom UICollectionViewCell apparently not redrawn when cell is reused

I have a UICollectionViewController whose collectionView:cellForItemAtIndexPath: returns a subclass of UICollectionViewCell. This cell has a couple of subviews which are also configured as the cell's outlets (e.g. cell.dot, a custom UIView that draws a colored dot in its drawRect:).
What appears to happen is that (at least some of) those subviews are not refreshed when scrolling the collection, i.e. when cells are reused. The problem goes away if I explicitly mark the subview as needing to be redrawn like so:
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
// ...
assert(cell.dot.superview == cell.contentView)
cell.dot.setNeedsDisplay() // apparently required for dot to be redrawn
return cell
}
Is it normal behavior that subviews of reused UICollectionViewCells are not automatically redrawn? Am I missing something else?

Resources