Subview of custom UICollectionViewCell apparently not redrawn when cell is reused - ios

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?

Related

My iOS UIScrollView refreshes only off-screen

In my iOS app, I do have my UIScrollView that contains UITableView with classes (cells) containing UIImages.
Now I do need to update the UIImages programmatically.
However the visible part of my UIScrollView does not update, unless I do scroll the changed item (UIImage) off the visible screen and back.
I've tried setNeedsLayout, setNeedsDidsplay for my UIScrollView, UITableView -- no success.
Any idea on what I could do wrong, or how can I force the ScrollView to update without actual scrolling?
Solved.
At first, UITableView inside UIScrollView was unnecessary since UITableView scrolls itself.
The solution was to use ReloadData method for my TableView.
this is because tableView calls func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell when cell reappear on screen so that it can reuse cell object to save memory, so call tableView.reloadData() or func reloadRows(at indexPaths: [IndexPath], with animation: UITableView.RowAnimation) whenever ( after data is changed obviously ) you want to reload the entire tableView or particular cell respectively.
And no need to use scrollView for tableView since tableView inherits from scrollView.

How to configure stackview with dynamic content inside UITableViewCell?

I have UITableViewCell which looks like CardView. Inside cell, I should display some dynamic content. That is why I have stackview inside my cell that will have my dynamic content. But the problem is that cellForRowAt method is called every time while scrolling and stackview starts having extra elements.
How should I solve this problem?
How I handled problem by myself?
I found prepareForReuse method of UITableViewCell which is called before re-configuring by cell. In that method, I clean my stackView. And now, my stackview will not have extra views. But, it is too bad for performance. The next way I tried is holding some flag inside my cell that tells me was stackview already configured. But, this approach didn't help (yes, it will not add extra elements, but content inside cells gets incorrect placement) as checking stackview length.
Here is my pseudo-code:
/// called in cellForRowAt
func configure(item: Item) {
item.forEach {
stackView.addArrangedSubview(ItemView(item))
}
}
func prepareForReuse() {
stackView.arrangedSubviews.forEach {
$0.removeFromSuperView()
}
}
If the prepareForReuse and dequeuing methods leads to exceeding 0.0167 sec (60 frames per second) then maybe in your edge case it will be better to create a cell instead of dequeuing it.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.configure(item: item)
return cell
}

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

Which method to setup UIview?

The cells in my collectionView contains a UIView that should have a shadow. The problem is that if I set the shadow in the cell's AwakeFromNib method the shadow will be applied to the UIView before it has the correct size (which means the shadow will have a different size than the UIView). Which method is called when the cell and all its subviews have been correctly loaded and setup?
you can do any UI related work for collection view in this delegate method
func collectionView(collectionView: UICollectionView, willDisplayCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath)
It will always work

detecting when an iOS UICollectionCell is going off screen

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
}

Resources