Nutrient 12 migration guide
This guide covers updating an iOS or Mac Catalyst project from Nutrient iOS SDK 11.5 to Nutrient iOS SDK 12. We encourage you to update as soon as possible, in order to take advantage of future new features and fixes.
Nutrient iOS SDK 12 fully supports iOS 14, 15, and 16. Xcode 14 or later is required to use this version of the SDK. Learn more in our version support guide.
To determine if you need to take action, check the list of the deprecated APIs. If you use a deprecated API in your project, take appropriate action.
New menu system
Nutrient iOS SDK 12 introduces a new, modern menu system for annotation creation and annotation selection menus. It uses the UIMenu
-based API to display either a horizontal bar or a context menu, depending on platform and input type.
Unlike the now-deprecated UIMenuItem
-based API, the modern menu system relies on declaring the entire menu tree upfront, and it takes care of displaying proper submenus when needed. Nutrient iOS SDK 12 adds a new suite of customizations to take advantage of this approach.
The modern menu system is only available for annotation creation and annotation selection menus. Text and image selection menus will adopt the modern menu system in a future version of Nutrient iOS SDK.
Customizing the menus directly
PDFViewController
now offers two new delegate methods for customizing the annotation creation and annotation selection menus directly:
-
pdfViewController(_:
menuForCreatingAnnotationAt: onPageView: appearance: suggestedMenu:) -
pdfViewController(_:
menuForAnnotations: onPageView: appearance: suggestedMenu:)
The example below appends a custom action to the annotation selection menu:
func pdfViewController(_ sender: PDFViewController, menuForAnnotations annotations: [Annotation], onPageView pageView: PDFPageView, appearance: EditMenuAppearance, suggestedMenu: UIMenu) -> UIMenu { let customAction = UIAction(title: "Custom") { _ in print("Hello from custom action!") } return suggestedMenu.replacingChildren(suggestedMenu.children + [customAction]) }
You can use UIAction
to insert closure-based actions, UICommand
to insert responder chain actions, and UIMenu
to insert submenus. UIDeferredMenuElement
is also supported.
As a result, the following delegate methods and subclassing hooks that were used to customize the annotation creation and annotation selection menus have been deprecated and will be removed in a future version of Nutrient iOS SDK:
-
PDFViewControllerDelegate.pdfViewController(_:
shouldShow: atSuggestedTargetRect: for: in: on:) -
PDFPageView.menuItemsForNewAnnotation(at:)
-
PDFPageView.menuItems(for:)
-
PDFPageView.shouldMoveStyleMenuEntriesIntoSubmenu
Customizing choices in the style menu
The new annotationMenuConfiguration
property allows you to customize the available choices for some of the properties in the Style menu for selected annotations by setting a custom closure for one of the following properties:
The example below customizes the available line width choices for ink annotations on the first page, but it leaves the default choices for all other annotations on all other pages:
let configuration = PDFConfiguration { $0.annotationMenuConfiguration = AnnotationMenuConfiguration { $0.lineWidthChoices = { annotation, pageView, defaultChoices in if annotation is InkAnnotation, pageView.pageIndex == 0 { return [2, 5, 10, 20] } else { return defaultChoices } } } }
The following PDFPageView
subclassing hooks that were used to customize these choices have been deprecated and will be removed in a future version of Nutrient iOS SDK:
-
PDFPageView.colorMenuItems(for:)
-
PDFPageView.fillColorMenuItems(for:)
-
PDFPageView.opacityMenuItem(for:
with:) -
PDFPageView.defaultColorOptions(for:)
-
PDFPageView.availableFontSizes
-
PDFPageView.availableLineWidths
Backward compatibility considerations
The new UIMenu
-based delegate methods are incompatible with the deprecated UIMenuItem
-based delegate methods and PDFPageView
subclassing hooks, and they must not be mixed.
Because the new customizations mark a significant departure from the now-deprecated UIMenuItem
-based API, Nutrient iOS SDK 12 will ease the transition by choosing to stick with the legacy menu system under certain conditions.
I’m not interested in customizing menus.
If you’re not interested in customizing menus and you don’t use any of the new or deprecated customizations, the modern menu system will be used by default.
I want to customize menus and I want to adopt the modern menu system.
If you implement either of the new UIMenu
-based delegate methods to customize the annotation creation or annotation selection menu, this will be treated as an explicit opt in to the modern menu system for that specific menu, even if you still have one of the deprecated delegate methods or subclassing hooks in your code.
I’ve been customizing menus but I’m not ready to implement the new UIMenu-based API yet.
If you don’t implement any of the new UIMenu
-based delegate methods but you do have one of the deprecated delegate methods or subclassing hooks in your code, Nutrient will stick with the legacy menu system and respect the deprecated customizations.
If this is the case, Nutrient will help you identify which exact deprecated customization caused it to opt out of the modern menu system by logging a warning similar to the following:
Presenting the legacy menu for configuration (...) because the following deprecated customizations are implemented: 'MyDelegate.pdfViewController(_:shouldShow:atSuggestedTargetRect:for:in:on:)', 'MyPageView.defaultColorOptions(for:)'. Remove them or implement 'MyDelegate.pdfViewController(_:menuForAnnotations:onPageView:appearance:suggestedMenu:)' to explicitly opt into the modern menu system.
This is a temporary mechanism designed to ease the transition to the modern menu system. The legacy menu system is limited and may exhibit problems. The deprecated APIs will eventually be removed, and the legacy menu system will cease to exist. We strongly encourage you to adopt the modern menu system as soon as possible. If you need help, don’t hesitate to reach out to us on support.
Use cases
This section explores the most common use cases when customizing the annotation creation and annotation selection menus, and it describes how to achieve the same results using the new UIMenu
-based API.
Presenting the annotation creation menu
Use the tryToShowAnnotationMenu(at:
method to programmatically present the menu that normally appears when you long press on space without selectable text or annotations:
// Before this update. pageView.showAnnotationMenu(at: point, animated: true)
// After this update.
viewController.interactions.tryToShowAnnotationMenu(at: point, in: coordinateSpace)
Presenting the menu for selected annotations
Use the new select(annotations:
method to select annotations and set true
for the presentMenu
parameter. It’s no longer possible to present the menu for annotations without selecting them at the same time:
// Before this update. let targetRect = pageView.convert(annotation.boundingBox, from: pageView.pdfCoordinateSpace) pageView.showMenu(for: [annotation], targetRect: targetRect, option: .menuOnly, animated: true)
// After this update. pageView.select(annotations: [annotation], presentMenu: true, animated: true)
Customizing the annotation creation menu
Use the new pdfViewController(_:
delegate method to customize the menu that appears when you long press on space without selectable text or annotations:
// Before this update. func pdfViewController(_ pdfController: PDFViewController, shouldShow menuItems: [MenuItem], atSuggestedTargetRect rect: CGRect, for annotations: [Annotation]?, in annotationRect: CGRect, on pageView: PDFPageView) -> [MenuItem] { if annotations == nil { // Return the customized `menuItems`. } else { return menuItems } }
// After this update. func pdfViewController(_ sender: PDFViewController, menuForCreatingAnnotationAt point: CGPoint, onPageView pageView: PDFPageView, appearance: EditMenuAppearance, suggestedMenu: UIMenu) -> UIMenu { // Return the customized `suggestedMenu`. }
To disable the annotation creation menu entirely, either return a UIMenu
with no children from the above delegate method, or set the isCreateAnnotationMenuEnabled
configuration property to false
.
Customizing the menu for selected annotations
Use the new pdfViewController(_:
delegate method to customize the menu that appears when you select one or more annotations:
// Before this update. func pdfViewController(_ pdfController: PDFViewController, shouldShow menuItems: [MenuItem], atSuggestedTargetRect rect: CGRect, for annotations: [Annotation]?, in annotationRect: CGRect, on pageView: PDFPageView) -> [MenuItem] { if let annotations, !annotations.isEmpty { // Return the customized `menuItems`. } else { return menuItems } }
// After this update. func pdfViewController(_ sender: PDFViewController, menuForAnnotations annotations: [Annotation], onPageView pageView: PDFPageView, appearance: EditMenuAppearance, suggestedMenu: UIMenu) -> UIMenu { // Return the customized `suggestedMenu`. }
To disable the annotation selection menu entirely, return a UIMenu
with no children from the above delegate method.
Filtering suggested menu elements
Use one of the new UIMenu
-based delegate methods and modify the suggestedMenu
parameter to exclude certain actions or submenus. Keep in mind that you need to search the entire menu tree, and not just the children of the root menu:
// Before this update. func pdfViewController(_ pdfController: PDFViewController, shouldShow menuItems: [MenuItem], atSuggestedTargetRect rect: CGRect, for annotations: [Annotation]?, in annotationRect: CGRect, on pageView: PDFPageView) -> [MenuItem] { menuItems.filter { $0.identifier == TextMenu.annotationMenuCopy.rawValue || $0.identifier == TextMenu.annotationMenuRemove.rawValue } }
// After this update. func pdfViewController(_ sender: PDFViewController, menuForAnnotations annotations: [Annotation], onPageView pageView: PDFPageView, appearance: EditMenuAppearance, suggestedMenu: UIMenu) -> UIMenu { suggestedMenu.filterActions(anyOf: [.PSPDFKit.copy, .PSPDFKit.delete]) }
In the above example, the
filterActions(anyOf:)
function isn’t part of the public API, but you can view its source in our Catalog example project.
Inserting custom menu elements
Use one of the new UIMenu
-based delegate methods and modify the suggestedMenu
parameter to insert a menu element at any index:
// Before this update. func pdfViewController(_ pdfController: PDFViewController, shouldShow menuItems: [MenuItem], atSuggestedTargetRect rect: CGRect, for annotations: [Annotation]?, in annotationRect: CGRect, on pageView: PDFPageView) -> [MenuItem] { let customMenuItem = MenuItem(title: "Custom") { print("Hello from custom menu item!") } return [customMenuItem] + menuItems }
// After this update. func pdfViewController(_ sender: PDFViewController, menuForAnnotations annotations: [Annotation], onPageView pageView: PDFPageView, appearance: EditMenuAppearance, suggestedMenu: UIMenu) -> UIMenu { let customAction = UIAction(title: "Custom") { _ in print("Hello from custom action!") } return suggestedMenu.replacingChildren([customAction] + suggestedMenu.children) }
Presenting submenus
Use one of the new UIMenu
-based delegate methods and modify the suggestedMenu
parameter to include a UIMenu
among the children. Nutrient will take care of the rest; there’s no longer a need to manually present submenus:
// Before this update. func pdfViewController(_ pdfController: PDFViewController, shouldShow menuItems: [MenuItem], atSuggestedTargetRect rect: CGRect, for annotations: [Annotation]?, in annotationRect: CGRect, on pageView: PDFPageView) -> [MenuItem] { let nestedMenuItem = MenuItem(title: "Nested") { print("Hello from nested menu item!") } let submenuMenuItem = MenuItem(title: "Submenu") { UIMenuController.shared.menuItems = [nestedMenuItem] UIMenuController.shared.showMenu(from: pageView, rect: rect) } return [submenuMenuItem] + menuItems }
// After this update. func pdfViewController(_ sender: PDFViewController, menuForAnnotations annotations: [Annotation], onPageView pageView: PDFPageView, appearance: EditMenuAppearance, suggestedMenu: UIMenu) -> UIMenu { let nestedAction = UIAction(title: "Nested") { _ in print("Hello from nested action!") } let submenu = UIMenu(title: "Submenu", children: [ nestedAction ]) return suggestedMenu.replacingChildren([submenu] + suggestedMenu.children) }
Displaying menu elements as images
Menu elements will be displayed as images if their titles are empty. This works with all supported menu elements — UIAction
, UICommand
, and UIMenu
:
// Before this update. func pdfViewController(_ pdfController: PDFViewController, shouldShow menuItems: [MenuItem], atSuggestedTargetRect rect: CGRect, for annotations: [Annotation]?, in annotationRect: CGRect, on pageView: PDFPageView) -> [MenuItem] { guard let annotations, !annotations.isEmpty else { return menuItems } let lockMenuItem = MenuItem(title: "Lock", image: UIImage(systemName: "lock")) { annotations.forEach { $0.flags.insert(.locked) } } return [lockMenuItem] + menuItems }
// After this update. func pdfViewController(_ sender: PDFViewController, menuForAnnotations annotations: [Annotation], onPageView pageView: PDFPageView, appearance: EditMenuAppearance, suggestedMenu: UIMenu) -> UIMenu { let lockImage = UIImage(systemName: "lock")! lockImage.accessibilityLabel = "Lock" let lockAction = UIAction(title: appearance == .contextMenu ? "Lock" : "", image: lockActionImage) { _ in annotations.forEach { $0.flags.insert(.locked) } } return [lockAction] + menuItems }
The above example demonstrates how you can combine various conditions to decide whether a menu element should be displayed as an image or not. We recommend always including the title when the menu appears as a .contextMenu
.
Always set the
accessibilityLabel
for menu element images that don’t have titles. This will ensure they’re properly discoverable with VoiceOver.
Further reading
For more information about the modern menu system, check out our customizing menus on iOS guide and the API documentation for PDFViewControllerDelegate
, PDFPageView
, and AnnotationMenuConfiguration
.
Deprecated APIs
This is the list of all symbols that were deprecated in Nutrient iOS SDK 12. If you use, implement, or override any of the following, take appropriate action.
Miscellaneous
-
UnitTo.point
Use a different unit for real-world distances instead.
Legacy menu system
-
PDFPageView.availableFontSizes
See the New Menu System section for more information. -
PDFPageView.availableLineWidths
See the New Menu System section for more information. -
PDFPageView.colorMenuItems(for:)
See the New Menu System section for more information. -
PDFPageView.defaultColorOptions(for:)
See the New Menu System section for more information. -
PDFPageView.fillColorMenuItems(for:)
See the New Menu System section for more information. -
PDFPageView.menuItems(for:)
See the New Menu System section for more information. -
PDFPageView.menuItemsForNewAnnotation(at:)
See the New Menu System section for more information. -
PDFPageView.opacityMenuItem(for:
with:)
See the New Menu System section for more information. -
PDFPageView.passthroughViewsForPopoverController
Use the.popoverPassthroughViews
presentation option instead. -
PDFPageView.select(_:
animated:)
Useselect(annotations:
orpresentMenu: animated:) focus(formElement:
instead.toggleValue: animated:) -
PDFPageView.selectColor(for:
isFillColor:)
UsepresentColorPicker(for:
instead.property: options: animated: completion:) -
PDFPageView.showAnnotationMenu(at:
animated:)
UsetryToShowAnnotationMenu(at:
instead.in:) -
PDFPageView.showColorPicker(for:
animated:)
UsepresentColorPicker(for:
instead.property: options: animated: completion:) -
PDFPageView.showInspector(for:
options: animated:)
UsepresentInspector(for:
instead.options: animated: completion) -
PDFPageView.showFontPicker(for:
animated:)
UsepresentFontPicker(for:
instead.options: animated: completion:) -
PDFPageView.showLinkPreviewActionSheet(for:
from: animated:)
UsepresentLinkActionSheet(for:
instead.options: animated: completion:) -
PDFPageView.showMenu(for:
targetRect: option: animated:)
Useselect(annotations:
instead.presentMenu: animated:) -
PDFPageView.showNoteController(for:
animated:)
UsepresentComments(for:
instead.options: animated: completion) -
PDFPageView.shouldMoveStyleMenuEntriesIntoSubmenu
See the New Menu System section for more information. -
PDFPageView.useAnnotationInspector(for:)
UsecanPresentInspector(for:)
instead. -
PDFViewControllerDelegate.pdfViewController(_:
shouldShow: atSuggestedTargetRect: for: in: on:)
See the New Menu System section for more information.
Legacy menu item identifiers
-
TextMenu.annotationMenuAlignment
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuAlignmentCenter
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuAlignmentLeft
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuAlignmentRight
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuBlendMode
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuCancel
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuColor
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuCopy
Use the.PSPDFKit.copy
action identifier in the modern menu system. -
TextMenu.annotationMenuCustomColor
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuEdit
Use the.PSPDFKit.editFreeText
or.PSPDFKit.editSound
action identifiers in the modern menu system. -
TextMenu.annotationMenuFillColor
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuFinishRecording
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuFitToText
Use the.PSPDFKit.sizeToFit
action identifier in the modern menu system. -
TextMenu.annotationMenuFont
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuGroup
Use the.PSPDFKit.group
action identifier in the modern menu system. -
TextMenu.annotationMenuHighlightType
Use the.PSPDFKit.type
menu identifier in the modern menu system. -
TextMenu.annotationMenuInspector
Use the.PSPDFKit.inspector
action identifier in the modern menu system. -
TextMenu.annotationMenuLineEnd
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuLineStart
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuLineTypeButt
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuLineTypeCircle
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuLineTypeClosedArrow
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuLineTypeDiamond
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuLineTypeNone
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuLineTypeOpenArrow
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuLineTypeReverseClosedArrow
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuLineTypeReverseOpenArrow
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuLineTypeSlash
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuLineTypeSquare
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuMerge
Use the.PSPDFKit.merge
action identifier in the modern menu system. -
TextMenu.annotationMenuNote
Use the.PSPDFKit.comments
action identifier in the modern menu system. -
TextMenu.annotationMenuOpacity
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuPaste
Use the.PSPDFKit.paste
action identifier in the modern menu system. -
TextMenu.annotationMenuPause
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuPauseRecording
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuPlay
Use the.PSPDFKit.play
action identifier in the modern menu system. -
TextMenu.annotationMenuPreviewFile
Use the.PSPDFKit.previewFile
action identifier in the modern menu system. -
TextMenu.annotationMenuRemove
Use the.PSPDFKit.delete
action identifier in the modern menu system. -
TextMenu.annotationMenuSave
Use the.PSPDFKit.store
action identifier in the modern menu system. -
TextMenu.annotationMenuSize
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuStyle
Use the.PSPDFKit.style
menu identifier in the modern menu system. -
TextMenu.annotationMenuThickness
This value is no longer used in the modern menu system. -
TextMenu.annotationMenuUngroup
Use the.PSPDFKit.ungroup
action identifier in the modern menu system.