I want to implement UICollectionView
that scrolls horizontally and infinitely?
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 :)
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:
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) } }
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) } } }
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With