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.
To run the example project, clone the repo, and run pod install
from the Example directory first.
- XCode 9.3
- Swift 3.2
This pod will probably work on older versions of XCode but I haven't tested it.
MSPeekCollectionViewDelegateImplementation is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod 'MSPeekCollectionViewDelegateImplementation'
-
Drag-Drop a
UICollectionView
-
Set the reuse identifier for the collection view's cell to
Cell
-
Create a reference for the collection view
@IBOutlet weak var collectionView: UICollectionView!
-
Bind collection view to outlet
-
Import library
import MSPeekCollectionViewDelegateImplementation
- Create a variable of type
MSPeekCollectionViewDelegateImplementation
var delegate: MSPeekCollectionViewDelegateImplementation!
- In
viewDidLoad()
, Configure thecollectionView
for peek behavior:
collectionView.configureForPeekingDelegate()
- 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)
- In
viewDidLoad()
, set the collection view's delegate:
collectionView.delegate = delegate
- 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
}
}
- In
viewDidLoad()
, Set the collection view's data source toself
collectionView.dataSource = self
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
}
}
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)
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)
You can implement the delegate of the peek implementation to listen to specific events. This is the protocol of the delegate
@objc public protocol MSPeekImplementationDelegate: AnyObject {
///Will be called when the current active index has changed
@objc optional func peekImplementation(_ peekImplementation: MSPeekCollectionViewDelegateImplementation, didChangeActiveIndexTo activeIndex: Int)
///Will be called when the user taps on a cell at a specific index path
@objc optional func peekImplementation(_ peekImplementation: MSPeekCollectionViewDelegateImplementation, didSelectItemAt indexPath: IndexPath)
}
To listen to those events, you can do something like this:
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
var peekImplementation: MSPeekCollectionViewDelegateImplementation!
override func viewDidLoad() {
super.viewDidLoad()
//Set the collection view
//...
peekImplementation = MSPeekCollectionViewDelegateImplementation()
peekImplementation.delegate = self
}
}
extension ViewController: MSPeekImplementationDelegate {
func peekImplementation(_ peekImplementation: MSPeekCollectionViewDelegateImplementation, didChangeActiveIndexTo activeIndex: Int) {
print("Changed active index to \(activeIndex)")
}
func peekImplementation(_ peekImplementation: MSPeekCollectionViewDelegateImplementation, didSelectItemAt indexPath: IndexPath) {
print("Selected item at \(indexPath)")
}
}
You can subclass the delegate implementation to integrate other features to it, or listen to certain events:
class SelectablePeekCollectionViewDelegateImplementation: MSPeekCollectionViewDelegateImplementation {
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)
Maher Santina, [email protected]
Any contribution is highly appreciated, please see CONTRIBUTING.md for more info.
MSPeekCollectionViewDelegateImplementation is available under the MIT license. See the LICENSE file for more info.