MoeinDeveloper
MoeinDeveloper

Reputation: 261

Scale transfrom on UICollectionViewCell

I have a vertical scrolling Collection view and I want to make the centered cell bigger when scrolling happens.

I'm using InfiniteLayout for infinite scrolling on my collection view.

here is my custom flow layout:

import Foundation
import InfiniteLayout
class CustomLayout: InfiniteLayout {

    public override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let attributes = super.layoutAttributesForElements(in: rect).flatMap {
            self.copyLayoutAttributes(from: $0)
        }
        guard let visibleRect = self.visibleCollectionViewRect() else {
            return attributes
        }
        let centeredOffset = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
        for attributes in attributes ?? [] {
            let diff = self.scrollDirection == .horizontal ? centeredOffset.x - attributes.center.x : centeredOffset.y - attributes.center.y
            let distance = abs(diff)
            let tolerance : CGFloat = 0.02
            var scale = 1.00 + tolerance - (( distance / centeredOffset.y ) * 0.105)
            if(scale > 1.0){
                scale = 1.0
            }
            if(scale < 0.460091){
                scale = 0.460091
            }
            attributes.transform = attributes.transform.scaledBy(x: scale, y: scale)
            attributes.alpha = changeSizeScaleToAlphaScale(scale)
        }
        return attributes
    }

    func changeSizeScaleToAlphaScale(_ x : CGFloat) -> CGFloat {
        let minScale : CGFloat = 0.46
        let maxScale : CGFloat = 1.0

        let minAlpha : CGFloat = 0.25
        let maxAlpha : CGFloat = 1.0

        return ((maxAlpha - minAlpha) * (x - minScale)) / (maxScale - minScale) + minAlpha
    }
}

but it doesn't work...

here is my delegate and data source:

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return fakeData.count
}

func numberOfSections(in collectionView: UICollectionView) -> Int {
    return 1
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = infinteCollectionView.dequeueReusableCell(withReuseIdentifier: "myCell", for: indexPath) as! MyCollectionViewCell
    cell.setText(text: fakeData[infinteCollectionView.indexPath(from: indexPath).row])
    return cell
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return CGSize(width: 200, height: 55)
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
    return collectionView.frame.height
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    return 10
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
    return UIEdgeInsets(top: 0, left: 0, bottom: 10, right: 0)
}


func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    self.infinteCollectionView.scrollToNearestVisibleCollectionViewCell()
}

func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    if !decelerate {
        self.infinteCollectionView.scrollToNearestVisibleCollectionViewCell()
    }
}

here is my extension for scroll to visible cell:

extension InfiniteCollectionView {

    func scrollToNearestVisibleCollectionViewCell() {
        self.decelerationRate = UIScrollView.DecelerationRate.normal
        let visibleCenterPositionOfScrollView = Float(self.contentOffset.y + (self.bounds.size.height / 2))
        var closestCellIndex = -1
        var closestDistance: Float = .greatestFiniteMagnitude
        for i in 0..<self.visibleCells.count {
            let cell = self.visibleCells[i]
            let cellHeight = cell.bounds.size.width
            let cellCenter = Float(cell.frame.origin.y + cellHeight / 2)

            // Now calculate closest cell
            let distance: Float = fabsf(visibleCenterPositionOfScrollView - cellCenter)
            if distance < closestDistance {
                closestDistance = distance
                closestCellIndex = self.indexPath(for: cell)!.row
            }
        }
        if closestCellIndex != -1 {
            self.scrollToItem(at: IndexPath(row: closestCellIndex, section: 0), at: .centeredVertically, animated: true)
        }
    }
}

Basicly I want to approach this:

collection view

Upvotes: 0

Views: 1026

Answers (1)

AbdoHema2016
AbdoHema2016

Reputation: 21

Here is how I did it not sure if it's the best way to do it

Make a var to track the index in the middle of collection view

    var index = IndexPath()

in the viewDidLayoutSubviews check for the cell in the middle once it's calculated invalidate the already configured layout

 override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        let center = self.view.convert(collectionView.center, to: collectionView)
        index = collectionView.indexPathForItem(at: center) ?? IndexPath(item: 0, section: 0)
        collectionView.collectionViewLayout.invalidateLayout()
    }

then in sizeforitem

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        if index.count != 0  {
            if index.item == indexPath.item {
                return CGSize(width: collectionView.frame.size.width, height: 100)
            }

        }
        return CGSize(width: collectionView.frame.size.width, height: 50)
    }

then in scrollviewdidscroll call viewDidLayoutSubviews again so the centered element gets recalculated and item size gets changed

func scrollViewDidScroll(_ scrollView: UIScrollView) {

        viewDidLayoutSubviews()
    }

Upvotes: 1

Related Questions