Reputation: 261
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:
Upvotes: 0
Views: 1026
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