2
votes

I have a UICollectionView that has the following settings,

public CollectionView(CollectionLayout layout) : base(CGRect.Empty, layout)
{
    RegisterClassForCell(typeof(CollectionCell), CollectionCell.CellIdentifier);
    CollectionViewLayout = layout;
    ShowsHorizontalScrollIndicator = false;
    PagingEnabled = true;
}

The CollectionViewLayout is,

public CollectionLayout()
{
    ScrollDirection = UICollectionViewScrollDirection.Horizontal;
    MinimumInteritemSpacing = 0f;
    MinimumLineSpacing = 0f;
    ItemSize = new CGSize(UIScreen.MainScreen.Bounds.Width, 200f);
}

so the cells in the CollectionView are stretched so that a cell fills the CollectionView. The CollectionView is only horizontally scrollable.

Now I want to have a dotted page indicator instead of the scroll bar for the CollectionView. Is there anyway I can achieve this properly?

4
You can use UIPageControl for the same developer.apple.com/documentation/uikit/uipagecontrol - Vinaykrishnan

4 Answers

10
votes

Perfectly correct approach for 2020:

Simply add a UIPageControl in storyboard.

Put it below (i.e., visible on top of) your collection view.

enter image description here

Link to it...

class YourVC: UIViewController, UICollectionViewDelegate,
         UICollectionViewDataSource,
         UICollectionViewDelegateFlowLayout {
    
    @IBOutlet var collectionView: UICollectionView!
    @IBOutlet var dots: UIPageControl!

Simply add a constraint centering it horizontally to the collection view, and add a constraint to align the bottoms.

That will give the standard positioning / spacing.

(Of course, you can place the dots anywhere you want, but that is the standard.)

enter image description here

Tip 1 - colors

Bizarrely the default colors for the dots are .. clear!

So set them to gray/black or whatever you wish:

enter image description here

Or you can do that in code:

override func viewDidLoad() {
    super.viewDidLoad()
    dots.pageIndicatorTintColor = .systemGray5
    dots.currentPageIndicatorTintColor = .yourCorporateColor
}

Next. In numberOfItemsInSection, add ...

func collectionView(_ collectionView: UICollectionView,
              numberOfItemsInSection section: Int) -> Int {
    
    let k = ... yourData.count, or whatever your count is
    
    dots.numberOfPages = k
    return k
}

Tip 2 - in fact, do NOT use the deceleration calls

Add this code:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    dots.currentPage = Int(
        (collectionView.contentOffset.x / collectionView.frame.width)
            .rounded(.toNearestOrAwayFromZero)
        )
    )
}

You simply set the page in "scrollViewDidScroll".

In fact

do not use scrollViewWillBeginDecelerating

do not use scrollViewDidEndDecelerating.

To see why: try it using either of those calls. Now skim quickly through many pages. Notice it does not work properly.

Simply use scrollViewDidScroll for the correct, perfect result, including initialization.

Tip 3 - do NOT use Int division - it completely changes the behavior and is totally wrong.

You will often see this example code:

// wrong, do not do this
dots.currentPage = Int(collectionView.contentOffset.x) /
                     Int(collectionView.frame.width)
// wrong, do not do this

That often-seen example code is completely wrong.

If you try that, it will result in the dots "jumping" in a non-standard way, as you skim through pages.

Best explanation is to try it and see.

For the usual, correct, Apple-style behavior as you scroll through or skim through the pages, the code is:

    dots.currentPage = Int(
        (collectionView.contentOffset.x / collectionView.frame.width)
        .rounded(.toNearestOrAwayFromZero)
    )

Final example...

enter image description here

1
votes

Drag a UIPageControl above your collectionView and make an IBOutlet of UIPageControl in your ViewController.

Then Put the following code:

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

Use UIPageControl. Link Below: UIPageControl

Then detect which cell you are in and write some code to change the "currentPage" of UIPageControl.

-1
votes

I added a UIPageControl component on top of the CollectionView, setting its constraints to the bottom of the CollectionView. Then I got an instance of this UIPageControl component to a property of the CollectionSource of the UICollectionView.

I set the Pages and CurrentPage properties of the UIPageControl as follows,

public class CollectionSource : UICollectionViewSource
{
    private List<SourceDataModel> _dataSource;
    public UIPageControl PageControl { get; set; }

    // other code

    public override void DecelerationEnded(UIScrollView scrollView)
    {
        var index = (int)(Collection.ContentOffset.X / Collection.Frame.Width);
        PageControl.CurrentPage = index;
        // other code
    }

    public override nint GetItemsCount(UICollectionView collectionView, nint section)
    {
        PageControl.Pages = _dataSource.Count;
        return _dataSource.Count;
    }
}

Tutorial Followed: https://blog.learningtree.com/paging-with-collection-views-part-2/