Configure PDF Annotation Toolbar using Swift for iOS
Manually configure and show the annotation toolbar, without UINavigationController
or PSPDFAnnotationBarButtonItem
. Get additional resources by visiting our guide on customizing the annotation toolbar in iOS.
//// Copyright © 2017-2025 PSPDFKit GmbH. All rights reserved.//// The Nutrient sample applications are licensed with a modified BSD license.// Please see License for details. This notice may not be removed from this file.//
import PSPDFKitimport PSPDFKitUI
class ManualToolbarSetupExample: Example {
override init() { super.init() title = "Manual annotation toolbar setup and management" contentDescription = "Flexible toolbar handling without UINavigationController or PSPDFAnnotationBarButtonItem." category = .barButtons priority = 220 }
override func invoke(with delegate: ExampleRunnerDelegate) -> UIViewController? { let document = AssetLoader.document(for: .annualReport) let controller = ManualToolbarSetupViewController(document: document) delegate.currentViewController?.present(controller, animated: true) // Present modally, so we can more easily configure it to have a different style. return nil }}
private class ManualToolbarSetupViewController: UIViewController, UIToolbarDelegate, FlexibleToolbarContainerDelegate {
private let pdfController: PDFViewController private lazy var customToolbar = UIToolbar(frame: CGRect(x: 0, y: 20, width: view.bounds.width, height: 44)) private var flexibleToolbarContainer: FlexibleToolbarContainer?
// MARK: Lifecycle
init(document: Document) { // Add PDFViewController as a child view controller. pdfController = PDFViewController(document: document) { $0.userInterfaceViewMode = .never $0.backgroundColor = UIColor.psc_systemBackground }
// Those need to be nilled out if you use the barButton items (e.g., annotationButtonItem) externally! pdfController.navigationItem.leftBarButtonItems = nil pdfController.navigationItem.rightBarButtonItems = nil
super.init(nibName: nil, bundle: nil)
addChild(pdfController) pdfController.didMove(toParent: self) }
required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
// MARK: UIViewController
override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = UIColor.psc_systemBackground
view.addSubview(pdfController.view)
// As an example, here we're not using the UINavigationController but instead a custom UIToolbar. // Note that if you're going that way, you'll lose some features that Nutrient provides, like dynamic toolbar updating or accessibility. customToolbar.delegate = self customToolbar.autoresizingMask = .flexibleWidth
// Configure the toolbar items var toolbarItems = [UIBarButtonItem]() customToolbar.isTranslucent = false toolbarItems.append(contentsOf: [UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneButtonPressed)), UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)])
// Normally we would just use the annotationButtonItem and let it do all the toolbar setup and management for us. // Here, however we'll show how one could manually configure and show the annotation toolbar without using // PSPDFAnnotationBarButtonItem. Note that PSPDFAnnotationBarButtonItem handles quite a few more // cases and should in general be preferred to this simple toolbar setup.
// It's still a good idea to check if annotations are available if pdfController.document?.canSaveAnnotations ?? false { toolbarItems.append(UIBarButtonItem(barButtonSystemItem: .compose, target: self, action: #selector(toggleToolbar))) } customToolbar.items = toolbarItems view.addSubview(customToolbar) }
override var preferredStatusBarStyle: UIStatusBarStyle { return .default }
// MARK: Annotation toolbar
@objc private func toggleToolbar(_ sender: Any) { if let flexibleToolbarContainer { flexibleToolbarContainer.hideAndRemove(animated: true) { _ in } return } let manager = pdfController.annotationStateManager let toolbar = AnnotationToolbar(annotationStateManager: manager) toolbar.matchUIBarAppearance(customToolbar) // (optional) let container = FlexibleToolbarContainer(frame: view.bounds) container.flexibleToolbar = toolbar container.overlaidBar = customToolbar container.containerDelegate = self view.addSubview(container) flexibleToolbarContainer = container container.show(animated: true) }
// MARK: FlexibleToolbarContainerDelegate
func flexibleToolbarContainerDidHide(_ container: FlexibleToolbarContainer) { flexibleToolbarContainer = nil }
func flexibleToolbarContainerContentRect(_ container: FlexibleToolbarContainer) -> CGRect { pdfController.view.frame }
// MARK: Layout
override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() // Position the pdfController content below the toolbar var frame: CGRect = view.bounds frame.origin.y = customToolbar.frame.maxY frame.size.height -= frame.origin.y pdfController.view.frame = frame }
// MARK: Public
var document: Document? { get { pdfController.document } set { pdfController.document = newValue } }
// MARK: Private
@objc private func doneButtonPressed(_ sender: Any) { self.dismiss(animated: true) }
// MARK: UIBarPositioningDelegate func position(for bar: UIBarPositioning) -> UIBarPosition { return .topAttached }}
This code sample is an example that illustrates how to use our SDK. Please adapt it to your specific use case.