How to add PageControl inside UICollectionView Image Scrolling

64,900

Solution 1

Here you have a complete class with Page Control pagination in a horizontal collection view. This is working on one of my applications, right now, and is working correctly. If you cannot get it work, do feel free to ask me and I'll help you.

First, in the Storyboard you have to set up your CollectionView with:

Layout: Flow
Scroll Direction: Horizontal
Scrolling enabled
Paging enabled

@IBOutlet weak var collectionView: UICollectionView! //img: 77

@IBOutlet weak var pageControl: UIPageControl!

var thisWidth:CGFloat = 0

override func awakeFromNib() {
    super.awakeFromNib()

    thisWidth = CGFloat(self.frame.width)
    collectionView.delegate = self
    collectionView.dataSource = self

    pageControl.hidesForSinglePage = true

}

func numberOfSections(in collectionView: UICollectionView) -> Int {
    return 10
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return 1
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "YourCell", for: indexPath)

    return cell
}

func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    self.pageControl.currentPage = indexPath.section
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    thisWidth = CGFloat(self.frame.width)
    return CGSize(width: thisWidth, height: self.frame.height)
}

Solution 2

I'm not a fan of the accepted answer. From time to time, willDisplay cell will return the wrong page for the page control depending on users interaction.

What I use:

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    pageControl.currentPage = Int(scrollView.contentOffset.x) / Int(scrollView.frame.width)
}

This will update the page after the user has finished scrolling thus making the currentPage of the pageControl more accurate.

Solution 3

For a more responsive UIPageControl, I prefer to use scrollViewDidScroll and calculating the offset with regards to the horizontal center of the scrollView.

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let offSet = scrollView.contentOffset.x
    let width = scrollView.frame.width
    let horizontalCenter = width / 2

    pageControl.currentPage = Int(offSet + horizontalCenter) / Int(width)
}

Solution 4

Swift 5 Very Smooth working

//MARK:- For Display the page number in page controll of collection view Cell
func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let visibleRect = CGRect(origin: self.collectionview.contentOffset, size: self.collectionview.bounds.size)
    let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
    if let visibleIndexPath = self.collectionview.indexPathForItem(at: visiblePoint) {
        self.pageControl.currentPage = visibleIndexPath.row
    }
}

Solution 5

Waiting for scrollViewDidEndDecelerating results in a slow update to the UIPageControl. If you want a more responsive UI, I suggest instead implementing scrollViewWillEndDragging(_:withVelocity:targetContentOffset:).

Swift 4 example where each collection view section is a page:

override func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    if let collectionView = scrollView as? ScoreCollectionView,
        let section = collectionView.indexPathForItem(at: targetContentOffset.pointee)?.section {
        self.pageControl.currentPage = section
    }
}
Share:
64,900

Related videos on Youtube

SwiftDeveloper
Author by

SwiftDeveloper

I coding iOS &amp; Swift applications.

Updated on October 09, 2021

Comments

  • SwiftDeveloper
    SwiftDeveloper over 2 years

    I have UICollectionView Horizontal Image listing codes. I want to add PageControl when scrolling images will shows, I added pagecontrol selector and IBOutlet but how can I integrate it between UICollecitonView?

    My codes are below:

     class  ViewController: UIViewController,  UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    
    
        @IBOutlet weak var View : DesignableView!
    
    
        @IBOutlet var collectionView: UICollectionView!
        @IBOutlet var collectionViewLayout: UICollectionViewFlowLayout!
    
        @IBOutlet open weak var pageControl: UIPageControl? {
            didSet {
                pageControl?.addTarget(self, action: #selector(ViewController.pageChanged(_:)), for: .valueChanged)
            }
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
             pageControl?.numberOfPages = 11
    
    
        }
    
    
        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return 11;
        }
    
    
    
    
        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell: ImageCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImageCollectionViewCell", for: indexPath) as! ImageCollectionViewCell
            cell.label.text = "Cell \(indexPath.row)"
            cell.backgroundImageView.image = UIImage(named: "Parallax \(indexPath.row + 1)")
    
            // Parallax cell setup
            cell.parallaxTheImageViewScrollOffset(self.collectionView.contentOffset, scrollDirection: self.collectionViewLayout.scrollDirection)
            return cell
        }
    
    
    
        @IBAction open func pageChanged(_ sender: UIPageControl) {
    
            print(sender.currentPage)
    
    
        }
    
    
    
    
        // MARK: Delegate
    
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
            return collectionView.bounds.size;
        }
    
        // MARK: Scrolling
    
        func scrollViewDidScroll(_ scrollView: UIScrollView) {
            // Parallax visible cells
            for cell: ImageCollectionViewCell in collectionView.visibleCells as! [ImageCollectionViewCell] {
                cell.parallaxTheImageViewScrollOffset(self.collectionView.contentOffset, scrollDirection: self.collectionViewLayout.scrollDirection)
            }
        }
    
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    
    
    
    }
    
  • SwiftDeveloper
    SwiftDeveloper over 7 years
    dude dont work with pagecontrol when i added func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return self.myarray.count // HERE dont work page control }
  • J Curti
    J Curti over 7 years
    That's because in order to make a page for each item, you have to use sections, not rows
  • gasparuff
    gasparuff almost 7 years
    But with this solution, when the user swipes to the next page and then swipes back again without lifting his finger, the pageControl shows the wrong page. I think you should consider @luke's answer
  • Nico
    Nico over 6 years
    That's something you should clearly state in your answer
  • Axy
    Axy over 6 years
    This should be the only accepted answer since it's the most clean and responsive way to do it.
  • Deamon
    Deamon over 6 years
    I have to substitute section for item
  • Deamon
    Deamon over 6 years
    If you swipe fast enough, the current page can produce the wrong result.
  • Hardik Darji
    Hardik Darji about 6 years
    Working as I want. Thx
  • Sarthak Mishra
    Sarthak Mishra almost 6 years
    Best solution! Responsive and accurate
  • Codenator81
    Codenator81 over 5 years
    Should be best answer
  • Ben Shabat
    Ben Shabat over 5 years
    how to make it work with a right to left pageControl?
  • sangavi
    sangavi over 5 years
    Awesome search for full day finally got great answer
  • Rick van der Linde
    Rick van der Linde about 5 years
    fyi row is for table views, item is for collection views.
  • Graham Perks
    Graham Perks almost 5 years
    Noticeably better than using the laggy scrollViewDidEndDecelerating solution.
  • user3788747
    user3788747 over 4 years
    Luke's solution somehow does not work any more for me. But David's answer works and it saved my life, ty.
  • Antoine El Murr
    Antoine El Murr over 4 years
    This should be the accepted answer, seriously I have seen the answer above and it took me like 3 hours to realize that the correct answer is underneath the accepted one
  • Jason Aller
    Jason Aller about 4 years
    When answering a three year old question with eight other answers you need to explain what is new and different about your answer. Proper formatting for a block of code is not the same and formatting each line as a section of inline code. Code only answers need explanation of how and why they work.
  • Khushtar parvez
    Khushtar parvez about 4 years
    This is another method if you can Compare to Another
  • Prashanth Thota
    Prashanth Thota almost 4 years
    @RickvanderLinde even though it is mentioned as row instead of item it works fine.
  • Lal Krishna
    Lal Krishna over 3 years
    This works smoothly if we disabled UIPageControl's userInteraction. For those who needs to support pagecontrol's action, put this calculation in scrollViewDidEndDecelerating
  • user2990765
    user2990765 over 3 years
    The problem I am facing is that the collection view always starts with the first page. How do I initialise the collection view to start somewhere from the middle page?