6

I have a collection view where the cell is of the size exactly to the collectionView, so each cell should occupy the whole screen. I have implemented a functionality where the cell is snapped to the complete view whenever it's dragged or decelerated through the scroll. This is how the UX works.

https://drive.google.com/open?id=1v8-WxCQUzfu8V_k9zM1UCWsf_-Zz4dpr

What I want:

As you can see from the clip, the cell snaps to the whole screen. Now, I want to execute a method after it snaps. Not before or not when it's partially displayed.

Following is the code I have written for snapping effect :

func scrollToMostVisibleCell(){
    let visibleRect = CGRect(origin: collectionView.contentOffset, size: collectionView.bounds.size)
    let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
    let visibleIndexPath = collectionView.indexPathForItem(at: visiblePoint)!
    collectionView.scrollToItem(at: visibleIndexPath as IndexPath, at: .top, animated: true)
    print("cell is ---> ", visibleIndexPath.row)
}



func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    scrollToMostVisibleCell()
}

func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    scrollToMostVisibleCell()
    if !decelerate {
        scrollToMostVisibleCell()
    }
}

If I use willDisplayCell method, then it' just going to return me as soon as the cell is in the view, even if it's just peeping in the collectionView.

Is there a way where I can check if the cell is completely in the view and then I can perform a function?

I have scrapped the internet over this question, but ain't able to find a satisfactory answer.

Aakash Dave
  • 866
  • 1
  • 15
  • 30
  • The answer referenced by Mangesh will return all visible cells - even those that are only ***partially*** visible. Here is another answer that checks if the cell is ***fully*** visible: https://stackoverflow.com/a/46833430/6257435 – DonMag Oct 30 '18 at 13:25
  • tried this already it just gives me an empty array. I am placing this in `scrollToMostVisibleCell()`. Should I be placing it elsewhere? – Aakash Dave Oct 30 '18 at 20:58
  • My goal is that the value should be returned as soon as the scrolling stops and the view is snapped to the collection view. (as shown in the demo video). I dont want to press a button ti get the value everytime. – Aakash Dave Oct 30 '18 at 22:39
  • Have you tried simply setting `.isPagingEnabled = true` on your collection view? It sounds like that's what you are going for... – DonMag Oct 31 '18 at 12:44

2 Answers2

1

Here is a complete example of a "full screen" vertical scrolling collection view controller, with paging enabled (5 solid color cells). When the cell has "snapped into place" it will trigger scrollViewDidEndDecelerating where you can get the index of the current cell and perform whatever actions you like.

Add a new UICollectionViewController to your storyboard, and assign its class to VerticalPagingCollectionViewController. No need to change any of the default settings for the controller in storyboard - it's all handled in the code below:

//
//  VerticalPagingCollectionViewController.swift
//
//  Created by Don Mag on 10/31/18.
//

import UIKit

private let reuseIdentifier = "Cell"

class VerticalPagingCollectionViewController: UICollectionViewController {

    private var collectionViewFlowLayout: UICollectionViewFlowLayout {
        return collectionViewLayout as! UICollectionViewFlowLayout
    }

    private var colors: [UIColor] = [.red, .green, .blue, .yellow, .orange]

    override func viewDidLoad() {
        super.viewDidLoad()

        // Register cell classes
        self.collectionView?.register(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)

        // enable paging
        self.collectionView?.isPagingEnabled = true

        // set section insets and item spacing to Zero
        collectionViewFlowLayout.sectionInset = UIEdgeInsets.zero
        collectionViewFlowLayout.minimumLineSpacing = 0
        collectionViewFlowLayout.minimumInteritemSpacing = 0
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        if let cv = collectionViewLayout.collectionView {
            collectionViewFlowLayout.itemSize = cv.frame.size
        }
    }

    override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        if let iPath = collectionView?.indexPathsForVisibleItems.first {
            print("DidEndDecelerating - visible cell is: ", iPath)
            // do what you want here...
        }
    }

    override func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return colors.count
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
        cell.backgroundColor = colors[indexPath.item]
        return cell
    }

}
DonMag
  • 69,424
  • 5
  • 50
  • 86
  • Hey thanks for this, but I am not looking for just basic paging! I want the user to have freedom of scrolling totally as they want it, but when they stop, the cell with most view snaps to the screen. Now after its snapped, i want the current indexpath and then perform a method. I hope that clears the doubt. This answer that you provided will just give me a cell to cell paging. I am more of looking at lets say, user scroll from cell 0 to 10. And then 10 is partially on the screen but acquires most of the view, hence it snaps to the total view. – Aakash Dave Oct 31 '18 at 14:26
  • OK - if you're still trying to iron this out, you might want to take a look at this project (not mine): https://github.com/hershalle/CollectionViewWithPaging-simplerExample ... it's doing horizontal scrolling, but should be easy enough to convert it to handle vertical scrolling (using `y` and `height` instead of `x` and `width` values). – DonMag Oct 31 '18 at 14:46
0

Using followedCollectionView.indexPathsForVisibleItems() to get visible cells visibleIndexPaths and check your indexPath is contained in visibleIndexPaths or not, before doing anything with cells. Ref : @anhtu Check whether cell at indexPath is visible on screen UICollectionView

Also from Apple : var visibleCells: [UICollectionViewCell] { get } . Returns an array of visible cells currently displayed by the collection view.

Mangesh
  • 2,257
  • 4
  • 24
  • 51