Create PDF Teleprompter using Swift for iOS

Use the continuous scrolling page view with a timer function to automatically scroll through a document, using touch events to pause scrolling. Get additional resources by visiting our guide on configuring scroll directions and page transitions in our iOS viewer.


//
// Copyright © 2021-2025 PSPDFKit GmbH. All rights reserved.
//
// The Nutrient sample applications are licensed with a modified BSD license.
// Please see License for details. This notice may not be removed from this file.
//
import PSPDFKit
import PSPDFKitUI
class TeleprompterExample: Example {
override init() {
super.init()
title = "Teleprompter example"
category = .subclassing
priority = 30
}
override func invoke(with delegate: ExampleRunnerDelegate?) -> UIViewController {
let document = AssetLoader.document(for: .annualReport)
let pdfViewController = AutoScrollPDFViewController(document: document)
return pdfViewController
}
}
class PauseAutoScrollGestureRecognizer: UIGestureRecognizer {
override init(target: Any?, action: Selector?) {
super.init(target: target, action: action)
cancelsTouchesInView = false
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
if touches.count != 1 {
state = .failed
return
}
state = .began
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
state = .ended
}
}
private class AutoScrollPDFViewController: PDFViewController, UIGestureRecognizerDelegate {
var scrollTimer: Timer?
var pauseAutoScrollGestureRecognizer: PauseAutoScrollGestureRecognizer?
var scrollingPaused = false
override func commonInit(with document: Document?, configuration: PDFConfiguration) {
super.commonInit(with: document, configuration: configuration)
updateConfiguration(builder: { builder in
builder.pageTransition = .scrollContinuous
builder.scrollDirection = .vertical
})
}
override func viewDidLoad() {
super.viewDidLoad()
pauseAutoScrollGestureRecognizer = PauseAutoScrollGestureRecognizer(target: self, action: #selector(handlePauseAutoScroll(_:)))
pauseAutoScrollGestureRecognizer?.delegate = self
if let pauseAutoScrollGestureRecognizer {
documentViewController?.view.addGestureRecognizer(pauseAutoScrollGestureRecognizer)
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
scrollTimer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(scroll), userInfo: nil, repeats: true)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
scrollTimer?.invalidate()
scrollTimer = nil
}
@objc func scroll() {
if scrollingPaused {
return
}
guard let documentViewController else { return }
// The layout object knows how many spreads we currently have:
let numberOfSpreads = documentViewController.layout.numberOfSpreads
let lastSpreadIndex = CGFloat(numberOfSpreads > 0 ? numberOfSpreads - 1 : 0)
// We scroll by updating the continuous spread index as long as we can:
let continuousSpreadIndex: CGFloat = documentViewController.continuousSpreadIndex + 0.001
if continuousSpreadIndex <= lastSpreadIndex {
documentViewController.continuousSpreadIndex = continuousSpreadIndex
}
}
@objc func handlePauseAutoScroll(_ tapGestureRecognizer: UITapGestureRecognizer?) {
if tapGestureRecognizer?.state == .began {
scrollingPaused = true
}
if tapGestureRecognizer?.state == .ended {
scrollingPaused = false
}
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
// We'll ignore long presses but allow everything else, such as scrolling.
if gestureRecognizer == pauseAutoScrollGestureRecognizer && (otherGestureRecognizer is UILongPressGestureRecognizer) {
return false
}
return true
}
}

This code sample is an example that illustrates how to use our SDK. Please adapt it to your specific use case.