Customizing the Annotation Toolbar on iOS
The AnnotationToolbar
(opens in a new tab) in PSPDFKit was designed to be flexible and highly configurable. It’s based on the FlexibleToolbar
(opens in a new tab), which is a subclass of UIView
and not UIToolbar
. Early versions 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 PDFViewController
’s view.
This is fully configurable via setting FlexibleToolbar.Position
and setting either supportedToolbarPositions
(opens in a new tab) or toolbarPosition
(opens in a new tab) on the toolbar:
.inTopBar
.left
.right
.top
.horizontal
.vertical
.default
.all
If FlexibleToolbar.Position.top
is a supported toolbar position, you must disable the document label by setting documentLabelEnabled
(opens in a new tab) to AdaptiveConditional.NO
in your PDFConfiguration
(opens in a new tab). Otherwise, an assertion will occur.
Presentation
The annotation toolbar can be shown or hidden using the annotationButtonItem
(opens in a new tab) defined on PDFViewController
. This bar button item is already part of the default rightBarButtonItems
on NavigationItem
(opens in a new tab). If you like, you can of course customize its placement to your liking.
If you want to invoke the annotation toolbar programmatically, you have two options.
You can invoke the annotationButtonItem
by using action dispatching:
let annotationButtonItem = pdfController.annotationButtonItemlet action = annotationButtonItem.action!UIApplication.shared.sendAction(action, to: annotationButtonItem.target, from: nil, for: nil)
UIBarButtonItem *annotationButtonItem = pdfController.annotationButtonItem;[UIApplication.sharedApplication sendAction:annotationButtonItem.action to:annotationButtonItem.target from:nil forEvent:nil];
Or you can toggle the toolbar manually:
// If not in document view mode, it'll be weird.pdfController.setViewMode(.document, animated: true)pdfController.annotationToolbarController?.updateHostView(nil, container: nil, viewController: pdfController)UsernameHelper.ask(forDefaultAnnotationUsernameIfNeeded: pdfController, completionBlock: { _ in pdfController.annotationToolbarController?.toggleToolbar(animated: true)})
// If 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’ll ensure the newly created annotations are associated with the correct 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 preconfigured with default annotation groups for both iPad and iPhone, but you can also set your own groups by assigning new groups, which is done by creating an AnnotationToolConfiguration
(opens in a new tab) object.
Toolbar groups are defined as an array of AnnotationToolConfiguration.ToolGroup
(opens in a new tab) objects, which themselves contain AnnotationToolConfiguration.ToolItem
(opens in a new tab) instances:
let configuration = AnnotationToolConfiguration(annotationGroups: [ AnnotationToolConfiguration.ToolGroup(items: [ AnnotationToolConfiguration.ToolItem(type: .ink, variant: .inkPen, configurationBlock: AnnotationToolConfiguration.ToolItem.inkConfigurationBlock()) ]), AnnotationToolConfiguration.ToolGroup(items: [ AnnotationToolConfiguration.ToolItem(type: .line), AnnotationToolConfiguration.ToolItem(type: .polyLine) ])])
PSPDFAnnotationToolbarConfiguration *configuration = [[PSPDFAnnotationToolbarConfiguration alloc] initWithAnnotationGroups:@[ [PSPDFAnnotationGroup groupWithItems:@[ [PSPDFAnnotationGroupItem itemWithType:PSPDFAnnotationStringInk variant:PSPDFAnnotationVariantStringInkPen configurationBlock:[PSPDFAnnotationGroupItem inkConfigurationBlock]] ]], [PSPDFAnnotationGroup groupWithItems:@[ [PSPDFAnnotationGroupItem itemWithType:PSPDFAnnotationStringLine], [PSPDFAnnotationGroupItem itemWithType:PSPDFAnnotationStringPolyLine] ]]]];
Finally, to set the AnnotationToolConfiguration
(opens in a new tab) in your PDFViewController
, you can use this code after creating the PDFViewController
:
controller.annotationToolbarController?.annotationToolbar.configurations = [configuration]
controller.annotationToolbarController.annotationToolbar.configurations = @[configuration];
To get the default button icons for tools with variants, pass one of the following as the configurationBlock
argument of the ToolItem
initializer:
AnnotationToolConfiguration.ToolItem.inkConfigurationBlock()
(opens in a new tab)AnnotationToolConfiguration.ToolItem.lineConfigurationBlock()
(opens in a new tab)AnnotationToolConfiguration.ToolItem.freeTextConfigurationBlock()
(opens in a new tab)AnnotationToolConfiguration.ToolItem.polygonConfigurationBlock()
(opens in a new tab)AnnotationToolConfiguration.ToolItem.measurementConfigurationBlock()
(opens in a new tab)
To customize the button icon, return a UIImage
containing your custom icon from the configurationBlock
(opens in a new tab). Whenever possible, try to return a template image from the configuration block (UIImageRenderingModeAlwaysTemplate
(opens in a new tab)):
let configurationBlock = { (item: AnnotationToolConfiguration.ToolItem, container: AnyObject?, tintColor: UIColor) -> UIImage in let image = UIImage(named: "Custom Button Icon")! return image.withRenderingMode(.alwaysTemplate)}
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 such a case, the toolbar will show a list of all editableAnnotationTypes
(opens in a new tab) 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 actions, and access the style manager. The buttons are automatically added or omitted, depending on the toolbar and PSPDFKit configuration settings. These 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 override the properties, you’ll have to subclass AnnotationToolbar
(opens in a new tab). Read the overriding classes guide 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 these buttons by assigning an array of ToolbarButton
(opens in a new tab) items to the additionalButtons
(opens in a new tab) 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 cannot be achieved due lack of available view real estate, the toolbar automatically clips buttons flagged with the collapsible
flag (from ToolbarButton
(opens in a new tab)) 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 FlexibleToolbarContainerDelegate.flexibleToolbarContainerContentRect(_:for:)
(opens in a new tab), a method PDFViewController
implements and one you could override if you have custom elements that the toolbar should avoid. The toolbar then checks the required sizing constraints of all registered toolbar configurations and related standard buttons, trying to find the configuration that best fits the rect that flexibleToolbarContainerContentRect(_:for:)
returned. If toolbar configurations are disabled (configurations == nil
), the toolbar will auto-size to fit as many annotation types from editableAnnotationTypes
(opens in a new tab) 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’ll have to override preferredSizeFitting(_:for:)
(opens in a new tab) and manually adjust the sizing.
Appearance Customization
The annotation toolbar exposes a variety of staying hooks for either direct or UIAppearance
-based customization. See the appearance styling(opens in a new tab) guide for additional information.
Setting the toolbarDelegate of the FlexibleToolbar
The toolbarDelegate
(opens in a new tab) of the FlexibleToolbar
(opens in a new tab) isn’t used by PSPDFKit and can be set freely.
The easiest way to do this is to access the annotation toolbar via the annotation toolbar controller:
let pdfController = PDFViewController(document: document)pdfController.annotationToolbarController?.annotationToolbar.toolbarDelegate = toolbarDelegate
PSPDFViewController *pdfController = [[PSPDFViewController alloc] initWithDocument:document];pdfController.annotationToolbarController.annotationToolbar.toolbarDelegate = toolbarDelegate;
You can also create a subclass of AnnotationToolbarController
(opens in a new tab) and override annotationToolbar
(opens in a new tab) to set it there, or create a subclass of AnnotationToolbar
(opens in a new tab) and override init(annotationStateManager:)
(opens in a new tab) to set it.
Showing and Hiding the Annotation Toolbar
You can show and hide the annotation toolbar with AnnotationToolbarController
(opens in a new tab).
First you have to set the hostView
(opens in a new tab) with FlexibleToolbarController.updateHostView(_:container:viewController:)
(opens in a new tab):
let pdfController = PDFViewController(document: document)pdfController.annotationToolbarController?.updateHostView(nil, container: nil, viewController: pdfController)
PSPDFViewController *pdfController = [[PSPDFViewController alloc] initWithDocument:document];[pdfController.annotationToolbarController updateHostView:nil container:nil viewController:pdfController];
After that, you can show the toolbar with FlexibleToolbarController.showToolbar(animated:)
(opens in a new tab):
let pdfController = PDFViewController(document: document)pdfController.annotationToolbarController?.showToolbar(animated: true)
PSPDFViewController *pdfController = [[PSPDFViewController alloc] initWithDocument:document];[pdfController.annotationToolbarController showToolbarAnimated:YES];
To hide the toolbar, use FlexibleToolbarController.hideToolbar(animated:)
(opens in a new tab):
let pdfController = PDFViewController(document: document)pdfController.annotationToolbarController?.hideToolbar(animated: true)
PSPDFViewController *pdfController = [[PSPDFViewController alloc] initWithDocument:document];[pdfController.annotationToolbarController hideToolbarAnimated:YES];