Customizing Menus

PSPDFKit uses UIMenuController for context sensitive menus when you select text, images, annotations or long press on an empty space. There are several delegates in PSPDFViewControllerDelegate to customize this behavior:

These calls will provide an menuItems array of PSPDFMenuItem objects. These are like UIMenuItem but allow block-based calls and images. You can return both of these objects. You can return nil to block any of these menus for appearing.

This is how the menu for text selection usually looks:

The annotation types offered here for creation are based on the values in editableAnnotationTypes of the currently active PSPDFConfiguration. By default this includes all annotation types with the exception of string links (PSPDFAnnotationStringLink).

To customize this, use the pdfViewController:shouldShowMenuItems:atSuggestedTargetRect:forSelectedText:inRect:onPageView: delegate. You can also disable certain types of menu actions via the allowedMenuActions property:

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/// Menu options when text is selected on this document.
public struct PSPDFTextSelectionMenuAction : OptionSet {
    public init(rawValue: UInt)

    /// Allow search from selected text.
    public static var search: PSPDFTextSelectionMenuAction { get }
    /// Offers to show "Define" on selected text.
    public static var define: PSPDFTextSelectionMenuAction { get }
    /// Offers a toggle for Wikipedia.
    public static var wikipedia: PSPDFTextSelectionMenuAction { get }
    /// Allows text-to-speech.
    public static var speak: PSPDFTextSelectionMenuAction { get }
    /// Allows text-to-speech.
    public static var all: PSPDFTextSelectionMenuAction { get }
}
Copy
1
2
3
4
5
6
7
8
/// Menu options when text is selected on this document.
typedef NS_OPTIONS(NSUInteger, PSPDFTextSelectionMenuAction) {
    PSPDFTextSelectionMenuActionSearch    = 1 << 0, /// Allow search from selected text.
    PSPDFTextSelectionMenuActionDefine    = 1 << 1, /// Offers to show "Define" on selected text.
    PSPDFTextSelectionMenuActionWikipedia = 1 << 2, /// Offers a toggle for Wikipedia.
    PSPDFTextSelectionMenuActionSpeak     = 1 << 3, /// Allows text-to-speech
    PSPDFTextSelectionMenuActionAll       = NSUIntegerMax
};

The menu delegate for annotations is pdfViewController:shouldShowMenuItems:atSuggestedTargetRect:forAnnotations:inRect:onPageView:.If this is called with nil as the annotation argument, the menu to create new annotations will be shown. (in that case annotationRect will also be nil)

Note that if you want to remove certain menu items, as a general rule, you should filter out unwanted menu items as a blacklist - if you try to whitelist menu items, you might break functionality. The idenfifier property of the PSPDFMenuItem object is not loacalized and perfect for comparison:

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public func pdfViewController(_ pdfController: PSPDFViewController, shouldShow menuItems: [PSPDFMenuItem], atSuggestedTargetRect rect: CGRect, forSelectedText selectedText: String, in textRect: CGRect, on pageView: PSPDFPageView) -> [PSPDFMenuItem] {
    // Disable Wikipedia
    // Note that for words that are in the iOS dictionary, instead of Wikipedia we show the "Define" menu item with the native dict.
    // There is also a simpler way to disable wikipedia (See PSPDFTextSelectionMenuAction)
    var newMenuItems = menuItems.filter { $0.identifier != PSPDFTextMenuWikipedia }

    guard let query = selectedText.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
        let queryURL = URL(string: String(format: "https://www.google.com/search?q=%@", arguments: [query])) else {
            return newMenuItems
    }

    // Add option to Google for it.
    let googleItem = PSPDFMenuItem(title: NSLocalizedString("Google", comment: ""), block: {
        // Create browser
        let browser = PSPDFWebViewController(url: queryURL)
        browser.delegate = pdfController
        browser.modalPresentationStyle = .popover
        browser.preferredContentSize = CGSize(width: 600, height: 500)

        let presentationOptions: [String : Any] = [
            PSPDFPresentationRectKey: NSValue(cgRect: textRect),
            PSPDFPresentationInNavigationControllerKey: true,
            PSPDFPresentationCloseButtonKey: true
        ]

        pdfController.present(browser, options: presentationOptions, animated: true, sender: nil, completion: nil)
        }, identifier: "Google")

    newMenuItems.append(googleItem)
    return newMenuItems
}
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
- (NSArray<PSPDFMenuItem *> *)pdfViewController:(PSPDFViewController *)pdfController shouldShowMenuItems:(NSArray<PSPDFMenuItem *> *)menuItems atSuggestedTargetRect:(CGRect)rect forSelectedText:(NSString *)selectedText inRect:(CGRect)textRect onPageView:(PSPDFPageView *)pageView {

    // Disable Wikipedia
    // Be sure to check for PSPDFMenuItem class; there might also be classic UIMenuItems in the array.
    // Note that for words that are in the iOS dictionary, instead of Wikipedia we show the "Define" menu item with the native dict.
    // There is also a simpler way to disable wikipedia (See PSPDFTextSelectionMenuAction)
    NSMutableArray *newMenuItems = [menuItems mutableCopy];
    for (PSPDFMenuItem *menuItem in menuItems) {
        if ([menuItem isKindOfClass:PSPDFMenuItem.class] && [menuItem.identifier isEqualToString:PSPDFTextMenuWikipedia]) {
            [newMenuItems removeObjectIdenticalTo:menuItem];
            break;
        }
    }

    // Add option to Google for it.
    PSPDFMenuItem *googleItem = [[PSPDFMenuItem alloc] initWithTitle:NSLocalizedString(@"Google", nil) block:^{
        NSString *URLString = [NSString stringWithFormat:@"https://www.google.com/search?q=%@", [selectedText stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet]];

        // Create browser
        PSPDFWebViewController *browser = [[PSPDFWebViewController alloc] initWithURL:(NSURL *)[NSURL URLWithString:URLString]];
        browser.delegate = pdfController;
        browser.modalPresentationStyle = UIModalPresentationPopover;
        browser.preferredContentSize = CGSizeMake(600.f, 500.f);

        NSDictionary *presentationOptions = @{
            PSPDFPresentationRectKey: BOXED(textRect),
            PSPDFPresentationInNavigationControllerKey: @YES,
            PSPDFPresentationCloseButtonKey: @YES
        };

        [pdfController presentViewController:browser options:presentationOptions animated:YES sender:nil completion:NULL];
    } identifier:@"Google"];
    [newMenuItems addObject:googleItem];

    return newMenuItems;
}

Creating annotations

When long-tapping on empty space, we will call the forAnnotations: variant without any annotations - this is to show the create annotations menu. There's a convenience property called createAnnotationMenuEnabled to disable this feature in PSPDFConfiguration, and you can also customize what types should be displayed there createAnnotationMenuGroups.

Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let controller = PSPDFViewController(document: document, configuration: PSPDFConfiguration { builder in
    builder.isCreateAnnotationMenuEnabled = false
    builder.createAnnotationMenuGroups = [
        PSPDFAnnotationGroup(items: [
            PSPDFAnnotationGroupItem(type: .freeText),
            PSPDFAnnotationGroupItem(type: .signature),
            PSPDFAnnotationGroupItem(type: .note)
        ]),
        PSPDFAnnotationGroup(items: [
            PSPDFAnnotationGroupItem(type: .ink, variant: .inkVariantPen, configurationBlock: PSPDFAnnotationGroupItem.inkConfigurationBlock())
        ]),
        PSPDFAnnotationGroup(items: [
            PSPDFAnnotationGroupItem(type: .ink, variant: .inkVariantHighlighter, configurationBlock: PSPDFAnnotationGroupItem.inkConfigurationBlock())
        ]),
        PSPDFAnnotationGroup(items: [
            PSPDFAnnotationGroupItem(type: .image),
            PSPDFAnnotationGroupItem(type: .stamp),
            PSPDFAnnotationGroupItem(type: .sound)
        ]),
        PSPDFAnnotationGroup(items: [
            PSPDFAnnotationGroupItem(type: .eraser)
        ])
    ]
})
Copy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
PSPDFViewController *controller = [[PSPDFViewController alloc] initWithDocument:document configuration:[PSPDFConfiguration configurationWithBuilder:^(PSPDFConfigurationBuilder *builder) {
    builder.createAnnotationMenuEnabled = NO;
    builder.createAnnotationMenuGroups = @[
      [PSPDFAnnotationGroup groupWithItems:@[
        [PSPDFAnnotationGroupItem itemWithType:PSPDFAnnotationStringFreeText],
        [PSPDFAnnotationGroupItem itemWithType:PSPDFAnnotationStringSignature],
        [PSPDFAnnotationGroupItem itemWithType:PSPDFAnnotationStringNote]
      ]],
      [PSPDFAnnotationGroup groupWithItems:@[
        [PSPDFAnnotationGroupItem itemWithType:PSPDFAnnotationStringInk
          variant:PSPDFAnnotationStringInkVariantPen
          configurationBlock:[PSPDFAnnotationGroupItem inkConfigurationBlock]]
      ]],
      [PSPDFAnnotationGroup groupWithItems:@[
        [PSPDFAnnotationGroupItem itemWithType:PSPDFAnnotationStringInk
          variant:PSPDFAnnotationStringInkVariantHighlighter
          configurationBlock:[PSPDFAnnotationGroupItem inkConfigurationBlock]]
      ]],
      [PSPDFAnnotationGroup groupWithItems:@[
        [PSPDFAnnotationGroupItem itemWithType:PSPDFAnnotationStringImage],
        [PSPDFAnnotationGroupItem itemWithType:PSPDFAnnotationStringStamp],
        [PSPDFAnnotationGroupItem itemWithType:PSPDFAnnotationStringSound]
      ]],
      [PSPDFAnnotationGroup groupWithItems:@[
        [PSPDFAnnotationGroupItem itemWithType:PSPDFAnnotationStringEraser]
      ]]
    ];
}]];