The article is really two tutorials in one: a classic paged-scroll UIKit setup, and a tour of the new UIPageControl APIs that arrived in iOS 14.
The first half shows the manual way to place multiple views side by side inside a horizontal
UIScrollView, then keep a UIPageControl in sync with the current page.
The second half focuses on what changed in iOS 14: you can now change the indicator art,
choose visible background treatments, and let the user scrub across the control itself.
The sample starts with three stored properties: the page views, the scroll view, and the page control.
That is the right mental model for this kind of UI. One array owns the individual page content, one scroll view provides the paging behavior, and one page control acts as the visible page index and as an alternate input method.
private var views = [UIView]()
private var scrollView: UIScrollView!
private var pageControl: UIPageControl!
This article also links a finished sample project if you want the complete version directly: PageViewExample on GitHub.
The scroll view setup is minimal: create it to match the screen, turn paging on, assign the delegate, and add it to the view hierarchy.
scrollView = UIScrollView(frame: self.view.frame)
scrollView.isPagingEnabled = true
scrollView.backgroundColor = .clear
scrollView.delegate = self
self.view.addSubview(scrollView)
The page content itself is then laid out manually in a loop. Each child view gets a frame whose
x position is offset by one screen width, which turns the array into a horizontal strip of pages.
The article uses a simple fixed example for the page frames, which keeps the geometry easy to follow even if the layout is not meant to be production-polished.
The page control is configured from the same source of truth as the scroll view: the number of page views already prepared in the array.
pageControl = UIPageControl(
frame: CGRect(origin: .zero, size: CGSize(width: view.bounds.width, height: 50))
)
pageControl.numberOfPages = views.count
pageControl.currentPage = 0
pageControl.addTarget(self, action: #selector(self.pageUpdated), for: .valueChanged)
pageControl.pageIndicatorTintColor = .gray
pageControl.currentPageIndicatorTintColor = .white
view.addSubview(pageControl)
view.bringSubviewToFront(pageControl)
This is the other half of the pattern: once the control knows how many pages exist, it can both reflect the current page and send events when the user taps a dot.
The remaining wiring is about geometry and synchronization.
In the layout step, the sample recalculates frames and sets the scroll view content width so the
pages can actually scroll. The article's viewWillLayoutSubviews section shows this
explicitly with a computed total width and a matching contentSize.
From there, the usual two-way sync applies:
The scroll view delegate updates pageControl.currentPage as the user swipes, and the
page control target method scrolls to the selected page when the user interacts with the control itself.
After the base paging setup works, the article switches to the iOS 14 UIPageControl APIs that make the control feel much less generic.
The first addition is a global preferred indicator image:
pageControl.preferredIndicatorImage = UIImage(systemName: "star.fill")
If you want each page to use its own symbol, the article then switches to per-page images:
pageControl.setIndicatorImage(UIImage(systemName: "sun.max.fill"), forPage: 0)
pageControl.setIndicatorImage(UIImage(systemName: "cloud.sun.fill"), forPage: 1)
pageControl.setIndicatorImage(UIImage(systemName: "cloud.drizzle.fill"), forPage: 2)
The next addition is background visibility. iOS 14 introduced styles that make the control easier to read over busy content:
pageControl.backgroundStyle = .minimal
pageControl.backgroundStyle = .prominent
The last upgrade is interaction itself. By default, dragging across the page control does not scrub between pages. The iOS 14 property below changes that behavior:
pageControl.allowsContinuousInteraction = true
The underlying paging architecture is old UIKit, but iOS 14 made the finishing details of UIPageControl much better.
That is the real value of this article. It does not just show how to wire a page control to a scroll view. It also shows how to stop the control from looking like the default dots every app used for years. With custom symbols, background styles, and scrub interaction, the same old paging pattern feels much more intentional.