PSPDFKit 12.3 Migration Guide

This guide covers updating an iOS or Mac Catalyst project from PSPDFKit 12.2 for iOS to PSPDFKit 12.3 for iOS. We encourage you to update to take advantage of future new features and fixes.

Information

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 for Selected Text and Images

PSPDFKit 12.3 for iOS adopts the modern menu system for selected text and images. The modern menu system, previously introduced in PSPDFKit 12 for iOS for annotation creation and selection menus, uses the UIMenu-based API to display either a horizontal bar or a context menu, depending on platform and input type.

The entire UIMenuItem-based legacy menu system is now deprecated, and the related APIs will be removed in a future version of PSPDFKit for iOS.

Customizing the Menus Directly

PDFViewController now offers two new delegate methods for customizing the text and image selection menus directly:

The example below appends a custom action to the menu for selected text:

func pdfViewController(_ sender: PDFViewController, menuForText glyphs: GlyphSequence, 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])
}

Use UIAction to insert closure-based actions, UICommand to insert responder chain actions, and UIMenu to insert submenus. On iOS 16 and later, UIDeferredMenuElement is also supported.

As a result, the following configuration property, delegate methods, and subclassing hooks that were used to customize the text and image selection menus have been deprecated and will be removed in a future version of PSPDFKit for iOS:

  • PDFConfiguration.allowedMenuActions

  • PDFViewControllerDelegate.pdfViewController(_:shouldShow:atSuggestedTargetRect:forSelectedText:in:on:)

  • PDFViewControllerDelegate.pdfViewController(_:shouldShow:atSuggestedTargetRect:forSelectedImage:in:on:)

  • PDFPageView.textSelectionMenuItemForCreatingAnnotation(withType:)

  • PDFPageView.textSelectionView(_:updateMenuAnimated:)

Presenting Menus for Selected Text or Images

PSPDFKit 12.3 introduces two new methods to PDFPageView. These methods are used for selecting text or images and presenting menus at the same time:

As a result, the following methods that were used to present text and image selection menus have been deprecated and will be removed in a future version of PSPDFKit for iOS:

  • PDFViewController.showMenuIfSelected(with:animated:)

  • PDFPageView.showMenuIfSelected(with:animated:)

  • PDFPageView.showMenu(for:animated:)

  • PDFPageView.showMenuIfSelected(animated:)

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, PSPDFKit 12.3 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 text or image 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, PSPDFKit will stick with the legacy menu system and respect the deprecated customizations.

If this is the case, PSPDFKit 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:forSelectedText:in:on:)', 'MyPageView.textSelectionMenuItemForCreatingAnnotation(withType:)'. Remove them or implement 'MyDelegate.pdfViewController(_:menuForText:onPageView:appearance:suggestedMenu:)' to explicitly opt into the modern menu system.
Warning

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, reach out to us on support.

Use Cases

This section explores the most common use cases when customizing the text and image selection menus, and it describes how to achieve the same results using the new UIMenu-based API.

Presenting Menu for Selected Text or Images

Use the new select(glyphs:presentMenu:animated:) method to select text and set true for the presentMenu parameter. It’s no longer possible to present the menu for text without selecting it at the same time:

// Before this update.
pageView.selectionView.selectedGlyphs = glyphs
pageView.selectionView.updateMenu(animated: true)
// After this update.
pageView.select(glyphs: glyphs, presentMenu: true, animated: true)

Presenting the Menu for Selected Images

Use the new select(image:presentMenu:animated:) method to select an image and set true for the presentMenu parameter. It’s no longer possible to present the menu for an image without selecting it at the same time:

// Before this update.
pageView.selectionView.selectedImage = image
pageView.selectionView.updateMenu(animated: true)
// After this update.
pageView.select(image: image, presentMenu: true, animated: true)

Customizing the Menu for Selected Text

Use the new pdfViewController(_:menuForText:onPageView:appearance:suggestedMenu:) delegate method to customize the menu that appears when you select text:

// Before this update.
func pdfViewController(_ pdfController: PDFViewController, shouldShow menuItems: [MenuItem], atSuggestedTargetRect rect: CGRect, forSelectedText selectedText: String, in textRect: CGRect, on pageView: PDFPageView) -> [MenuItem] {
    // Return the customized `menuItems`.
}
// After this update.
func pdfViewController(_ sender: PDFViewController, menuForText glyphs: GlyphSequence, onPageView pageView: PDFPageView, appearance: EditMenuAppearance, suggestedMenu: UIMenu) -> UIMenu {
    // Return the customized `suggestedMenu`.
}

To disable the text selection menu, return a UIMenu with no children from the delegate method above. To disable selecting text entirely, set the isTextSelectionEnabled configuration property to false. Note that disabling selecting text will disable selecting images as well.

Customizing the Menu for Selected Images

Use the new pdfViewController(_:menuForImage:onPageView:appearance:suggestedMenu:) delegate method to customize the menu that appears when you select text:

// Before this update.
  func pdfViewController(_ pdfController: PDFViewController, shouldShow menuItems: [MenuItem], atSuggestedTargetRect rect: CGRect, forSelectedImage selectedImage: ImageInfo, in imageRect: CGRect, on pageView: PDFPageView) -> [MenuItem] {
    // Return the customized `menuItems`.
}
// After this update.
func pdfViewController(_ sender: PDFViewController, menuForImage image: ImageInfo, onPageView pageView: PDFPageView, appearance: EditMenuAppearance, suggestedMenu: UIMenu) -> UIMenu {
    // Return the customized `suggestedMenu`.
}

To disable the image selection menu, return a UIMenu with no children from the above delegate method. To disable selecting images entirely, set the isImageSelectionEnabled configuration property to false. Note that selecting images will be disabled by default if selecting text is also disabled.

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, forSelectedText selectedText: String, in textRect: CGRect, on pageView: PDFPageView) -> [MenuItem] {
    menuItems.filter {
        $0.identifier == TextMenu.annotationMenuCopy.rawValue || $0.identifier == TextMenu.annotationMenuShare.rawValue
    }
}
// After this update.
func pdfViewController(_ sender: PDFViewController, menuForText glyphs: GlyphSequence, onPageView pageView: PDFPageView, appearance: EditMenuAppearance, suggestedMenu: UIMenu) -> UIMenu {
    suggestedMenu.reject(actions: [.PSPDFKit.copy, .PSPDFKit.share])
}
Information

In the example above, the reject(actions:) 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, forSelectedText selectedText: String, in textRect: 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, menuForText glyphs: GlyphSequence, 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)
}

Always Show the Speak Text Menu Action

Before this update, we used to always show the Speak action, which speaks selected text out loud, in the text selection menu. Starting with PSPDFKit 12.3 for iOS, this menu action is now only shown if the Speak Selection accessibility option is enabled in the Settings app.

To restore the behavior of the previous version and always show the Speak and Stop actions for reading content out loud, you can customize the menu in the following way:

// After this update.
func pdfViewController(_ sender: PDFViewController, menuForText glyphs: GlyphSequence, onPageView pageView: PDFPageView, appearance: EditMenuAppearance, suggestedMenu: UIMenu) -> UIMenu {
    let newAccessibilityMenu = UIMenu(identifier: .PSPDFKit.accessibility, options: [.displayInline], children: [stopAction() ?? speakAction(for: glyphs, pageView: pageView)])
    return suggestedMenu.replacingChildren(suggestedMenu.children.compactMap { element in
        if let menu = element as? UIMenu, menu.identifier == .PSPDFKit.accessibility {
            return newAccessibilityMenu
        } else {
            return element
        }
    })
}

func stopAction() -> UIAction? {
    let speechSynthesizer = SDK.shared.speechController.speechSynthesizer
    guard speechSynthesizer.isSpeaking else {
        return nil
    }
    return UIAction(title: "Stop", image: UIImage(systemName: "stop"), attributes: [.keepsMenuPresented]) { _ in
        speechSynthesizer.stopSpeaking(at: .immediate)
    }
}

func speakAction(for glyphs: GlyphSequence, pageView: PDFPageView) -> UIAction {
    return UIAction(title: "Speak", image: UIImage(systemName: "play"), attributes: [.keepsMenuPresented]) { _ in
        SDK.shared.speechController.speakText(glyphs.trimmedText, delegate: pageView.selectionView)
    }
}

Further Reading

For more information about the modern menu system, check out our Customizing Menus on iOS guide and the API documentation for PDFViewControllerDelegate, GlyphSequence, PDFPageView, and ContentMenuConfiguration.

Check out the PSPDFKit 12 migration guide as well if you’re upgrading from a version older than that. It describes how to adopt the modern menu system for annotation creation and selection menus.

Deprecated APIs

This is the list of all symbols that were deprecated in PSPDFKit 12.3 for iOS. If you use, implement, or override any of the following, take appropriate action.

Miscellaneous

Legacy Menu System

Legacy Menu Item Identifiers

  • TextMenu
    Use UIAction.Identifier or UIMenu.Identifier in the modern menu system.

  • TextMenu.copy
    Use the .PSPDFKit.copy action identifier in the modern menu system.

  • TextMenu.createLink
    This value is no longer used in the modern menu system.

  • TextMenu.define
    Use the .PSPDFKit.define action identifier in the modern menu system.

  • TextMenu.pause
    Use the .PSPDFKit.accessibility menu identifier in the modern menu system.

  • TextMenu.saveAs
    Use the .PSPDFKit.saveAs action identifier in the modern menu system.

  • TextMenu.share
    Use the .PSPDFKit.share action identifier in the modern menu system.

  • TextMenu.speak
    Use the .PSPDFKit.accessibility menu identifier in the modern menu system.

  • TextMenu.wikipedia
    This value is no longer used in the modern menu system.

  • TextMenu.annotationMenuHighlight
    This value is no longer used in the modern menu system.

  • TextMenu.annotationMenuRedaction
    This value is no longer used in the modern menu system.

  • TextMenu.annotationMenuSquiggle
    This value is no longer used in the modern menu system.

  • TextMenu.annotationMenuStrikeout
    This value is no longer used in the modern menu system.

  • TextMenu.annotationMenuUnderline
    This value is no longer used in the modern menu system.