A custom paging behavior that peeks the previous and next items in a collection view

MSPeekCollectionViewDelegateImplementation

Current design trends require complex designs which allow horizontal scrolling inside vertical scrolling. So to show the users that they can scroll vertically, a peeking item should be shown on the side. This library does exactly that. I wrote this library because there's no pod that does this simple feature. Also, other libraries require me to inherit from a UICollectionViewController, which doesn't give alot of freedom if I'm inheriting from other View Controllers.

Example

To run the example project, clone the repo, and run pod install from the Example directory first.

Requirements

  • XCode 9.3
  • Swift 3.2

This pod will probably work on older versions of XCode but I haven't tested it.

Installation

MSPeekCollectionViewDelegateImplementation is available through CocoaPods. To install
it, simply add the following line to your Podfile:

pod 'MSPeekCollectionViewDelegateImplementation'

Usage

Storyboard

  1. Drag-Drop a UICollectionView

  2. Set the reuse identifier for the collection view's cell to Cell

  3. Create a reference for the collection view

@IBOutlet weak var collectionView: UICollectionView!
  1. Bind collection view to outlet

  2. Import library

import MSPeekCollectionViewDelegateImplementation
  1. Create a variable of type MSPeekCollectionViewDelegateImplementation
var delegate: MSPeekCollectionViewDelegateImplementation!
  1. In viewDidLoad(), Configure the collectionView for peek behavior:
collectionView.configureForPeekingDelegate()
  1. In viewDidLoad(), initialize the delegate using the basic initializer:
delegate = MSPeekCollectionViewDelegateImplementation()

Or you can use whatever arguments from the ones below (Can be combined together as needed):

delegate = MSPeekCollectionViewDelegateImplementation(cellSpacing: 10)
delegate = MSPeekCollectionViewDelegateImplementation(cellPeekWidth: 20)
//scrollThreshold is the minimum amount of scroll distance required to move to the adjacent item.
delegate = MSPeekCollectionViewDelegateImplementation(scrollThreshold: 150)
//maximumItemsToScroll is the maximum number of items that can be scrolled if the scroll distance is large
delegate = MSPeekCollectionViewDelegateImplementation(maximumItemsToScroll: 3)
//numberOfItemsToShow is the number of items that will be shown at the same time.
delegate = MSPeekCollectionViewDelegateImplementation(numberOfItemsToShow: 3)

  1. In viewDidLoad(), set the collection view's delegate:
collectionView.delegate = delegate
  1. Create the data source implementation as an extension for the ViewController
extension ViewController: UICollectionViewDataSource {
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 4
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
        //TODO: Configure cell
        return cell
    }
}
  1. In viewDidLoad(), Set the collection view's data source to self
collectionView.dataSource = self

Working Example

import UIKit
import MSPeekCollectionViewDelegateImplementation

class ViewController: UIViewController {
    
    @IBOutlet weak var collectionView: UICollectionView!
    let delegate = MSPeekCollectionViewDelegateImplementation()

    override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.configureForPeekingDelegate()
        collectionView.delegate = delegate
        collectionView.dataSource = self
    }


}

extension ViewController: UICollectionViewDataSource {
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 4
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
        cell.contentView.backgroundColor = UIColor.red
        return cell
    }
}

Features

Getting the offset of a specific index

The implementation introduces a function (scrollView(_:,contentOffsetForItemAtIndex:) -> CGFloat) to get the content offset of an item at a specific index. This can be helpful if you want to scroll the collection view programmatically to a specific index (Maybe create a carousel with a timer). You can do that by using the following code:

let secondItemContentOffset = delegate.scrollView(collectionView, contentOffsetForItemAtIndex: 1)
collectionView.setContentOffset(CGPoint(x: secondItemContentOffset, y: 0), animated: false)

Customization

Vertical Scroll Direction

The implementation supports collection views with vertical directions and will automatically position cells correctly, you can set the scrolling and peeking to be vertical using:

delegate = MSPeekCollectionViewDelegateImplementation(scrollDirection: .vertical)
collectionView.configureForPeekingDelegate(scrollDirection: .vertical)

Or alternatively:

let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
delegate = MSPeekCollectionViewDelegateImplementation(scrollDirection: layout.scrollDirection)
collectionView.configureForPeekingDelegate(scrollDirection: layout.scrollDirection)

Subclassing

You can subclass the delegate implementation to integrate other features to it, or listen to certain events:

class SelectablePeekCollectionViewDelegateImplementation: MSPeekCollectionViewDelegateImplementation {
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        print("Item Selected")
    }
    
    override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        super.scrollViewWillBeginDragging(scrollView)
        // Add other code to support other features
    }
}

Note: Make sure you call super on overriden functions (Unless you know what you're doing)

GitHub