I'm working on a collection view that should look like this:
The scroll view works but I want to make my collection view peak the next item like in the screenshot, and when scrolling it scales the normal size of course.
I couldn't manage to do that with only enabling paging (of course).
Thank you all for any help!
Edit: This kinda helps what I want to achieve, but it still lacks scaling and does not work near good while scrolling.
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return -100
}
If you want to tweak the behaviour of item sizing inside UICollectionView
you should subclass UICollectionViewLayout
or UICollectionViewFlowLayout
class and add the sizing behaviour inside collectionView(_:layout:sizeForItemAt:)
.
The following examples expects that UICollectionView
aspect ratio is 1:0.7 and UICollectionViewCell
ratio is 3:4. Layout is subclassed from UICollectionViewFlowLayout
class. Below code then down scales the item based on the distance from the centre.
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard let collectionView = self.collectionView else { return nil }
// Get the array of layout attributes from the superclass, which will be the starting point for modification.
let attributesArray = super.layoutAttributesForElements(in: rect)?.map { $0.copy() as! UICollectionViewLayoutAttributes }
// Calculate the center of the visible rect to use as a reference point for scaling.
let center = collectionView.contentOffset.x + (collectionView.bounds.width / 2.0)
// Iterate over the layout attributes array and adjust the attributes of cells within the visible rect.
attributesArray?.forEach { attributes in
// Ensure we're only adjusting attributes for cells that are visible.
if attributes.frame.intersects(rect) {
// Determine how far the cell's center is from the center of the visible rect.
let distanceFromCenter = abs(center - attributes.center.x)
// Define the point at which cells no longer scale.
let maxDistance = (collectionView.bounds.width / 2) + (attributes.size.width / 2)
// Normalize the distance to a factor between 0 and 1.
let normalizedDistance = min(distanceFromCenter / maxDistance, 1)
// Define the minimum scale factor (e.g., no cell will be smaller than 70% of its original size).
let minimumScaleFactor: CGFloat = 0.7
// Calculate the scale factor based on the cell's distance from the center.
let scaleFactor = 1 - (1 - minimumScaleFactor) * normalizedDistance
// Apply a 3D scale transform to the cell's attributes.
attributes.transform3D = CATransform3DMakeScale(scaleFactor, scaleFactor, 1)
// Ensure that cells closer to the center appear above others.
attributes.zIndex = Int(scaleFactor * 10)
}
}
return attributesArray
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
// Invalidate the layout on bounds changes to recalculate scaling for new cell positions.
return true
}
This will make effect as shown on the following image, but without paging. For getting paging to work you should also override targetContentOffset(forProposedContentOffset:withScrollingVelocity:)
method on the UICollectionViewFlowLayout
subclass and apply appropriate logic to it.