// // ViewController.swift // Comp Layout Bug // // Created by aayvon on 6/17/24. // import UIKit class ViewController: UIViewController { private var layout: UICollectionViewLayout { UICollectionViewCompositionalLayout { _, _ in self.makeCarouselLayout() } } private var collectionView: UICollectionView! override func viewDidLoad() { super.viewDidLoad() collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) collectionView.register(CustomCell.self, forCellWithReuseIdentifier: CustomCell.reuseID) collectionView.dataSource = self collectionView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(collectionView) NSLayoutConstraint.activate([ collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor), collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor), collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20), collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor) ]) // scroll to middle point for comp layout carousel // only works after calling `layoutIfNeeded` collectionView.layoutIfNeeded() collectionView.scrollToItem(at: IndexPath(row: 200, section: 0), at: .centeredHorizontally, animated: false) } func makeCarouselLayout() -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) let item = NSCollectionLayoutItem(layoutSize: itemSize) let group = NSCollectionLayoutGroup.horizontal(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.7), heightDimension: .absolute(200)), subitems: [item]) let section = NSCollectionLayoutSection(group: group) section.orthogonalScrollingBehavior = .groupPagingCentered section.interGroupSpacing = 0 section.visibleItemsInvalidationHandler = { items, offset, environment in // the width of the collection view let contentWidth = environment.container.contentSize.width // center of the currently visible part of the collectionview (take offset into account) let visibleCenterX = offset.x + contentWidth / 2 // max distance from the center, which is half the width of the collection view // increase to make it slower to grow/shrink, decrease to make it faster to grow/shrink let maxDistance = contentWidth / 2 // the min scale to apply to an item when not centered let minScale = 0.8 items.forEach { item in // distance of the item from the visible center, always positive let distance = abs(visibleCenterX - item.center.x) // scale based on the distance from the center let scale = max(1 - (distance / maxDistance) * (1 - minScale), minScale) // do not apply to header guard item.representedElementKind != UICollectionView.elementKindSectionHeader else { return } if contentWidth <= 430 { item.transform = CGAffineTransform(scaleX: scale, y: scale) item.alpha = scale } else { item.transform = .identity } } } return section } } extension ViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 400 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CustomCell.reuseID, for: indexPath) as? CustomCell else { return UICollectionViewCell() } cell.setIndexLabel(index: indexPath.row) return cell } }