Customizing PDF Viewer Styling on iOS

To seamlessly integrate PSPDFKit into your application, you will likely want to adjust some of the appearance settings of the framework UI elements. PSPDFKit was designed to be very flexible in this regard and offers a wide variety of appearance-centric properties and customization hooks.

Tint Color

PSPDFKit conforms to the standard UIView tintColor inheritance scheme and will automatically adopt your application’s global tint color. You can set the desired PSPDFKit tintColor by setting the tintColor property on your current window or on any other common superview of PSPDFKit view components. You are also free to set the tintColor property on any of the PSPDFKit UIView subclasses, either by setting the instance property directly or by using the UIAppearance protocol.

Status Bar

PSPDFKit was designed with a view controller-based status bar appearance in mind. In order to get the correct status bar appearance and behaviors for PSPDFKit view controllers, ensure the UIViewControllerBasedStatusBarAppearance flag in your app’s Info.plist is set. (This is the default behavior, but some old products might have set this to NO. In that case, you first need to update your application.)

ℹ️ Note: When the application root view controller is a UINavigationController instance, the status bar appearance is deferred by the barStyle property of the navigation controller’s navigationBar. Therefore, if you show PDFViewController in a UINavigationController, be sure to correctly set the navigation bar barStyle in order to get the desired status bar style.

UIAppearance

The recommended and easiest way to style PSPDFKit to your liking is using the UIAppearance protocol. PSPDFKit instance properties that were designed to be used with UIAppearance are marked with the standard UI_APPEARANCE_SELECTOR qualifier.

ℹ️ Note: It is important that you apply UIAppearance settings early in your application (or view controller) lifecycle. The appearance settings should be in place before viewDidLoad() is called.

UINavigationBar and UIToolbar Styling

Two of the most likely candidates for appearance customization are UINavigationBar and UIToolbar. If you are already setting the appearance of either one using UIAppearance selectors in your app, PSPDFKit will inherit these settings.

However, applying default styles to UINavigationBar and UIToolbar directly will have no effect on instances created by the PSPDFKit framework and might have the unwanted side effect of also leaking certain appearance styles to system-provided view controllers (like MFMailComposeViewController). Thus we recommended that you instead limit the UINavigationBar and UIToolbar customization to your application only. In order to achieve this with PSPDFKit as well, you can limit the appearance customization to instances inside PDFNavigationController. PDFNavigationController is a simple UINavigationController subclass that adds some additional features and is used throughout PSPDFKit. If you’re presenting PDFViewController inside a UINavigationController (the most common case), you might want to use PDFNavigationController to ease styling. If you’re using your own UINavigationController or embedding PDFViewController in another view controller, it will be your responsibility to style the container controller and use the correct appearance bounding for UINavigationBar and UIToolbar.

ℹ️ Note: Customizing the bar appearance (including the background color) needs to be done using the UIBarAppearance API instead of the legacy customization API. This applies to navigation bars, the scrubber bar (both floating and docked), and all the toolbars. You can set the bar appearance using the standardAppearance and compactAppearance properties on UINavigationBar, ScrubberBar, and Toolbar (the superclass of AnnotationToolbar, PDFDocumentEditorToolbar, and FreeTextAccessoryView, respectively).

Basic Appearance Customization Example

let mainColor: UIColor = UIColor(white: 0.2, alpha: 1)
let secondaryColor = UIColor.systemOrange

// Update the window's `tintColor` as soon as a new window is created.
currentWindow.tintColor = mainColor;

// Navigation bar and toolbar customization. We're limiting appearance customization to instances that are
// inside `PDFNavigationController` so that we don't affect the appearance of certain system controllers.
let navBarProxy = UINavigationBar.appearance(whenContainedInInstancesOf: [PDFNavigationController.self])
let toolbarProxy = UIToolbar.appearance(whenContainedInInstancesOf: [PDFNavigationController.self])

// `UINavigationBar` styling.
let navigationBarAppearance = UINavigationBarAppearance()
navigationBarAppearance.backgroundColor = mainColor

navBarProxy.standardAppearance = navigationBarAppearance
navBarProxy.compactAppearance = navigationBarAppearance
navBarProxy.scrollEdgeAppearance = navigationBarAppearance

// `UIToolbar` styling.
let toolbarAppearance = UIToolbarAppearance()
toolbarAppearance.backgroundColor = mainColor

toolbarProxy.standardAppearance = toolbarAppearance
toolbarProxy.compactAppearance = toolbarAppearance

// Make sure we're getting a light title and status bar.
navBarProxy.overrideUserInterfaceStyle = .dark

navBarProxy.tintColor = secondaryColor
toolbarProxy.tintColor = secondaryColor
UIColor *mainColor = [UIColor colorWithWhite:0.2f alpha:1.f];
UIColor *secondaryColor = [UIColor systemOrangeColor];

// Update the window's `tintColor` as soon as a new window is created.
currentWindow.tintColor = mainColor;

// Navigation bar and toolbar customization. We're limiting appearance customization to instances that are
// inside `PSPDFNavigationController` so that we don't affect the appearance of certain system controllers.
UINavigationBar *navBarProxy = [UINavigationBar appearanceWhenContainedInInstancesOfClasses:@[PSPDFNavigationController.class]];
UIToolbar *toolbarProxy = [UIToolbar appearanceWhenContainedInInstancesOfClasses:@[PSPDFNavigationController.class]];

// `UINavigationBar` styling.
UINavigationBarAppearance *navigationBarAppearance = [[UINavigationBarAppearance alloc] init];
navigationBarAppearance.backgroundColor = mainColor;

navBarProxy.standardAppearance = navigationBarAppearance;
navBarProxy.compactAppearance = navigationBarAppearance;
navBarProxy.scrollEdgeAppearance = navigationBarAppearance;

// `UIToolbar` styling.
UIToolbarAppearance *toolbarAppearance = [[UIToolbarAppearance alloc] init];
toolbarAppearance.backgroundColor = mainColor;

// Apply the same appearance styling to all sizes of `UIToolbar`.
toolbarProxy.standardAppearance = toolbarAppearance;
toolbarProxy.compactAppearance = toolbarAppearance;

// Make sure we're getting a light title and status bar.
navBarProxy.overrideUserInterfaceStyle = UIUserInterfaceStyleDark;

navBarProxy.tintColor = secondaryColor;
toolbarProxy.tintColor = secondaryColor;

For a live example, please take a look at PSCAppDelegate.customizeAppearanceOfNavigationBar() and PSCAppDelegate.customizeAppearanceOfToolbar() in the PSPDFKit Catalog.

UINavigationBar and UIToolbar in Popovers

By default, the UINavigationBar and UIToolbar instances contained in a PDFNavigationController presented as a popover are reset to their default iOS appearances by PSPDFKit. This ensures that the bars seamlessly blend with the default popover background view.

If you want to override this behavior, you can do so in your initialization code. The styling code should be similar to the Basic Appearance Customization Example above, with the exception that we add PDFNavigationController and UIPopoverPresentationController to the hierarchy of container classes. This is so that the appearance proxy will only apply styling when we have a navigation bar embedded within a PDFNavigationController embedded within a UIPopoverPresentationController. Here’s how it would look:

let navBarPopoverProxy = UINavigationBar.appearance(whenContainedInInstancesOf: [PDFNavigationController.self, UIPopoverPresentationController.self])
let toolbarPopoverProxy = UIToolbar.appearance(whenContainedInInstancesOf: [PDFNavigationController.self, UIPopoverPresentationController.self])
UINavigationBar *navBarPopoverProxy = [UINavigationBar appearanceWhenContainedInInstancesOfClasses:@[PSPDFNavigationController.class, UIPopoverPresentationController.class]];
UIToolbar *toolbarPopoverProxy = [UIToolbar appearanceWhenContainedInInstancesOfClasses:@[PSPDFNavigationController.class, UIPopoverPresentationController.class]];

The style of the buttons (normal and selected) added to UINavigationBar and UIToolbar is inherited from the bars themselves. The tint color for these buttons is updated to match the tint color of the bars they’ve been added to, while the background color is nil. For the selected style of the buttons, the tint color is changed to that of the background color of the bar they’ve been added to, and a background color is set, which is the tint color of the bar.

Normal State Selected State

Customizing Bars Using UIAppearance with SwiftUI

Because SwiftUI offers more limited customization options than UIKit, some apps primarily using SwiftUI choose to customize the appearance of navigation bars, toolbars, and tab bars using UIAppearance. For example, as of iOS 18, UIAppearance is the only way to use a custom font for the navigation bar title.

To avoid transparent bar backgrounds in a default SwiftUI setup, PDFView uses these modifiers internally:

.toolbarBackground(.visible, for: .navigationBar)
.toolbarBackground(.visible, for: .tabBar)

A side effect of this is resetting all appearance properties of the bars, overriding customizations set up with UIAppearance while the PDFView is visible. To avoid this, add these modifiers to your PDFView:

PDFView(...)
    .toolbarBackground(.automatic, for: .navigationBar)
    .toolbarBackground(.automatic, for: .tabBar)

This results in SwiftUI leaving the bars alone, so UIAppearance customizations remain in place.

Annotation Toolbar

AnnotationToolbar (and its superclass, FlexibleToolbar) will attempt to automatically set its appearance parameters to match those of the UINavigationBar of the UINavigationController in which it is currently being displayed. In case these parameters aren’t appropriate, or if further customization is required, you can use the appearance properties defined in FlexibleToolbar to adjust the toolbar styling (using the UIAppearance protocol or setting the property values directly).

The following is an example illustrating how to customize just the annotation toolbar appearance (changing it from the default inferred appearance). You can customize the scrubber bar exactly the same way by changing AnnotationToolbar to ScrubberBar on the first line:

let annotationToolbarProxy = AnnotationToolbar.appearance()
let barColor = UIColor.systemOrange

let appearance = UIToolbarAppearance()
appearance.backgroundColor = barColor
annotationToolbarProxy.standardAppearance = appearance
annotationToolbarProxy.compactAppearance = appearance
annotationToolbarProxy.tintColor = UIColor(white: 0.2, alpha: 1)
PSPDFAnnotationToolbar *annotationToolbarProxy = [PSPDFAnnotationToolbar appearance];
UIColor *barColor = UIColor.systemOrangeColor;

UIToolbarAppearance *appearance = [[UIToolbarAppearance alloc] init];
appearance.backgroundColor = barColor;
annotationToolbarProxy.standardAppearance = appearance;
annotationToolbarProxy.compactAppearance = appearance;

annotationToolbarProxy.tintColor = [UIColor colorWithWhite:0.2f alpha:1.f];

The buttons added to the annotation toolbar follow a styling similar to that of the buttons added to the navigation and toolbar created by PSPDFKit: No background color is set for the normal state of the buttons, and the tint color is same as that of the annotation toolbar. Meanwhile, for the selected button state, the annotation toolbar tint color is set as the background color of the button, and the background color is set as the tint color of the button.

Normal State Selected State

However, the selected style for the buttons added to an annotation bar can be further customizied by adding the following:

annotationToolbarProxy.selectedBackgroundColor = UIColor(white: 0.8, alpha: 1)
annotationToolbarProxy.selectedTintColor = UIColor(white: 0.2, alpha: 1)
[annotationToolbarProxy setSelectedBackgroundColor:[UIColor colorWithWhite:0.8f alpha:1.f]];
[annotationToolbarProxy setSelectedTintColor:[UIColor colorWithWhite:0.2f alpha:1.f]];

Changing the Scrubber Bar Background Color without UIAppearance

An alternative to using the UIAppearance proxy is to set the properties on the object directly:

let scrubberBar = pdfViewController.userInterfaceView.scrubberBar
let barColor = .systemOrange

let appearance = UIToolbarAppearance()
appearance.backgroundColor = barColor

scrubberBar.standardAppearance = appearance
scrubberBar.compactAppearance = appearance
PSPDFScrubberBar *scrubberBar = pdfViewController.userInterfaceView.scrubberBar;
UIColor *barColor = UIColor.systemOrangeColor;

UIToolbarAppearance *appearance = [[UIToolbarAppearance alloc] init];
appearance.backgroundColor = barColor;

scrubberBar.standardAppearance = appearance;
scrubberBar.compactAppearance = appearance

Font Customization

You can change the font of a view when it’s contained in a specific view controller class. For example, you can change the font of a UITextView when it’s contained in a NoteAnnotationViewController:

let textView = UITextView.appearance(whenContainedInInstancesOf: [PSPDFNoteAnnotationViewController.self])
textView.font = UIFont(name: "Courier", size: 14)
UITextView *textView = [UITextView appearanceWhenContainedInInstancesOfClasses:@[[PSPDFNoteAnnotationViewController class]]];
textView.font = [UIFont fontWithName:@"Courier" size:14.f];

Further Reading