> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/ios/annotation/customization.md).

# Customize annotation interactions in iOS viewer

Discover how to implement annotation and PDF interaction with the open-source Tools.framework. Learn about annotation creation, text selection, form filling, and link following capabilities. Customize

Annotation and PDF interaction is implemented within the open-source [`Tools.framework`](/ios/annotation/overview.md). This includes annotation creation & selection, text selection, form filling and link following.

The behavior is implemented via a collection of "Tools": classes that derive from the abstract base class [`PTTool`](https://sdk.apryse.com/api/ios/Classes/PTTool.html) and that are coordinated by a [`PTToolManager`](https://sdk.apryse.com/api/ios/Classes/PTToolManager.html). The `PTToolManager` provides the ability to customize tool behavior, and enable/disable the ability for users to create or edit annotations (both in general and on an annotation type by annotation type basis).

## Create and edit annotations

To disable (or re-enable) the ability to create or edit annotations, options can be altered as shown below. When disabled, the relevant annotation tool is removed from the [annotation toolbar](/ios/annotation/annotation-toolbar-i.md), and from the [`UIMenuController`](https://developer.apple.com/uikit/uimenucontroller/).

{% tabs %}
{% tab title="Swift" %}
{% code lineNumbers="true" %}

```swift
// disables all text annotation types (highlights, underlines, etc.)
toolManager.highlightAnnotationOptions.canCreate = false
toolManager.highlightAnnotationOptions.canEdit = false
toolManager.underlineAnnotationOptions.canCreate = false
toolManager.underlineAnnotationOptions.canEdit = false
toolManager.strikeOutAnnotationOptions.canCreate = false
toolManager.strikeOutAnnotationOptions.canEdit = false
toolManager.squigglyAnnotationOptions.canCreate = false
toolManager.squigglyAnnotationOptions.canEdit = false
```

{% endcode %}
{% endtab %}

{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
// disables all text annotation types (highlights, underlines, etc.)
toolManager.highlightAnnotationOptions.canCreate = NO;
toolManager.highlightAnnotationOptions.canEdit = NO;
toolManager.underlineAnnotationOptions.canCreate = NO;
toolManager.underlineAnnotationOptions.canEdit = NO;
toolManager.strikeOutAnnotationOptions.canCreate = NO;
toolManager.strikeOutAnnotationOptions.canEdit = NO;
toolManager.squigglyAnnotationOptions.canCreate = NO;
toolManager.squigglyAnnotationOptions.canEdit = NO;
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Annotation interaction and UIMenuController

Further control over annotation interaction behaviour via the [`PTToolManagerDelegate`](https://sdk.apryse.com/api/ios/Protocols/PTToolManagerDelegate.html) protocol.

There are several methods that allow you to be notified of, and if desired modify or prevent, default behavior from occurring. For all methods, if `false` is returned, then the default behavior will not occur.

1. [`-toolManager:shouldSelectAnnotation:onPageNumber:`](https://sdk.apryse.com/api/ios/Protocols/PTToolManagerDelegate.html#/c:objc\(pl\)PTToolManagerDelegate\(im\)toolManager:shouldSelectAnnotation:onPageNumber:)This method is called just before an annotation is selected. The [annotation object](https://sdk.apryse.com/api/ios/Classes/PTAnnot.html) itself is available for the decision making process, and for further action if needed.
2. [`-toolManager:shouldShowMenu:forAnnotation:onPageNumber:`](https://sdk.apryse.com/api/ios/Protocols/PTToolManagerDelegate.html#/c:objc\(pl\)PTToolManagerDelegate\(im\)toolManager:shouldSelectAnnotation:onPageNumber:)This method is called just before the selection popup menu is shown. The [`UIMenuController`](https://developer.apple.com/uikit/uimenucontroller/) object is passed so that menu items can be added or removed as required.
3. [`-toolManager:shouldHandleLinkAnnotation:orLinkInfo:onPageNumber:`](https://sdk.apryse.com/api/ios/Protocols/PTToolManagerDelegate.html#/c:objc\(pl\)PTToolManagerDelegate\(im\)toolManager:shouldHandleLinkAnnotation:orLinkInfo:onPageNumber:)This method is called just before a link is followed. The link is provided so that alternate action can be taken if required.
4. [`-toolManager:shouldSwitchToTool:`](https://sdk.apryse.com/api/ios/Protocols/PTToolManagerDelegate.html#/c:objc\(pl\)PTToolManagerDelegate\(im\)toolManager:shouldSwitchToTool:)This method is called just before a tool is activated. The [tool](https://sdk.apryse.com/api/ios/Classes/PTTool.html) is available for the decision making process.

### Example

The sample code uses `-toolManager:shouldShowMenu:forAnnotation:onPageNumber:` to restrict use of the [`UIMenuController`](https://developer.apple.com/uikit/uimenucontroller/) to copying and defining text found in the PDF. This is done by stopping the UIMenuController from popping up in any case other than selecting text, and by removing options to highlight/underline etc. from the selected text UIMenuController popup.

{% tabs %}
{% tab title="Swift" %}
{% code lineNumbers="true" %}

```swift
// PTToolManagerDelegate method implementation.
func toolManager(_ toolManager: PTToolManager, shouldShowMenu menuController: UIMenuController, forAnnotation annotation: PTAnnot, onPageNumber pageNumber: UInt) -> Bool {
    // Only show quick menu for the text select tool.
    if type(of: toolManager.tool) === TextSelectTool.self {
        // Remove the annotation creation menu items.
        menuController.menuItems = removeAnnotationItems(menuController.menuItems!)
  
        return true;
    } else {
        return false;
    }
}

func removeAnnotationItems(_ items: [UIMenuItem]) -> [UIMenuItem] {
    let stringsToRemove = ["Highlight", "Underline", "Squiggly", "Strikeout"]
    
    // Filter out menu items with titles matching specified strings.
    return items.filter({ (menuItem) -> Bool in
        return !stringsToRemove.contains(menuItem.title)
    })
}
```

{% endcode %}
{% endtab %}

{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
// PTToolManagerDelegate method implementation.
-(BOOL)toolManager:(PTToolManager *)toolManager shouldShowMenu:(UIMenuController *)menuController forAnnotation:(PTAnnot *)annotation onPageNumber:(unsigned long)pageNumber {
    // Only show quick menu for the text select tool.
    if ([toolManager.tool isKindOfClass:[TextSelectTool class]]) {
        // Remove the annotation creation menu items.
        menuController.menuItems = [self removeAnnotationItems:menuController.menuItems];

        return YES;
    } else {
        return NO;
    }
}

- (NSArray<UIMenuItem *> *)removeAnnotationItems:(NSArray<UIMenuItem *> *)items {
    NSArray<NSString *> *stringsToRemove = @[
        PTLocalizedString(@"Highlight", nil),
        PTLocalizedString(@"Underline", nil),
        PTLocalizedString(@"Squiggly", nil),
        PTLocalizedString(@"Strikeout", nil)
    ];
    
    // Filter out menu items with titles matching specified strings.
    return [items objectsAtIndexes:[items indexesOfObjectsPassingTest:^BOOL(UIMenuItem *menuItem, NSUInteger idx, BOOL *stop) {
        return ![stringsToRemove containsObject:menuItem.title];
    }]];
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

| Default Menu Options                                                                                                                                                                                                                             | Customized Menu Options                                                                                                                                                                                                                          |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| ![](https://4149080208-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgY6xtY9ZQd9XMpvWlGM0%2Fuploads%2Fgit-blob-55e916ff02b74ab6cb35fca25c8c12d06cd278db%2F88f82bec5692d821728ec1fd0ff1860c9630593b-746x1057.png?alt=media) | ![](https://4149080208-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgY6xtY9ZQd9XMpvWlGM0%2Fuploads%2Fgit-blob-ee82a7419077954663982f94d692161f2c653f04%2Fddb2d8e7e95dbf073f7da8507bf2c147134feef9-746x1057.png?alt=media) |
| ![](https://4149080208-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgY6xtY9ZQd9XMpvWlGM0%2Fuploads%2Fgit-blob-225567763808518b0cdc9ad2210677505b4bb9a3%2F5b9d090ef5112a9f2e87b2bc809d575cca1f557b-746x1057.png?alt=media) | ![](https://4149080208-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgY6xtY9ZQd9XMpvWlGM0%2Fuploads%2Fgit-blob-6539b7069d6136aa5d6387137322c164d3cbe3a2%2F2f8c10ab1ee744c15211ccc853f46b5923701f3b-746x1057.png?alt=media) |

## Override classes

`Tools.framework` frequently creates new instances of objects defined within the framework (e.g. a new instance of `PTAnnotEditTool` is created when a user taps on an annotation). In order to customize certain aspects of the default behavior, it is required to create and use a subclass of the built-in tool. To enable this without requiring source code modification, `Tools.framework` includes a system to "inject" an externally defined subclass that will be used by the `Tools.framework` during its normal operation. This is done via the `PTOverrides` class as follows:

{% tabs %}
{% tab title="Swift" %}
{% code lineNumbers="true" %}

```swift
// EGAnnotEditTool is an externally defined class that derives from PTAnnotEditTool
PTOverrides.overrideClass(PTAnnotEditTool.self, withClass: EGAnnotEditTool.self);
```

{% endcode %}
{% endtab %}

{% tab title="Obj-C" %}
{% code lineNumbers="true" %}

```objc
// EGAnnotEditTool is an externally defined class that derives from PTAnnotEditTool
[PTOverrides overrideClass:[PTAnnotEditTool class] withClass:[EGAnnotEditTool class]];
```

{% endcode %}
{% endtab %}
{% endtabs %}

Note that in order to be compatible with the external override system, the base class must conform to the `PTOverridable` protocol.

## Customize by modifying the source code

Because `Tools.framework` is open source, any and all changes are possible by editing the source code directly. The project is found in `/Lib/Tools/src`.

If you customize the tools source code, you will likely want to use a universal framework (one that contains architectures for both simulators and devices), which is *not* done automatically by Xcode. The Tools project includes a custom script step that automatically creates a universal framework, located in a folder next to the default build location, with the name `{Debug,Release}-universal`.

(Building a universal framework also be done manually by first building for device, then simulator, and using the terminal command `xcrun -sdk iphoneos lipo -create` to merge the binaries (`Tools.framework/Tools`) into a universal binary.)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/ios/annotation/customization.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
