Customize the Annotation Toolbar

The PSPDFAnnotationToolbar in PSPDFKit was designed to be flexible and highly configurable. It's based on the PSPDFFlexibleToolbar which is a subclass of UIView and not UIToolbar. Early version of PSPDFKit used a toolbar-based version, but it turned out to be too inflexible.

By default the annotation toolbar can either dock to the top or anchor on the left/right side of the PSPDFViewController's view.

This is fully configurable via setting PSPDFFlexibleToolbarPosition and setting either supportedToolbarPositions or toolbarPosition on the toolbar.

Copy
1
2
3
4
5
6
7
8
9
public struct PSPDFFlexibleToolbarPosition : OptionSet {
    public init(rawValue: UInt)

    public static var positionInTopBar: PSPDFFlexibleToolbarPosition { get }
    public static var positionLeft: PSPDFFlexibleToolbarPosition { get }
    public static var positionRight: PSPDFFlexibleToolbarPosition { get }
    public static var positionsVertical: PSPDFFlexibleToolbarPosition { get }
    public static var positionsAll: PSPDFFlexibleToolbarPosition { get }
}
Copy
1
2
3
4
5
6
7
8
typedef NS_OPTIONS(NSUInteger, PSPDFFlexibleToolbarPosition) {
    PSPDFFlexibleToolbarPositionNone      = 0,
    PSPDFFlexibleToolbarPositionInTopBar  = 1 << 0,
    PSPDFFlexibleToolbarPositionLeft      = 1 << 1,
    PSPDFFlexibleToolbarPositionRight     = 1 << 2,
    PSPDFFlexibleToolbarPositionsVertical = PSPDFFlexibleToolbarPositionLeft | PSPDFFlexibleToolbarPositionRight,
    PSPDFFlexibleToolbarPositionsAll      = PSPDFFlexibleToolbarPositionInTopBar | PSPDFFlexibleToolbarPositionsVertical
};

Presentation

The annotation toolbar can be shown or hidden using the annotationButtonItem defined on PSPDFViewController. This bar button item is already part of the default rightBarButtonItems on PSPDFNavigationItem. If you like, you can of course customize its placing to your liking.

If you want to invoke the annotation toolbar programmatically, you have two options.

You can either invoke the annotationButtonItem by using action dispatching:

Copy
1
2
3
let annotationButtonItem = pdfController.annotationButtonItem
guard let action = annotationButtonItem.action else { return }
UIApplication.shared.sendAction(action, to: annotationButtonItem.target, from: nil, for: nil)
Copy
1
2
UIBarButtonItem *annotationButtonItem = pdfController.annotationButtonItem;
[UIApplication.sharedApplication sendAction:annotationButtonItem.action to:annotationButtonItem.target from:nil forEvent:nil];

Or toggle the toolbar manually:

Copy
1
2
3
4
5
6
// If we're not in document view mode, it'll be weird.
pdfController.setViewMode(.document, animated: true)
pdfController.annotationToolbarController?.updateHostView(nil, container: nil, viewController: pdfController)
PSPDFUsernameHelper.ask(forDefaultAnnotationUsernameIfNeeded: pdfController, completionBlock: { _ in
    pdfController.annotationToolbarController?.toggleToolbar(animated: true)
})
Copy
1
2
3
4
5
6
// If we're not in document view mode, it'll be weird.
[pdfController setViewMode:PSPDFViewModeDocument animated:YES];
[pdfController.annotationToolbarController updateHostView:nil container:nil viewController:pdfController];
[PSPDFUsernameHelper askForDefaultAnnotationUsernameIfNeeded:pdfController completionBlock:^(NSString *userName) {
    [pdfController.annotationToolbarController toggleToolbarAnimated:YES];
}];

Asking for the user's author name is an optional, but recommended step. This way you will ensure than newly created annotations are correctly associated with the right author name.

Toolbar buttons

Annotation buttons

The annotation toolbar utilizes button grouping order to efficiently display a large amount of annotation tools. The toolbar comes pre-configured with default annotation groups for both iPad and iPhone, but you can also set your own groups by assigning new groups by creating a PSPDFAnnotationToolbarConfiguration object.

Toolbar groups are defined as an array of PSPDFAnnotationGroup objects, which themselves contain PSPDFAnnotationGroupItem instances.

Copy
1
2
3
4
5
6
7
8
9
let configuration = PSPDFAnnotationToolbarConfiguration(annotationGroups: [
        PSPDFAnnotationGroup(items: [
            PSPDFAnnotationGroupItem(type: .ink, variant: .inkVariantPen, configurationBlock: PSPDFAnnotationGroupItem.inkConfigurationBlock())
        ]),
        PSPDFAnnotationGroup(items: [
            PSPDFAnnotationGroupItem(type: .line),
            PSPDFAnnotationGroupItem(type: .polyLine)
        ])
])
Copy
1
2
3
4
5
6
7
8
9
PSPDFAnnotationToolbarConfiguration *configuration = [[PSPDFAnnotationToolbarConfiguration alloc] initWithAnnotationGroups:@[

    [PSPDFAnnotationGroup groupWithItems:@[
        [PSPDFAnnotationGroupItem itemWithType:PSPDFAnnotationStringInk variant:PSPDFAnnotationStringInkVariantPen configurationBlock:[PSPDFAnnotationGroupItem inkConfigurationBlock]]]],

    [PSPDFAnnotationGroup groupWithItems:@[
        [PSPDFAnnotationGroupItem itemWithType:PSPDFAnnotationStringLine],
        [PSPDFAnnotationGroupItem itemWithType:PSPDFAnnotationStringPolyLine]]]
]];

And to finally set the PSPDFAnnotationToolbarConfiguration in your PSPDFViewController you can use this code after creating the PSPDFViewController:

1
controller.annotationToolbarController?.annotationToolbar.configurations = [configuration]
1
controller.annotationToolbarController.annotationToolbar.configurations = @[configuration];

To customize the button icon, return an UIImage containing your custom icon from the configurationBlock. Whenever possible try to return a template image from the configuration block (UIImageRenderingModeAlwaysTemplate):

Copy
1
2
3
4
let configurationBlock = { (item: PSPDFAnnotationGroupItem, container: AnyObject?, tintColor: UIColor) -> UIImage in
    let image = UIImage(named: "Custom Button Icon")!
    return image.withRenderingMode(.alwaysTemplate)
}
Copy
1
2
3
4
PSPDFAnnotationGroupItemConfigurationBlock configurationBlock = ^UIImage *(PSPDFAnnotationGroupItem *item, id container, UIColor *tintColor) {
    UIImage *image = [UIImage imageNamed:@"Custom Button Icon"];
    return [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
};

Use the provided tint color only when you need multi-color images.

You can disable annotation toolbar configurations by setting the configuration property to nil. In that case the toolbar will show a list of all editableAnnotationTypes without any grouping.

Standard buttons

In addition to the annotation group buttons, the toolbar also provides some additional buttons to manage toolbar presentation, undo / redo and access the style manager. The buttons get automatically added or are omitted depending on the toolbar and PSPDFKit configuration settings. Those buttons can be customized by overriding the doneButton, undoButton, redoButton and strokeColorButton properties. The buttons can also be completely removed by returning nil from the overridden getters.

To overwrite the properties you will have to overwrite the PSPDFAnnotationToolbar class. Please take a look at the Overriding Classes help article for more information.

Custom buttons

The annotation toolbar also provides a convenient hook to add additional, non annotation type specific, buttons to the toolbar. You add those buttons by assigning an array of PSPDFToolbarButton items to the additionalButtons property on the annotation toolbar. The buttons will be positioned in between the annotation buttons and the undo / redo buttons.

Button overflow

The toolbar usually auto-sizes to accommodate all of its buttons. If this can not be achieved due lack of available view real estate, the toolbar automatically clips buttons flagged with the collapsible flag (from PSPDFToolbarButton) and groups them in a special collapsedButtons item.

Auto sizing

In vertical mode, the annotation toolbar will automatically size its height depending on the available screen real estate, the available toolbar configurations and the active standard toolbar buttons. The final toolbar height is determined by first querying -[PSPDFFlexibleToolbarContainerDelegate flexibleToolbarContainerContentRect:forToolbarPosition:], a method PSPDFViewController implements and one you could override if you have custom elements that the toolbar should avoid. The toolbar than checks the required sizing constraints of all registered toolbar configurations and related standard buttons, trying to find the configuration that best fits the rect flexibleToolbarContainerContentRect:forToolbarPosition: returned. If toolbar configurations are disabled (configurations == nil) the toolbar will auto-size to fit as many annotation types from editableAnnotationTypes as possible.

The buttons property of the annotation toolbar is set only after toolbar sizing completes. This is required, because the toolbar size is a prerequisite for determining which buttons will actually be shown on the toolbar. If you modify the buttons property of the annotation toolbar manually, auto-sizing might no longer yield acceptable results. In that case you will have to override preferredSizeFitting:forToolbarPosition: and manually adjust the sizing.

Appearance customization

The annotation toolbar exposes a variety of staying hooks for either direct or UIAppearance-based customization. See Appearance styling for further info.

Setting the toolbarDelegate of the PSPDFFlexibleToolbar

This delegate isn't used by PSPDFKit and can be set freely.

The easiest way is to simply access the annotation toolbar via the annotation toolbar controller:

Copy
1
2
let pdfController = PSPDFViewController(document: document)
pdfController.annotationToolbarController?.annotationToolbar.toolbarDelegate = toolbarDelegate
Copy
1
2
PSPDFViewController *pdfController = [[PSPDFViewController alloc] initWithDocument:document];
pdfController.annotationToolbarController.annotationToolbar.toolbarDelegate = toolbarDelegate;

You can also create a subclass of the PSPDFAnnotationToolbarController and override annotationToolbar to set it there, or create a subclass of the PSPDFAnnotationToolbar and override initWithAnnotationStateManager: to set it.

Showing and hiding the annotation toolbar

You can show and hide the annotation toolbar with PSPDFAnnotationToolbarController.

First you have to set the hostView with -[PSPDFFlexibleToolbarController updateHostView:container:viewController:]:

Copy
1
2
let pdfController = PSPDFViewController(document: document)
pdfController.annotationToolbarController?.updateHostView(nil, container: nil, viewController: pdfController)
Copy
1
2
PSPDFViewController *pdfController = [[PSPDFViewController alloc] initWithDocument:document];
[pdfController.annotationToolbarController updateHostView:nil container:nil viewController:pdfController];

After that you can show the toolbar with -[PSPDFFlexibleToolbarController showToolbarAnimated:]:

1
2
let pdfController = PSPDFViewController(document: document)
pdfController.annotationToolbarController?.showToolbar(animated: true)
Copy
1
2
PSPDFViewController *pdfController = [[PSPDFViewController alloc] initWithDocument:document];
[pdfController.annotationToolbarController showToolbarAnimated:YES];

To hide the toolbar use -[PSPDFFlexibleToolbarController hideToolbarAnimated:]:

1
2
let pdfController = PSPDFViewController(document: document)
pdfController.annotationToolbarController?.hideToolbar(animated: true)
Copy
1
2
PSPDFViewController *pdfController = [[PSPDFViewController alloc] initWithDocument:document];
[pdfController.annotationToolbarController hideToolbarAnimated:YES];