SwiftUI PDF Library

PSPDFKit exposes specific APIs to use PSPDFKit with SwiftUI out of the box. This makes dealing with PSPDFKit in a SwiftUI app easier, and it doesn’t require you to wrap PDFViewController yourself if you want to go the SwiftUI route.

The main entry point is the PDFView struct, which conforms to SwiftUI’s View protocol. PDFView wraps PDFViewController into a SwiftUI-compatible container.

Showing a PDF

To show a PDF, use PDFView in the body of your SwiftUI view, and pass it a Document:

var document: Document

var body: some View {
    PDFView(document: document)
}

Configuration

PDFView has special view modifiers to make configuring it as easy as possible in SwiftUI. The most important properties from PDFConfiguration are available. While currently not all available options from PDFConfiguration are available as view modifiers for the PDFView, the more widely used configuration options are available.

Using these view modifiers in action would look something like this:

PDFView(document: document)
    .scrollDirection(.vertical)
    .pageTransition(.scrollContinuous)
    .pageMode(.single)
    .spreadFitting(.fill)

In case you ever find yourself needing a configuration option that hasn’t yet been mapped to a view modifier, you can always fall back to using a PDFConfigurationBuilder and configuring the SwiftUI PDFView in the initializer, like this:

PDFView(document: document) { builder in
    builder.searchResultZoomScale = 2
}

These are all the view modifier configurations available:

Controller Setup

Since most configuration options, delegates, and actions are exposed in a SwiftUI-friendly way, there are only a few reasons why you should fall back to using the underlying PDFViewController for more advanced setups. You can access the PDFViewController via the updateControllerConfiguration(block:) view modifier.

For example, if you want to access properties or methods of PDFViewController that aren’t yet available in SwiftUI, use the following configuration:

PDFView(document: document)
    .updateControllerConfiguration { pdfController in
        // Use the underlying PDFViewController (pdfController) here.
    }

Delegates

Some of the most relevant delegate methods from PDFViewControllerDelegate are exposed using view modifiers that take a closure, which are called when a corresponding event happens.

To get a callback whenever a page is displayed, you can use this:

PDFView(document: document)
    .onWillBeginDisplayingPageView { _, pageIndex in
        print("Displaying page \(pageIndex)")
    }

You can find the exposed delegate view modifiers in the API documentation.

Actions

Executing actions on the PDFView can also be done in SwiftUI. For this, we expose the actionEventPublisher subject. You can send various events to this subject to execute all kinds of actions. The action events are of type ActionEvent:

@State var actionEventPublisher = PassthroughSubject<PDFView.ActionEvent, Never>()

PDFView(document: document, actionEventPublisher: actionEventPublisher)
.toolbar {
    Button("Next Page") {
        actionEventPublisher.send(.scrollToNextSpread(animated: true))
    }
}

Bindings

The bindings on PDFView can be used to transfer state back and forth between your logic and SwiftUI, with both sides always being automatically up to date.

The available bindings are:

  • pageIndexBinding — Binds the current pageIndex of the underlying PDFViewController. It always reflects the current value of the page index. Changing it will set the page index on PDFViewController.

  • viewModeBinding — Binds the current viewMode of the underlying PDFViewController. It always reflects the current value of the view mode. Changing it will set the view mode on PDFViewController.

  • selectedAnnotationsBinding — Binds the current selectedAnnotations of the visible PDFPageView from the underlying PDFViewController. It always reflects the current value of the selected annotations. Changing it will set the selected annotations on the visible page view. Note that all the annotations that will be set in this binding need to be on the same page.

All of these are provided in the initializer using the Binding property wrapper.

For example, to add a stepper that changes the current page next to a label indicating the current page index, you can use this code snippet:

@State var pageIndex = PageIndex(0)

VStack {
    Stepper("Current Page: \(pageIndex + 1)",
        value: $pageIndex,
        in: 0...document.pageCount - 1)
    PDFView(document: document, pageIndex: $pageIndex)
}

To make PDFView show a navigation bar, you can wrap it in a NavigationView or NavigationStack, like this:

NavigationStack {
    PDFView(document: document)
}

If you’re integrating PDFView in a screen in your app that already has a navigation bar set up using UIKit and you want PSPDFKit to take it over, make sure PDFView is in the view hierarchy of the UIHostingController that’s added in a UINavigationController, like this:

struct ContentView: View {
    let document: Document
    var body: some View {
        PDFView(document: document)
    }
}

let hostingController = UIHostingController(rootView: ContentView(document: document))
navigationController.pushViewController(hostingController, animated: true)

By default, PDFView won’t add any buttons or set the title of the navigation bar. To learn how to add buttons or set the title, refer to the following sections.

Toolbar Buttons

To add toolbar buttons to your PDFView, you can use the SwiftUI toolbar(_:) modifier. PSPDFKit exposes a variety of buttons or a set of buttons that can be used in a toolbar. DefaultToolbarButtons provides a set of default buttons that offer common functionalities for all different view modes. The default buttons may change in future releases of PSPDFKit.

When setting up toolbar buttons, you must provide a scope using the @PDFView.Scope property wrapper and set it using the pdfViewScope(_:) modifier in the view hierarchy that covers both the toolbar modifier and PDFView. PSPDFKit uses this scope to share state and configuration between the buttons and PDFView. This can be done like this:

@PDFView.Scope var scope

PDFView(document: document)
    .toolbar {
        DefaultToolbarButtons()
    }
    .pdfViewScope(scope)

You can also individually show buttons to fit your application’s specific needs by using views like AnnotationButton, ThumbnailsButton, or ContentEditingButton, like this:

@PDFView.Scope var scope

PDFView(document: document)
    .toolbar {
        ContentEditingButton()
    }
    .pdfViewScope(scope)

To control whether the document title is shown in the navigation bar, use the showDocumentTitle(_:) modifier. This sets whether the document title should only be shown in the navigation bar, shown in the document label (which floats above document content), or adapt based on the available width, which is the default. You can show the document title like this:

PDFView(document: document)
    .showDocumentTitle()

Publishers

Additionally, there are some publishers exposed, allowing you to keep track of changes in an easier, more SwiftUI-friendly way.

While this API is comfortable to use in SwiftUI, it’s not exclusive to SwiftUI; it can also be used when integrating PDFViewController traditionally via UIKit.

These publishers are built on top of Apple’s Combine framework.

They include documentPublisher, pageIndexPublisher on PDFViewController and savePublisher, and annotationChangePublisher on Document.

Examples

For more details on how to integrate PDFView, look at the SwiftUI examples in the Catalog example project, as they show various use cases when using SwiftUI. You might also want to check out the dedicated SwiftUIDocumentBrowser example, which shows how to integrate PSPDFKit into a document-based SwiftUI app.