I am currently working on a horizontal gallery scroll view. I have decided to use UICollectionView with horizontal paging enabled. It almost works great... expect on rotation the collection view cells are not centred (see screenshot attached), where red is the first cell and yellow is the second one.
It usually works fine on the first rotation but then just messes up :D
This is the code I have for setting up the class:
class GalleryScrollView: UIView {
private var collectionView: UICollectionView!
private var layout: UICollectionViewFlowLayout!
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
override func layoutSubviews() {
layout.invalidateLayout()
}
private func setupView() {
layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
collectionView = UICollectionView(frame: self.frame,
collectionViewLayout: layout)
collectionView.register(UINib(nibName: "PhotoGalleryCell", bundle: nil), forCellWithReuseIdentifier: "photoGalleryCell")
collectionView.isPagingEnabled = true
collectionView.contentInsetAdjustmentBehavior = .never
collectionView.dataSource = self
collectionView.delegate = self
collectionView.autoresizingMask = .flexibleWidth
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.backgroundColor = .green
self.addSubview(collectionView)
NSLayoutConstraint.activate([
collectionView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
collectionView.topAnchor.constraint(equalTo: self.topAnchor),
collectionView.bottomAnchor.constraint(equalTo: self.bottomAnchor)
])
collectionView.reloadData()
}
}
Followed by the collection view setup:
extension GalleryScrollView: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoGalleryCell", for: indexPath) as? PhotoGalleryCell else {
return UICollectionViewCell()
}
if indexPath.item == 0 {
cell.backgroundColor = .red
} else {
cell.backgroundColor = .yellow
}
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
2
}
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
return self.frame.size
}
I tried playing around with func collectionView(_ collectionView: UICollectionView, targetContentOffsetForProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint {
but for some reason the func was never called.
Did anyone else run into this problem? I saw a lot of topics in objective c threads, not much in swift. I am also somewhat struggling to understand why this is happening, so just understanding the problem would be very helpful. Thank you!
You don't seem to have handled rotations. You need to reloadData
when a rotation occurs. You'll get the event in UIViewController
from there you can call reloadData
on the instance of your UICollectionView
.
class GalleryViewController: UIViewController {
//...
var galleryScrollView: GalleryScrollView
//...
override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
super.willTransition(to: newCollection, with: coordinator)
galleryScrollView.collectionViewLayout.invalidateLayout()
}
//...
}
Then in GalleryScrollView
:
class GalleryScrollView: UIView {
var currentVisibleIndexPath = IndexPath(row: 0, section: 0)
//....
}
extension GalleryScrollView: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
//...
func collectionView(_ collectionView: UICollectionView, targetContentOffsetForProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint {
let attributes = collectionView.layoutAttributesForItem(at: currentVisibleIndexPath)
let newOriginForOldIndex = attributes?.frame.origin
return newOriginForOldIndex ?? proposedContentOffset
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let center = CGPoint(x: scrollView.contentOffset.x + (scrollView.frame.width / 2), y: (scrollView.frame.height / 2))
if let indexPath = self.screenView.indexPathForItem(at: center) {
currentVisibleIndexPath = indexPath
}
}
}