SwiftUI

Since version 10.1 for iOS, 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.

Note that all the APIs mentioned here require iOS 13 or up, since SwiftUI and Combine were introduced with iOS 13.

Showing a PDF

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

1
2
3
4
5
@ObservedObject var document: Document

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

The ObservableObject property wrapper is applied on the Document object to make sure all internal document changes are reflected in the object that’s provided.

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:

Copy
1
2
3
4
5
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 with the conventional options by using the updateConfiguration(builder:) view modifier, like this:

1
2
3
4
PDFView(document: _document)
    .updateConfiguration { 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 use a parent navigation bar but not a custom navigation bar, you’d want to use the following navigation bar configuration:

Copy
1
2
3
4
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:

1
2
3
4
PDFView(document: _document)
    .onWillBeginDisplayingPageView { _, pageIndex in
        print("Displaying page \(pageIndex)")
    }

All the exposed delegate view modifiers are listed below:

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:

Copy
1
2
3
4
5
6
7
8
9
10
@State var actionEventPublisher = PassthroughSubject<PDFView.ActionEvent, Never>()

PDFView(document: _document, actionEventPublisher: actionEventPublisher)
.navigationBarItems(trailing:
    HStack {
        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:

Copy
1
2
3
4
5
6
7
8
@State var pageIndex = PageIndex(0)

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

Navigation Bar Setup

If you’re integrating PDFView in a screen in your app that already has a navigation bar and you want PSPDFKit to take it over, you can add the useParentNavigationBar(true) view modifier. It could look like this:

Copy
1
2
3
4
5
6
PDFView(document: _document)
    .useParentNavigationBar(true)
    .updateControllerConfiguration { controller in
        controller.navigationItem.setRightBarButtonItems(
            [controller.thumbnailsButtonItem, controller.annotationButtonItem],
            for: .document, animated: false)

To make PDFView use a custom navigation bar, you can wrap it in a NavigationView:

1
2
3
NavigationView {
    PDFView(document: _document)
}

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.