---
title: "iOS undo and redo annotations in PDFs | Nutrient SDK"
canonical_url: "https://www.nutrient.io/guides/ios/features/undo-redo/"
md_url: "https://www.nutrient.io/guides/ios/features/undo-redo.md"
last_updated: "2026-06-19T09:21:00.273Z"
description: "Nutrient iOS SDK supports undo and redo functionality for creating, deleting, and editing annotations, and for form filling."
---

# Undo and redo annotations on iOS

Nutrient iOS SDK supports undo and redo functionality for creating, deleting, and editing annotations, and for form filling. Users can undo and redo changes using the buttons in the [annotation toolbar](https://www.nutrient.io/api/ios/documentation/pspdfkitui/annotationtoolbar), [keyboard shortcuts](https://www.nutrient.io/guides/ios/features/keyboard-shortcuts.md), or the systemwide UI and gestures.

To achieve this, Nutrient uses a standard [`UndoManager`](https://developer.apple.com/documentation/foundation/undomanager). We recommend studying [its documentation](https://developer.apple.com/documentation/foundation/undomanager) to better understand the principles of how this class works. An undo manager is owned and controlled by an instance of [`UndoController`](https://www.nutrient.io/api/ios/documentation/pspdfkit/undocontroller).

An undo controller acts as a data source for the undo manager it manages, and it provides several functions for recording undoable commands. Every [`Document`](https://www.nutrient.io/api/ios/documentation/pspdfkit/document) has its own undo controller, which is exposed via the [`undoController`](https://www.nutrient.io/api/ios/documentation/pspdfkit/document/undocontroller) property. This allows undo and redo functionality to be disabled on a per-document basis.

## Recording commands

To participate in undo and redo, you need to explicitly record undoable commands using [`UndoController`](https://www.nutrient.io/api/ios/documentation/pspdfkit/undocontroller). An undoable command may consist of one or more actions described below.

### Adding and removing annotations

To record an undoable command of adding one or more annotations to a document, wrap an appropriate call in a recording closure, passing an array of annotations expected to be added:

```swift

document.undoController.recordCommand(named: "Add Stamp", adding: [stamp]) {
    document.add(annotations: [stamp])
}

```

To record an undoable command of removing one or more annotations from a document, write the following:

```swift

document.undoController.recordCommand(named: "Remove Note", removing: [note]) {
    document.remove(annotations: [note])
}

```

In the above examples, [`add(annotations:)`](https://www.nutrient.io/api/ios/documentation/pspdfkit/document/add(annotations:options:)) and [`remove(annotations:)`](https://www.nutrient.io/api/ios/documentation/pspdfkit/document/remove(annotations:options:)) can be replaced with equivalent calls to [`AnnotationManager`](https://www.nutrient.io/api/ios/documentation/pspdfkit/annotationmanager) or [`AnnotationProvider`](https://www.nutrient.io/api/ios/documentation/pspdfkit/annotationprovider). As long as such a call results in an annotation being added to or removed from a document, an undoable command will be recorded.

> Adding annotations using [`AnnotationStateManager`](https://www.nutrient.io/api/ios/documentation/pspdfkitui/annotationstatemanager) will result in undoable commands being automatically recorded. Don’t wrap such calls in a recording closure; otherwise, you’ll end up with duplicates. [`AnnotationStateManager`](https://www.nutrient.io/api/ios/documentation/pspdfkitui/annotationstatemanager) is used implicitly when adding annotations using the built-in [annotation toolbar](https://www.nutrient.io/guides/ios/customizing-the-interface/customizing-the-annotation-toolbar.md).

The `title` argument is an optional localized string that can later be displayed in the UI. In the examples above, the recorded undoable commands will be Undo Add Stamp and Undo Remove Note. You can read these titles from the [`UndoManager`](https://developer.apple.com/documentation/foundation/undomanager).

You can only record an undoable command for a subset of added or removed annotations. In the following example, an undoable command will only be recorded for one of two annotations:

```swift

document.undoController.recordCommand(named: nil, remove: [arrow]) {
    document.add(annotations: [arrow, link])
}

```

### Changing annotations

To record an undoable command of changing multiple properties of one or more annotations, wrap your modifications in a recording closure:

```swift

document.undoController.recordCommand(named: "Increase Font Size", changing: [freeText]) {
    freeText.fontSize += 10
    freeText.sizeToFit()
}

```

> Changing the [stacking order](https://www.nutrient.io/guides/ios/annotations/annotation-z-index.md) of annotations can’t be recorded as an undoable action at this moment. [Reach out to us](https://support.nutrient.io/hc/en-us/requests/new) if this is a feature you’re interested in adding to your product.

### Mixing actions

Undoable commands don’t necessarily need to consist of just one type of action. You can freely compose them out of multiple actions:

```swift

document.undoController.recordCommand(named: "Replace Shape") { recorder in
    recorder.record(removing: [square]) {
        document.remove(annotations: [square])
    }
    recorder.record(adding: [circle]) {
        document.add(annotations: [circle])
    }
    recorder.record(changing: [freeText]) {
        freeText.contents = "Circle"
    }
}

```

### Recording actions continuously

Some changes, like changing opacity using a slider or resizing an annotation, begin at one point in time and end at another. To record an undoable command for continuous actions, use a recorder object instead:

```swift

func resizingWillBegin() {
    // Ask the undo controller for a recorder object and retain it.
    recorder = document.undoRecorder.beginRecordingCommand(named: "Resize Stamp", changing: [stamp])
}

// Let the user resize the annotation in the UI.

func resizingDidFinish() {
    // At the end, commit the recorder from a delegate method or completion closure.
    recorder.commit()
}

```

## Disabling undo and redo

Use the [`UndoManager`](https://developer.apple.com/documentation/foundation/undomanager) directly to call [`disableUndoRegistration()`](https://developer.apple.com/documentation/foundation/undomanager/1412239-disableundoregistration). Keep in mind that disabling an undo manager is a balancing operation, meaning [`enableUndoRegistration()`](https://developer.apple.com/documentation/foundation/undomanager/1408957-enableundoregistration) must be called an equal number of times to reenable it:

```swift

document.undoController.undoManager.disableUndoRegistration()

```

## Performing undo and redo

Because undoing and redoing while working with documents are crucial and often-used operations, there are many ways in which they can be performed.

### Standard techniques

Nutrient is a good citizen and supports the standard techniques of undoing and redoing commands. First of all, users can use Command-Z and Shift-Command-Z to undo and redo commands. These keyboard shortcuts work both on iOS and in Mac Catalyst apps.

On iOS, Nutrient additionally supports both [Shake to Undo](https://developer.apple.com/design/human-interface-guidelines/undo-and-redo) and the [three-finger swipe](https://developer.apple.com/design/human-interface-guidelines/gestures) gesture. Shake to Undo can be deactivated by users in the [Accessibility settings](https://support.apple.com/guide/iphone/touch-iph77bcdd132/14.0/ios/14.0) or programmatically using the [`applicationSupportsShakeToEdit`](https://developer.apple.com/documentation/uikit/uiapplication/1623127-applicationsupportsshaketoedit) property of `UIApplication`. The three-finger swipe gesture can be disabled by overriding the [`editingInteractionConfiguration`](https://developer.apple.com/documentation/uikit/uiresponder/3327318-editinginteractionconfiguration) property of `UIViewController`.

In a Mac Catalyst app, undoing and redoing is also possible using the Edit menu in the app’s menu bar.

### From the annotation toolbar

Users can use the buttons in the [annotation toolbar](https://www.nutrient.io/guides/ios/customizing-the-interface/customizing-the-annotation-toolbar.md) to perform undo and redo. If you’re using it in your app, you’re all set. If you have a completely custom annotation toolbar, see the [Reacting to Changes](#reacting-to-changes) section to learn how to integrate it with our undo and redo architecture.

### Programmatically

Use the [`UndoManager`](https://developer.apple.com/documentation/foundation/undomanager) directly to check if undoing or redoing is possible, and if so, do it:

```swift

if document.undoController.undoManager.canUndo {
    document.undoController.undoManager.undo()
}

```

## Reacting to changes

If you’re implementing your own undo and redo buttons and you’re trying to update the enabled state, use the following [delegate method](https://www.nutrient.io/api/ios/documentation/pspdfkitui/annotationstatemanagerdelegate/annotationstatemanager(_:didchangeundostate:redostate:)) of [`AnnotationStateManager`](https://www.nutrient.io/api/ios/documentation/pspdfkitui/annotationstatemanager), which listens to the appropriate notifications and calls back with the states whenever they change:

```swift

func annotationStateManager(_ manager: AnnotationStateManager, didChangeUndoState undoEnabled: Bool, redoState redoEnabled: Bool) {
    customUndoButton.isEnabled = undoEnabled
    customRedoButton.isEnabled = redoEnabled
}

```

> This class supports multiple delegates. Use [`add(_:)`](https://www.nutrient.io/api/ios/documentation/pspdfkitui/annotationstatemanager/add(_:)) and [`remove(_:)`](https://www.nutrient.io/api/ios/documentation/pspdfkitui/annotationstatemanager/remove(_:)) to register them.

If you need more detailed control, you can also observe various [undo manager notifications](https://developer.apple.com/documentation/foundation/undomanager). When a new undoable command is recorded, an [`NSUndoManagerDidCloseUndoGroup`](https://developer.apple.com/documentation/foundation/nsnotification/name/1408817-nsundomanagerdidcloseundogroup) notification will be posted. When a user undoes or redoes a command, [`NSUndoManagerDidUndoChange`](https://developer.apple.com/documentation/foundation/nsnotification/name/1410142-nsundomanagerdidundochange) or [`NSUndoManagerDidRedoChange`](https://developer.apple.com/documentation/foundation/nsnotification/name/1413579-nsundomanagerdidredochange) will be posted.

See [`CustomVerticalAnnotationToolbarExample`](https://github.com/PSPDFKit/pspdfkit-ios-catalog/blob/master/Catalog/Examples/ControllerCustomization/CustomVerticalAnnotationToolbarExample.swift) for a sample implementation of custom undo and redo buttons in the [Nutrient Catalog](https://www.nutrient.io/guides/ios/getting-started/example-projects.md#nutrient-catalog).

## Automatically recorded commands

Nutrient integrates the undo and redo functionality into most of the built-in components. The following list outlines the most important actions that result in undoable commands being recorded. If you’re using our stock controls, there’s nothing you need to do to have them in your app.

### Adding annotations

- Adding annotations using the [annotation toolbar](https://www.nutrient.io/guides/ios/customizing-the-interface/customizing-the-annotation-toolbar.md) or [menu](https://www.nutrient.io/guides/ios/customizing-the-interface/customizing-menus.md)

- [Dragging and dropping](https://www.nutrient.io/guides/ios/features/drag-and-drop.md) text, images, or PDF stamps onto a page

- Pasting previously copied text, images, or annotations

- Programmatically using [`AnnotationStateManager`](https://www.nutrient.io/api/ios/documentation/pspdfkitui/annotationstatemanager)

### Removing annotations

- Removing annotations using the [menu](https://www.nutrient.io/guides/ios/customizing-the-interface/customizing-menus.md) or [keyboard shortcuts](https://www.nutrient.io/guides/ios/features/keyboard-shortcuts.md)

- Clearing annotations from within the [annotation list](https://www.nutrient.io/api/ios/documentation/pspdfkitui/annotationtableviewcontroller)

- Cutting annotations from pages

### Changing annotations

- Editing properties using the [annotation inspector](https://www.nutrient.io/guides/ios/annotations/annotation-inspector.md) or the [menu](https://www.nutrient.io/guides/ios/customizing-the-interface/customizing-menus.md)

- Moving, resizing, or rotating annotations

- Editing contents of free text annotations

- Adjusting shapes of line, polyline, and polygon annotations

- Adding, editing, and removing [replies and reviews](https://www.nutrient.io/guides/ios/annotations/replies.md)

- Editing form fields

## Further reading

For more information about the undo and redo architecture, check out the documentation of the [`UndoController`](https://www.nutrient.io/api/ios/documentation/pspdfkit/undocontroller) and [`UndoRecorder`](https://www.nutrient.io/api/ios/documentation/pspdfkit/undorecorder) protocols.
---

## Related pages

- [Annotations object model on iOS](/guides/ios/annotations/the-annotation-object-model.md)
- [Setting annotation authors on iOS](/guides/ios/annotations/annotation-author-name.md)
- [Define annotation behavior with flags on iOS](/guides/ios/annotations/annotation-flags.md)
- [How to embed files in PDF annotations on iOS](/guides/ios/annotations/create-edit-and-remove/attach-a-file.md)
- [Defining annotation blend modes on iOS](/guides/ios/annotations/annotation-blend-modes.md)
- [Detect changes to PDF annotations in iOS apps](/guides/ios/annotations/detecting-if-annotations-have-changed.md)
- [Annotation state manager on iOS](/guides/ios/annotations/annotation-state-manager.md)
- [Drag-and-drop annotations on iOS](/guides/ios/annotations/create-edit-and-remove/drag-and-drop.md)
- [Disable PDF annotation editing for iOS users](/guides/ios/annotations/create-edit-and-remove/disable-editing.md)
- [Programmatically create PDF annotations on iOS](/guides/ios/annotations/programmatically-creating-annotations.md)
- [Image picker: Add image annotations to PDFs on iOS](/guides/ios/miscellaneous/image-picker.md)
- [Z-index for annotation stacking order on iOS](/guides/ios/annotations/annotation-z-index.md)

