Add a Custom Free Text Input Accessory in Swift for iOS

This example shows how you can add a custom Free Text inputAccessory view on top of the keyboard on iOS.


//
// Copyright © 2019-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 PSPDFKit
import PSPDFKitUI
/// Demonstrates how to configure `PDFViewController` to use a custom `inputAccessoryView` when
/// creating or editing `FreeTextAnnotation`. In addition, it draws attention to the key sources
/// of mistakes that would lead to visual glitches or no visible `inputAccessoryView` at all.
class CustomFreeTextInputAccessoryExample: Example {
override init() {
super.init()
title = "Custom Free Text inputAccessory"
category = .annotations
}
/// A FreeTextAnnotationView that installs a custom `inputAccessoryView` for editing the text of
/// its annotation.
///
/// The custom view is useless but very visible.
class UselessInputAccessoryFreeTextAnnotationView: FreeTextAnnotationView {
override func textViewForEditing() -> UITextView {
let textView = super.textViewForEditing()
// UIKit will only adjust the size if we help it a bit
let uselessAccessory = UIView(frame: CGRect(origin: .zero, size: CGSize(width: 0, height: 42)))
uselessAccessory.autoresizingMask = .flexibleWidth
// For an accessory view that uses autolayout, like UIToolbar, the above would be:
// let uselessAccessory = UIToolbar()
// uselessAccessory.translatesAutoresizingMaskIntoConstraints = false
// shine bright like a diamond…
uselessAccessory.backgroundColor = .cyan
// install the accessory view
#if !os(visionOS)
textView.inputAccessoryView = uselessAccessory
#endif
return textView
}
}
override func invoke(with delegate: ExampleRunnerDelegate) -> UIViewController? {
let document = AssetLoader.document(for: .annualReport)
return PDFViewController(document: document) {
// register our custom class
$0.overrideClass(FreeTextAnnotationView.self, with: UselessInputAccessoryFreeTextAnnotationView.self)
/*
If we do not set this to false, Nutrient’s default accessory view will be installed in
a way that makes it impossible to replace without visual glitches.
*/
$0.freeTextAccessoryViewEnabled = false
}
}
}

This code sample is an example that illustrates how to use our SDK. Please adapt it to your specific use case.