I have a 5 images in my horizontal collectionView and it appears like carousel, one cell overlaps the other ones. Also, I have a UIPageControl and next/previous buttons for changing carousel items.
class ViewController:
UIViewController,
UICollectionViewDelegate,
UICollectionViewDataSource {
@IBOutlet private var collectionView: UICollectionView!
@IBOutlet private var nextButton: UIButton!
@IBOutlet private var previousButton: UIButton!
@IBOutlet private var pageControl: UIPageControl!
let images: [UIImage] = [
image1,
image2,
image3,
image4,
image5,
]
var currentPage: Int = 0 {
didSet {
pageControl.currentPage = currentPage
}
}
In viewDidLoad method I'm setting the pageControl items like:
override func viewDidLoad() {
collectionView.delegate = self
collectionView.dataSource = self
previousButton.isHidden = true
nextButton.isEnabled = true
pageControl.numberOfPages = images.count
pageControl.currentPage = 0
pageControl.clipsToBounds = false
}
When it comes to showing carousel items in collectionView using next/prev buttons it works well. But the problem is currentPage state is not updating for the scrollViewDidEndDecelerating method and after some traverse using next/prev buttons it scrolls to first cell or the last cell of the collectionView automatically and the currentPage state is confusing.
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let pageNumber = ceil(scrollView.contentOffset.x / scrollView.frame.size.width)
pageControl.currentPage = Int(pageNumber)
currentPage = Int(pageNumber)
updateButtonStates(with: currentPage)
updateUI(with: currentPage)
}
@IBAction func didTapOnPreviousButton(_ sender: Any) {
if currentPage > 0 {
currentPage -= 1
print("IndexPath(item: currentPage, section: 0)", IndexPath(item: currentPage, section: 0))
collectionView?.isPagingEnabled = false
collectionView.scrollToItem(
at: IndexPath(item: currentPage,
section: 0),
at: .centeredHorizontally,
animated: true)
collectionView?.isPagingEnabled = true
pageControl.currentPage = currentPage
updateButtonStates(with: currentPage)
updateUI(with: currentPage)
}
}
@IBAction func didTapNextButton(_ sender: Any) {
if currentPage < images.count - 1 {
currentPage += 1
print("IndexPath(item: currentPage, section: 0)", IndexPath(item: currentPage, section: 0))
collectionView?.isPagingEnabled = false
collectionView.scrollToItem(
at: IndexPath(item: currentPage,
section: 0),
at: .centeredHorizontally,
animated: true)
collectionView?.isPagingEnabled = true
pageControl.currentPage = currentPage
updateButtonStates(with: currentPage)
updateUI(with: currentPage)
}
}
private func updateUI(with currentPage: Int) {
pageControl.currentPage = currentPage
previousButton.isHidden = currentPage == 0
nextButton.isHidden = currentPage == images.count - 1
}
func updateButtonStates(with currentPage: Int) {
pageControl.currentPage = currentPage
previousButton.isEnabled = currentPage > 0
nextButton.isEnabled = currentPage < images.count
}
What I want to do is when the user scrolls between the cells and then click on the next/prev buttons, pageControl state should be same for both 2 different actions.
This is how I solved the problem:
I used scrollViewWillEndDragging method to get exact point of the scrollView and the current itemWidth of the collectionView.
func scrollViewWillEndDragging(
_: UIScrollView,
withVelocity _: CGPoint,
targetContentOffset: UnsafeMutablePointer<CGPoint>
) {
let offset = targetContentOffset.pointee.x
let widthPerItem = collectionView.bounds.width / images.count
pageControl.currentPage = Int(floor(offset / widthPerItem))
updateButtonStates(with: pageControl.currentPage)
updateUI(with: pageControl.currentPage)
}
And also updated UI and next/prev button states with the IBAction updates:
@IBAction
func didTapOnPreviousButton(_: Any) {
let prevIndex = max(pageControl.currentPage - 1, 0)
let indexPath = IndexPath(item: prevIndex, section: 0)
pageControl.currentPage = prevIndex
collectionView?.isPagingEnabled = false
collectionView.scrollToItem(
at: indexPath,
at: .centeredHorizontally,
animated: true
)
updateButtonStates(with: pageControl.currentPage)
updateUI(with: pageControl.currentPage)
}
@IBAction
func didTapOnNextButton(_: Any) {
let nextIndex = min(pageControl.currentPage + 1, images.count - 1)
let indexPath = IndexPath(item: nextIndex, section: 0)
pageControl.currentPage = nextIndex
collectionView?.isPagingEnabled = false
collectionView.scrollToItem(
at: indexPath,
at: .centeredHorizontally,
animated: true
)
updateButtonStates(with: pageControl.currentPage)
updateUI(with: pageControl.currentPage)
}
Briefly, pageControl.currentPage
state should be updated in the corresponding methods like next/prev button actions and the scrollView.