Evernote like Stretch UICollectionViewFlowLayout in Swift 5

ARNSpace Stretch UICollectionViewFlowLayout

Evernote like UICollectionViewFlowLayout. (Swift language)

Swift 2.1
Platforms iOS
Xcode 7.1+

Usage

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

Requirements

  • Xcode 7.1+
  • iOS 8.0+
  • Swift 2.1
  • CcoaPods 0.36.0+ (if use CcoaPods)

CocoaPods

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

pod "ARNSpaceStretchFlowLayout"

Usage

Create Constant File ARNSpaceStretchFlowLayout.h and add this Code:

#import <Foundation/Foundation.h>

FOUNDATION_EXPORT double ARNSpaceStretchFlowLayoutVersionNumber;
FOUNDATION_EXPORT const unsigned char ARNSpaceStretchFlowLayoutVersionString[];

Create UICollectionViewFlowLayout Swift File

import UIKit

public class ARNSpaceStretchFlowLayout : UICollectionViewFlowLayout {
    
    public var scrollResistanceDenominator : CGFloat = 0.0
    
    public var overflowPadding : UIEdgeInsets {
        get {
            return self.contentOverflowPadding
        }
        set (overflowPadding) {
            self.contentOverflowPadding = overflowPadding
            self.bufferedContentInsets = overflowPadding
            self.bufferedContentInsets.top *= -1
            self.bufferedContentInsets.bottom *= -1
        }
    }
    
    public override func collectionViewContentSize() -> CGSize {
        var contentSize = super.collectionViewContentSize()
        contentSize.height += self.contentOverflowPadding.top + self.contentOverflowPadding.bottom
        
        return contentSize
    }
    
    private var contentOverflowPadding : UIEdgeInsets = UIEdgeInsetsZero
    private var bufferedContentInsets : UIEdgeInsets = UIEdgeInsetsZero
    private var transformsNeedReset : Bool = false
    
    public override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let newRect = UIEdgeInsetsInsetRect(rect, self.bufferedContentInsets)
        let att = super.layoutAttributesForElementsInRect(newRect)
        
        var attributes:[UICollectionViewLayoutAttributes] = []
        att?.forEach() { attributes.append($0.copy() as! UICollectionViewLayoutAttributes) }
        
        attributes.forEach(){ [unowned self] in
            var center = $0.center
            center.y += self.contentOverflowPadding.top
            $0.center = center
        }
        
        let collectionViewHeight = super.collectionViewContentSize().height
        let topOffset = self.contentOverflowPadding.top
        let bottomOffset = collectionViewHeight - self.collectionView!.frame.size.height + self.contentOverflowPadding.top
        let yPosition = self.collectionView!.contentOffset.y
        
        if yPosition < topOffset {
            let stretchDelta = topOffset - yPosition
            
            attributes.forEach(){ [unowned self] in
                let distanceFromTop = $0.center.y - self.contentOverflowPadding.top
                let scrollResistance = distanceFromTop / self.scrollResistanceDenominator
                $0.transform = CGAffineTransformMakeTranslation(0, -stretchDelta + (stretchDelta * scrollResistance))
            }
            
            self.transformsNeedReset = true
        } else if yPosition > bottomOffset {
            let stretchDelta = yPosition - bottomOffset
            
            attributes.forEach(){ [unowned self] in
                let distanceFromBottom = collectionViewHeight + self.contentOverflowPadding.top - $0.center.y
                let scrollResistance = distanceFromBottom / self.scrollResistanceDenominator
                $0.transform = CGAffineTransformMakeTranslation(0, stretchDelta + (-stretchDelta * scrollResistance))
            }
            
            self.transformsNeedReset = true
        } else if self.transformsNeedReset == true {
            self.transformsNeedReset = false
            
            attributes.forEach(){
                $0.transform = CGAffineTransformIdentity
            }
        }
        
        return attributes
    }
    
    public override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
        return true
    }
}

Add Collection View in StoryBoard and set Constant in ViewController.swift after add CollectionView Code In ViewController.swift

import UIKit

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
    
    @IBOutlet weak var collectionView : UICollectionView!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let flowLayout:ARNSpaceStretchFlowLayout = ARNSpaceStretchFlowLayout()
        flowLayout.sectionInset = UIEdgeInsetsMake(5, 0.0, 5.0, 0.0)
        flowLayout.itemSize = CGSizeMake(self.view.frame.size.width, 60)
        flowLayout.headerReferenceSize = CGSizeMake(self.view.frame.size.width, 64)
        flowLayout.footerReferenceSize = CGSizeMake(self.view.frame.size.width, 64)
        flowLayout.minimumLineSpacing = 5
        flowLayout.minimumInteritemSpacing = 5
        flowLayout.scrollResistanceDenominator = 1000.0
        self.collectionView.collectionViewLayout = flowLayout
    }
    
}

extension ViewController {
    
    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 15
    }
    
    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) 
        
        return cell
    }
    
    func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
        if kind == UICollectionElementKindSectionHeader {
            let headerView:UICollectionReusableView = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "Header", forIndexPath: indexPath) 
            
            return headerView
        } else {
            let footerView:UICollectionReusableView = collectionView.dequeueReusableSupplementaryViewOfKind(kind, withReuseIdentifier: "Footer", forIndexPath: indexPath) 
            
            return footerView
        }
    }
}

It was inspired by the following products.(Respect!)

Download

ARNSpaceStretchFlowLayout

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *