This tutorial main focus will be the UIPageControlTimerProgress class.

UIPageControlTimerProgress has been introduced in iOS 17, and it is also available in tvOS 17.

This class allows us to create timed paging really easily and with a relatively small amount of code.


The new UIPageControlTimerProgress will display a UIPageControl as usual, but the current page indicator has a pill shape with a progress bar inside.

Once the progress bar is filled, the UIPageViewController will transition to the next page.

It is the same kind of behavior that we can see in apps like Instagram, Snapchat, and WhatsApp stories.

We have seen this behavior for a long time, but it is nice that Apple decided to introduce it eventually.

SwiftUI interoperability

Currently, it is available only for UIKit, but you can wrap the UIPageViewController in a UIViewControllerRepresentable struct and use it in your SwiftUI project.

Hopefully, Apple will eventually add it to SwiftUI as a style modifier for the TabView.

Now, let’s see how to use it.

Page control timer implementation

UIPageControlTimerProgress is not aUIPageControl in itself, but it has to be assigned to the progress property of the UIPageControl component.

To do so, let’s start by creating the ViewController that will host the page controller.

class TimerPageViewController: UIPageViewController {

/// This property will hold the pages to show.

let pages: [PageVC]


The PageVC code and the UIPageViewController implementation is here. I didn’t include it in the tutorial since this isn’t the main focus.

Then we create the UIPageControlTimerProgress component and assign it to the UIPageControl.

So, inside the viewDidLoad method add:

let timerProgress = UIPageControlTimerProgress(preferredDuration: 2)

timerProgress.delegate = self // we will configure this later

timerProgress.resetsToInitialPageAfterEnd = true

let pageControl = UIPageControl()

pageControl.progress = timerProgress

pageControl.numberOfPages = pages.count

// Add the page control to the view controller.



pageControl.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),

pageControl.centerXAnchor.constraint(equalTo: view.centerXAnchor),



Then conform the TimerPageViewController to UIPageControlTimerProgressDelegate and implement the pageControlTimerProgress(_:shouldAdvanceToPage:) method.

extension TimerPageViewController: UIPageControlTimerProgressDelegate {

func pageControlTimerProgress(_ progress: UIPageControlTimerProgress, shouldAdvanceToPage page: Int) -> Bool {

let nextPage = pages[page]

self.setViewControllers([nextPage], direction: .forward, animated: true)

return true



This method is called every time the current progress of the UIPageControlTimerProgress reaches the end and asks you whether it should advance to the next page.

Here we get the next view controller and set it to the PageViewController in order to show it.

Finally, we return true to tell the control that it can advance to the next page.

Start the timer process page controller

The UIPageControlTimerProgress won’t start automatically, but we need to do so by calling resumeTimer().

I have found that the most useful place to start the timer is inside viewDidAppear, since inside viewDidLoad it won’t have any effect. Maybe this is a bug that will be fixed in the public release.

override func viewDidAppear(_ animated: Bool) {




Handle user interaction

As it is now, if the user swipes to the next page, the progress control won’t go to the next indicator until it has finished with the current one.

To fix this behavior, we need to implement a method from the UIPageViewControllerDelegate protocol, called pageViewController(_:willTransitionTo:).

This method is called as soon as the user starts scrolling.

class TimerPageViewController {

// 1.

var suspensionTimer: Timer?


extension TimerPageViewController: UIPageViewControllerDelegate {

func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {

// 2.



// 3.

suspensionTimer = Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { _ in





Here we:

  1. Add a variable that will hold the timer that handles user interactions
  2. Pause the page control timer progress as soon as the user starts swiping
  3. Start a timer for 2 seconds that will resume the progress page controller in case the user stops swiping through pages

This way, if the user swipes a page, the page control timer will pause and resume after 2 seconds.

And we are done with the new UIPageControlTimerProgress component.


To preview what we have done, you can use the new #Preview macro available in Swift 5.9.

#Preview {

TimerPageViewController(pages: [PageVC(index: 0, image: "bergamo"),

PageVC(index: 1, image: "venice"),

PageVC(index: 2, image: "florence")])


Here is the result


Complete code

Here you can find the complete code.


This implementation is not written anywhere else since it is a new component and Apple didn’t provide any sample code, as far as I know.

So, if you have any suggestions on how to improve it, let me know here, I will really appreciate it.

UIPageControlTimerProgress is a really cool feature that Apple introduced in iOS 17.

It makes it clearer to the user that the slideshow is animated and how long each page will last.

Source link