Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save danielCarlosCE/7a5f80dc6087773ba147be4dc72da826 to your computer and use it in GitHub Desktop.
Save danielCarlosCE/7a5f80dc6087773ba147be4dc72da826 to your computer and use it in GitHub Desktop.
Horizontal paging for collections based on the content size
//
// ContentSizePagingCollectionDelegate.swift
// CardsCarousel
//
// Created by Daniel Carlos Souza Carvalho on 2/20/21.
//
import UIKit
/// Horizontal paging for collections based on the content size
///
/// While isPagingEnabled stops on multiples of the scroll view’s **bounds** when the user scrolls,
/// this calculates the offset based on the resulting **content size**, given the cell width and the cell distances.
///
/// Make sure to set isPagingEnabled to false in the collection
///
/// After reloading the data, call collection.layoutIfNeeded().
/// That way collection.contentSize = collection.collectionViewLayout.collectionViewContentSize
class ContentSizePagingCollectionDelegate: NSObject, UICollectionViewDelegateFlowLayout {
private let cellWitdhPercentage: CGFloat = 0.85
private let idealCellDistance: CGFloat = 16
private var currentPositionIndex = 0
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let cellWidth = cellWitdh(forCollectionViewWidth: collectionView.frame.width)
return CGSize(width: cellWidth, height: collectionView.frame.height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return cellDistance(forCollectionViewWidth: collectionView.frame.width)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
let collectionViewWidth = collectionView.frame.width
let sides = collectionViewWidth - cellWitdh(forCollectionViewWidth: collectionViewWidth)
let horizontalInset: CGFloat = sides/2
return UIEdgeInsets(top: 0, left: horizontalInset, bottom: 0, right: horizontalInset)
}
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let offsetWidthForOneItem = caculateContentOffsetForOneItem(scrollView)
let offsetForCurrentItem = { CGPoint(x: offsetWidthForOneItem * CGFloat(self.currentPositionIndex), y: targetContentOffset.pointee.y) }
enum HorizontalDirection { case left, right }
let horizontalDirection: HorizontalDirection = velocity.x > 0 ? .right : .left
switch horizontalDirection {
case .left:
let isFirstItem = currentPositionIndex <= 0
guard isFirstItem == false else {
targetContentOffset.pointee = offsetForCurrentItem()
return
}
currentPositionIndex -= 1
targetContentOffset.pointee = offsetForCurrentItem()
case .right:
let isLastItem = (scrollView.contentOffset.x + offsetWidthForOneItem >= scrollView.contentSize.width)
guard isLastItem == false else {
targetContentOffset.pointee = offsetForCurrentItem()
return
}
currentPositionIndex += 1
targetContentOffset.pointee = offsetForCurrentItem()
}
}
private func cellWitdh(forCollectionViewWidth collectionViewWidth: CGFloat) -> CGFloat {
let itemWidth = collectionViewWidth * cellWitdhPercentage
return itemWidth
}
private func cellDistance(forCollectionViewWidth collectionViewWidth: CGFloat) -> CGFloat {
let sides = collectionViewWidth - cellWitdh(forCollectionViewWidth: collectionViewWidth)
let oneSide = sides/2
let final = min(idealCellDistance, oneSide/2)
return final
}
private func caculateContentOffsetForOneItem(_ scrollView: UIScrollView) -> CGFloat {
let cellItemWidth = cellWitdh(forCollectionViewWidth: scrollView.frame.width)
let sides = scrollView.frame.width - cellItemWidth
let oneSide: CGFloat = sides/2
let nextItemVisiblePart = scrollView.frame.width - (oneSide + cellItemWidth + cellDistance(forCollectionViewWidth: scrollView.frame.width))
return oneSide + (cellItemWidth - nextItemVisiblePart)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment