Reset Scrollview in UICollectionViewCell after scrolling - ios

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

Related

Set dynamic height constraint on collectionviewcell which is in uitableviewcell based on image height

I am trying to add custom height constraint to collection view cell which is present in uitableview cell. The height of constraint is dependent on image which needs to be fetched from url(Using sdwebimage to fetch image in collectionviewcell). Trying to to update constraint in sizeforitem in collectionviewcell. The problem i am facing because of this is that initially fixed height is getting set as per storyboard constraint and as soon as i am scrolling the height constraint is getting updated which is creating problems.
Need to know following
1)Need to know a proper way to implement this thing
2)Where and how should i update constraint so that the tableviewcell as well as collectionviewcell will be set to exact height as of image
3)Exactly where should i download images from sdwebimage
https://i.stack.imgur.com/brWvy.png
You can try to set height for collection view cell in
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {}
I think this will be helpful to you.
This function will be called every time once user start to swipe Up/Down or Left/Right in the case of the Horizontal collection view.

setContentOffset not working for UIScrollView inside UITableViewCell for the first time

I have a horizontal UIScrollView which is added as a subview inside custom UITableViewCell. For some cases, I want to change the scrollview content offset which I achieve by doing this code
cell.horizontalScrollView.setContentOffset(CGPoint(x: 300, y: cell.horizontalScrollView.contentOffset.y), animated: true)
inside the UITableView's cellForRowAtIndexPath function and tableview.reloadData() is called whenever I needed to scroll it programmatically.
The issue is, this does not work for the first time. ie,
When the tableview is loaded for the first time, the scrollviews inside the visible cells does not get scrolled according to the code. Its offset is just at (0,0). ---- (This is the issue!)
But after that, when I scrolled the tableView, then the new cells(reused cells) gets updated and scrollviews position gets changed. (which I needed from the start itself!)
So, I want to change the UIScrollView's initial offset(position) programmatically. How can I achieve that?
Atlast, I found the solution after sitting for a couple of days!
Had to implement layoutIfNeeded for the UITableViewCell inside the cellForRowAtIndexPath method before setting the contentOffset for the UIScrollView.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
cell.layoutIfNeeded()
cell.horizontalScrollView.setContentOffset(CGPoint(x: 300, y: cell.horizontalScrollView.contentOffset.y), animated: false)
return cell
}
The best way to implement this is to subclass your cell and then set the content offset in layoutSubviews method.

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?

How to know that tableView started scrolling

I have a doubt: is there any way to intercept a tableView scrolling to add it an action? For example my prototype cell background is red, touching up inside a cell its background color begin blue and scrolling the tableView background color return red.
Is it possible to do this?!
Thanks in advance.
UITableView inherits from UIScrollView and UITableViewDelegate extends UIScrollViewDelegate.
Particularly you may be interested in scrollViewDidScroll method. So, in your UITableViewDelegate implementation, add the following method:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
NSLog("Table view scroll detected at offset: %f", scrollView.contentOffset.y)
}
In the ViewController that is set as the delegate for the tableView, you can also set the delegate methods of a scrollView. These will be called when the tableView is scrolled as it contains a scrollView.
e.g:
extension ViewController: UIScrollViewDelegate {
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
}
}
You are better of keeping a variable around e.g. var isBackgroundColorRed: Bool = true
And another for scroll y position when you set the background color to blue. e.g.
var blueBackgroundColorYOffset: CGFloat?
When you set the background color to blue, set the y offset, to the contentView.origin.y.
Then in the delegate for the tableview (which subclasses UIScrollView)
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if !isBackgroundColorRed {
// apply some color blending between blue and red based on
// the amount of movement on y axis, until you reach the limit
// its also important to take the absolute value of the blueBackgroundColorYOffset
// and the offset here in scrollViewDidScroll to cover up or down movements
// say 1 cell's height, then set your isBackgroundColorRed to true
}
}
Try adding this to your project and update yur bridging header.
UIColor-CrossFade
This technique will give you a nice UX rather than a sudden background change.
CellForRow is a way to detect scrolling which also gives you the indexPath.row of the next cell entering the view. If the user scrolls such a short distance that a new or recycled cell is not even configured, then dragging will not be detected. However even with built in methods, such as scrollViewWillBeginDragging, short distance scrolling will not be detected.
var lastCellForRowAtIndex = 0
var isScrollingUp = false
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
isScrollingUp = indexPath.row > lastCellForRowAtIndex
lastCellForRowAtIndex = indexPath.row
}
cellForRowAtIndexPath will be repeatedly called when you are scrolling the tableView unless you have very few number of rows.

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