Skip to content

Instantly share code, notes, and snippets.

@vinhnx
Last active February 24, 2022 18:58
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save vinhnx/bb1354b247ebfe3790563173ac72baa9 to your computer and use it in GitHub Desktop.
Save vinhnx/bb1354b247ebfe3790563173ac72baa9 to your computer and use it in GitHub Desktop.
Collection view cell fade as scrolling effect

// reference: https://stackoverflow.com/a/42705208/1477298

You can do a lot of fun stuff to collection views. I like to subclass UICollectionViewFlowLayout. Here is an example that fades the top and the bottom of the collection view based on distance from center. I could modify it to fade only the very edges but you should figure it after you look through the code.

import UIKit

class FadingLayout: UICollectionViewFlowLayout,UICollectionViewDelegateFlowLayout {

    //should be 0<fade<1
    private let fadeFactor: CGFloat = 0.5
    private let cellHeight : CGFloat = 60.0

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    init(scrollDirection:UICollectionViewScrollDirection) {
        super.init()
        self.scrollDirection = scrollDirection

    }

    override func prepare() {
        setupLayout()
        super.prepare()
    }

    func setupLayout() {
        self.itemSize = CGSize(width: self.collectionView!.bounds.size.width,height:cellHeight)
        self.minimumLineSpacing = 0
    }

    override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
        return true
    }

    func scrollDirectionOver() -> UICollectionViewScrollDirection {
        return UICollectionViewScrollDirection.vertical
    }
    //this will fade both top and bottom but can be adjusted
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let attributesSuper: [UICollectionViewLayoutAttributes] = super.layoutAttributesForElements(in: rect) as [UICollectionViewLayoutAttributes]!
        if let attributes = NSArray(array: attributesSuper, copyItems: true) as? [UICollectionViewLayoutAttributes]{
            var visibleRect = CGRect()
            visibleRect.origin = collectionView!.contentOffset
            visibleRect.size = collectionView!.bounds.size
            for attrs in attributes {
                if attrs.frame.intersects(rect) {
                    let distance = visibleRect.midY - attrs.center.y
                    let normalizedDistance = abs(distance) / (visibleRect.height * fadeFactor)
                    let fade = 1 - normalizedDistance
                    attrs.alpha = fade
                }
            }
            return attributes
        }else{
            return nil
        }
    }
    //appear and disappear at 0
    override func initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        let attributes = super.layoutAttributesForItem(at: itemIndexPath)! as UICollectionViewLayoutAttributes
        attributes.alpha = 0
        return attributes
    }

    override func finalLayoutAttributesForDisappearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        let attributes = super.layoutAttributesForItem(at: itemIndexPath)! as UICollectionViewLayoutAttributes
        attributes.alpha = 0
        return attributes
    }
}

And in your setup in your controller with the collection view it would look like this.

let layout = FadingLayout(scrollDirection: .vertical)
collectionView.delegate = self
collectionView.dataSource = self
self.collectionView.setCollectionViewLayout(layout, animated: false)

I can tell you how to modify it if I knew the use case a bit better.

@Heki2017
Copy link

Like your work. Just don't understand how to create a single side fade (f.i. top) or reducing the size to just top 5%. Is there away you can help me ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment