I want to implement paging in the UICollectionView to make it appear like the AppStore
The previous and next cells should be partially visible from leading and trailing edges. I want to give it a feel just like we get to see in the Apps section of AppStore.
Below is my configuration code for the collection view. I tried implementing insetForSectionAt function but that completely messed up the scrolling behaviour of the collection view. Any help will be appreciated. Thank you!
//MARK: Configure collectionView
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func setupCollectionView() {
imagesColletionView.delegate = self
imagesColletionView.dataSource = self
imagesColletionView.register(CustomCollectionViewCell.self, forCellWithReuseIdentifier: CustomCollectionViewCell.cellIdentifier)
view.addSubview(imagesColletionView)
// Set up horizontal scrolling and paging
if let layout = imagesColletionView.collectionViewLayout as? UICollectionViewFlowLayout {
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 0
imagesColletionView.isPagingEnabled = true
imagesColletionView.setCollectionViewLayout(layout, animated: false)
}
//Set constraints
imagesColletionView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
imagesColletionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
imagesColletionView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: 10),
imagesColletionView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: -10),
imagesColletionView.heightAnchor.constraint(equalToConstant: UIScreen.main.bounds.height/4)
])
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return colors.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CustomCollectionViewCell.cellIdentifier, for: indexPath) as! CustomCollectionViewCell
cell.backgroundColor = colors[indexPath.row]
return cell
}
// UIScrollViewDelegate method to detect scrolling
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
// Calculate the visible index based on content offset
let visibleRect = CGRect(origin: imagesColletionView.contentOffset, size: imagesColletionView.bounds.size)
let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
if let indexPath = imagesColletionView.indexPathForItem(at: visiblePoint) {
// Scroll to the corresponding row in the table view
let selectedRowIndexPath = IndexPath(row: indexPath.item, section: 0)
namesTableView.selectRow(at: selectedRowIndexPath, animated: true, scrollPosition: .top)
}
}
func scrollToCollectionViewItem(at indexPath: IndexPath) {
// Determine the corresponding index path in the collection view
let collectionViewIndexPath = IndexPath(item: indexPath.row, section: 0)
// Scroll to the item in the collection view
imagesColletionView.scrollToItem(at: collectionViewIndexPath, at: .centeredHorizontally, animated: true)
}
}
//enables horizontal scroll
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
let cellWidth = collectionView.bounds.width
let cellHeight = collectionView.bounds.height
return CGSize(width: cellWidth, height: cellHeight)
}
}
Here's the solution:
var imagesColletionView: UICollectionView!
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func setupCollectionView() {
imagesColletionView = UICollectionView(frame: view.bounds, collectionViewLayout: createCompositionalLayout())
imagesColletionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
imagesColletionView.backgroundColor = .systemBackground
imagesColletionView.delegate = self
imagesColletionView.dataSource = self
imagesColletionView.register(CustomCollectionViewCell.self, forCellWithReuseIdentifier: CustomCollectionViewCell.cellIdentifier)
view.addSubview(imagesColletionView)
imagesColletionView.isScrollEnabled = false //disable vertical scroll
//Set constraints
imagesColletionView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
imagesColletionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
imagesColletionView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
imagesColletionView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
imagesColletionView.heightAnchor.constraint(equalToConstant: UIScreen.main.bounds.height/3)
])
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CustomCollectionViewCell.cellIdentifier, for: indexPath) as! CustomCollectionViewCell
cell.cellImageView.image = UIImage(named: images[indexPath.row])
return cell
}
//used in taking data from tableview to go to respective cell
func scrollToCollectionViewItem(at indexPath: IndexPath) {
// Determine the corresponding index path in the collection view
let collectionViewIndexPath = IndexPath(item: indexPath.row, section: 0)
// Scroll to the item in the collection view
imagesColletionView.scrollToItem(at: collectionViewIndexPath, at: .centeredHorizontally, animated: true)
}
//use in custom paging
func createCompositionalLayout() -> UICollectionViewLayout {
let layout = UICollectionViewCompositionalLayout {
sectionIndex, layoutEnvironment in
let section = self.images[sectionIndex]
return self.createFeaturedSection()
}
return layout
}
func createFeaturedSection() -> NSCollectionLayoutSection {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1))
let layOutItem = NSCollectionLayoutItem(layoutSize: itemSize)
layOutItem.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 5, bottom: 0, trailing: 5)
//this fractional width value decides the leading & trailing partial showing of cells.
let layoutGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.9), heightDimension: .estimated(350))
let layOutGroup = NSCollectionLayoutGroup.horizontal(layoutSize: layoutGroupSize, subitems: [layOutItem])
let layOutSection = NSCollectionLayoutSection(group: layOutGroup)
layOutSection.orthogonalScrollingBehavior = .groupPagingCentered
return layOutSection
}
}
However, on implementing UICollectionViewCompositionalLayout, in horizontal direction UIScrollView delegate methods stopped working as setting orthogonalScrollingBehavior to a section embeds internal UICollectionViewOrthogonalScrollerEmbeddedScrollView this new embedded scroll view handles scrolling behaviour in the section So ultimately it sets another internal subview to the collection view (the scrolling behaviour handler) which is a completely different instance Hence, we are unable to get any call back for delegate methods of UICollectionViewDelegate/UIScrollView
Feel free to post another answer if you are aware of any solution that allows giving the paging behaviour to the collection view while at the same time allowing to implementation of UIScrollView delegate methods...