Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement horizontally infinite scrolling UICollectionView?

I want to implement UICollectionView that scrolls horizontally and infinitely?

like image 389
Abdelrahman Avatar asked Dec 21 '15 12:12

Abdelrahman


2 Answers

If your data is static and you want a kind of circular behavior, you can do something like this:

var dataSource = ["item 0", "item 1", "item 2"]  func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {     return Int.max // instead of returnin dataSource.count }  func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {      let itemToShow = dataSource[indexPath.row % dataSource.count]     let cell = UICollectionViewCell() // setup cell with your item and return     return cell } 

Basically you say to your collection view that you have a huge number of cells (Int.max won't be infinite, but might do the trick), and you access your data source using the % operator. In my example we'll end up with "item 0", "item 1", "item 2", "item 0", "item 1", "item 2" ....

I hope this helps :)

like image 146
cicerocamargo Avatar answered Oct 06 '22 09:10

cicerocamargo


Apparently the closest to good solution was proposed by the Manikanta Adimulam. The cleanest solution would be to add the last element at the beginning of the data list, and the first one to the last data list position (ex: [4] [0] [1] [2] [3] [4] [0]), so we scroll to the first array item when we are triggering the last list item and vice versa. This will work for collection views with one visible item:

  1. Subclass UICollectionView.
  2. Override UICollectionViewDelegate and override the following methods:

    public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {     let numberOfCells = items.count     let page = Int(scrollView.contentOffset.x) / Int(cellWidth)     if page == 0 { // we are within the fake last, so delegate real last         currentPage = numberOfCells - 1     } else if page == numberOfCells - 1 { // we are within the fake first, so delegate the real first         currentPage = 0     } else { // real page is always fake minus one        currentPage = page - 1     }     // if you need to know changed position, you can delegate it     customDelegate?.pageChanged(currentPage) }   public func scrollViewDidScroll(_ scrollView: UIScrollView) {     let numberOfCells = items.count     if numberOfCells == 1 {         return     }     let regularContentOffset = cellWidth * CGFloat(numberOfCells - 2)     if (scrollView.contentOffset.x >= cellWidth * CGFloat(numberOfCells - 1)) {         scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x - regularContentOffset, y: 0.0)     } else if (scrollView.contentOffset.x < cellWidth) {         scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x + regularContentOffset, y: 0.0)     } } 
  3. Override layoutSubviews() method inside your UICollectionView in order to always to make a correct offset for the first item:

    override func layoutSubviews() {     super.layoutSubviews()      let numberOfCells = items.count     if numberOfCells > 1 {         if contentOffset.x == 0.0 {             contentOffset = CGPoint(x: cellWidth, y: 0.0)         }     } } 
  4. Override init method and calculate your cell dimensions:

    let layout = self.collectionViewLayout as! UICollectionViewFlowLayout cellPadding = layout.minimumInteritemSpacing cellWidth = layout.itemSize.width 

Works like a charm! If you want to achieve this effect with collection view having multiple visible items, then use solution posted here.

like image 32
Bio-Matic Avatar answered Oct 06 '22 08:10

Bio-Matic