UICollectionView: current index path for page control

Viewed 59505

I'm using a UICollectionView with a flow layout to show a list of cells, I also have a page control to indicate current page, but there seems to be no way to get current index path, I know I can get visible cells:

UICollectionView current visible cell index

however there can be more than one visible cells, even if each of my cells occupies full width of the screen, if I scroll it to have two halves of two cells, then they are both visible, so is there a way to get only one current visible cell's index?

Thanks

11 Answers

Swift 5.1

The easy way and more safety from nil crash

func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    if collectionView == newsCollectionView {
        if newsPager.currentPage == indexPath.row {
            guard let visible = newsCollectionView.visibleCells.first else { return }
            guard let index = newsCollectionView.indexPath(for: visible)?.row else { return }
            newsPager.currentPage = index
        }

    }
}

for swift 4.2

@IBOutlet weak var mPageControl: UIPageControl!
@IBOutlet weak var mCollectionSlider: UICollectionView!

private var _currentIndex = 0
private var T1:Timer!
private var _indexPath:IndexPath = [0,0]

private func _GenerateNextPage(){
    self._currentIndex = mCollectionSlider.indexPathForItem(at: CGPoint.init(x: CGRect.init(origin: mCollectionSlider.contentOffset, size: mCollectionSlider.bounds.size).midX, y: CGRect.init(origin: mCollectionSlider.contentOffset, size: mCollectionSlider.bounds.size).midY))?.item ?? 0
    self.mPageControl.currentPage = self._currentIndex
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
    _SetTimer(AutoScrollInterval)
    _GenerateNextPage()
}

@objc private func _AutoScroll(){
    self._indexPath = IndexPath.init(item: self._currentIndex+1, section: 0)
    if !(self._indexPath.item < self.numberOfItems){
        _indexPath = [0,0]
    }
    self.mCollectionSlider.scrollToItem(at: self._indexPath, at: .centeredHorizontally, animated: true)
}
private func _SetTimer(_ interval:TimeInterval){
    if T1 == nil{
        T1 = Timer.scheduledTimer(timeInterval: interval , target:self , selector: #selector(_AutoScroll), userInfo: nil, repeats: true)
    }
}

you can skip the function _SetTimer() , thats for auto scroll

Note - I have found andykkt's answer useful but since it is in obj-c converted it to swift and also implemented logic in another UIScrollView delegate for a smoother effect.

func updatePageNumber() {
    // If not case to `Int` will give an error.
    let currentPage = Int(ceil(scrollView.contentOffset.x / scrollView.frame.size.width))
    pageControl.currentPage = currentPage
}

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    // This will be call when you scrolls it manually.
    updatePageNumber()
}

func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
    // This will be call when you scrolls it programmatically.
    updatePageNumber()
}

With UICollectionViewDelegate methods

func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    pageControl.currentPage = indexPath.row
}
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    if pageControl.currentPage == indexPath.row {
        pageControl.currentPage = collectionView.indexPath(for: collectionView.visibleCells.first!)!.row
    }
}

Swift 5.0


 extension youriewControllerName:UIScrollViewDelegate{
        func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    
            let pageWidth = self.collectionView.frame.size.width
            pageControl.currentPage = Int(self.collectionView.contentOffset.x / pageWidth)
        }
    }
(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    CGFloat pageWidth = _cvImagesList.frame.size.width;
    float currentPage = _cvImagesList.contentOffset.x / pageWidth;

     _pageControl.currentPage = currentPage + 1;
    NSLog(@"finishPage: %ld", (long)_pageControl.currentPage);
}
Related