title: Using IGListKit for horizontal list with paging
body: <p>I am building an app that relies heavily on collection view so I decided to use IGListKit. Here is what I have to do:</p>
  
  <p><a href="https://i.stack.imgur.com/eQfoy.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/eQfoy.png" alt="enter image description here"></a></p>
  
  <p>and here is what I have:</p>
  
  <p><a href="https://i.stack.imgur.com/TYnzE.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/TYnzE.png" alt="enter image description here"></a>
  ... and it is not moving in any direction!</p>
  
  <p>Here is my code:</p>
  
  <pre><code>import UIKit
  import IGListKit
  
  class MatchCollectionViewCell: UICollectionViewCell {
      var helloWorld = "Hello World"
  }
  
  class LabelSectionController: ListSectionController {
      override func sizeForItem(at index: Int) -&gt; CGSize {
          return CGSize(width: collectionContext!.containerSize.width, height: 55)
      }
  
      override func cellForItem(at index: Int) -&gt; UICollectionViewCell {
          return collectionContext!.dequeueReusableCell(of: MatchCollectionViewCell.self, for: self, at: index)
      }
  }
  
  class MatchViewController: UIViewController {
  
      // MARK: - Outlets
      @IBOutlet weak var matchCollectionView: UICollectionView!
  
  
      // MARK: - Variables
      let layout = UICollectionViewFlowLayout()
      lazy var adapter: ListAdapter = {
          return ListAdapter(updater: ListAdapterUpdater(), viewController: self, workingRangeSize: 0)
      }()
      var users: [User] = []
  
      // MARK: - Lifecycle
      override func viewDidLoad() {
          super.viewDidLoad()
          matchCollectionView.dataSource = self
          matchCollectionView.delegate = self
          matchCollectionView.collectionViewLayout = CenterCellCollectionViewFlowLayout()
          matchCollectionView.isPagingEnabled = true
          layout.minimumLineSpacing = 8
          layout.scrollDirection = .horizontal
  
          for i in 1...10 {
              let newMatchingPreferences = MatchingPreferences(preferedAge: (23, 33))
              let newUser = User(id: i, name: "Some Name \(i)", email: "some_name@gmail.com", age: 27, location: "New York", isOnboarded: true, isPremium: true, matchingPreferences: newMatchingPreferences)
              users.append(newUser)
          }
      }
  
      // MARK: - Actions
  
      // MARK: - Methods
  }
  
  extension MatchViewController: ListAdapterDataSource {
      func objects(for listAdapter: ListAdapter) -&gt; [ListDiffable] {
          return self.users
      }
  
      func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -&gt; ListSectionController {
          return LabelSectionController()
      }
  
      func emptyView(for listAdapter: ListAdapter) -&gt; UIView? {
          let view = UIView()
          view.backgroundColor = .lightGray
          return view
      }
  }
  
  extension MatchViewController: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
      func numberOfSections(in collectionView: UICollectionView) -&gt; Int {
          return 1
      }
  
      func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -&gt; Int {
          return 10
      }
  
      func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -&gt; UICollectionViewCell {
          let cell = matchCollectionView.dequeueReusableCell(withReuseIdentifier: "MatchCell", for: indexPath) as! MatchCollectionViewCell
          print(cell.helloWorld)
          return cell
      }
  
      func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -&gt; CGSize {
          return CGSize(width: view.bounds.width - 32, height: view.bounds.height - 40)
      }
  
  
      func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -&gt; UIEdgeInsets {
          return .zero
      }
  
      func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -&gt; CGFloat {
          return 0
      }
  
      func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -&gt; CGFloat {
          return 0
      }
  }
  
  class CenterCellCollectionViewFlowLayout: UICollectionViewFlowLayout {
      var mostRecentOffset: CGPoint = CGPoint.zero
  
      override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -&gt; CGPoint {
  
          if velocity.x == 0 {
              return self.mostRecentOffset
          }
  
          guard let cv = self.collectionView,
              let attributesForVisibleCells = self.layoutAttributesForElements(in: cv.bounds) else {
                  // Fallback
                  self.mostRecentOffset = super.targetContentOffset(forProposedContentOffset: proposedContentOffset)
                  return self.mostRecentOffset
          }
  
          let halfWidth = cv.bounds.size.width * 0.5
  
          var candidateAttributes: UICollectionViewLayoutAttributes?
          for attributes in attributesForVisibleCells {
  
              // Skip comparison with non-cell items (headers and footers)
              if attributes.representedElementCategory != UICollectionElementCategory.cell {
                  continue
              }
  
              if (attributes.center.x == 0) || (attributes.center.x &gt; (cv.contentOffset.x + halfWidth) &amp;&amp; velocity.x &lt; 0) {
                  continue
              }
  
              candidateAttributes = attributes
          }
  
          // Beautification step , I don't know why it works!
          if proposedContentOffset.x == -(cv.contentInset.left) {
              return proposedContentOffset
          }
  
          guard let attributes = candidateAttributes else {
              return mostRecentOffset
          }
  
          self.mostRecentOffset = CGPoint(x: floor(attributes.center.x - halfWidth), y: proposedContentOffset.y)
  
          return self.mostRecentOffset
      }
  }
  </code></pre>
  
  <p>Basically, I know how to make crude collection view but paging and centering is a bit hard for me. I feel like I am close but obviously something is missing. I hope somebody can point me to a right direction! Thanks.</p>