Review and Reply to Annotations on iOS
Annotation replies enable you and other users to have written discussions directly inside a document. As of PSPDFKit 7.5 for iOS, PSPDFKit provides convenient APIs for accessing replies in a document, as well as user interface (UI) components for viewing and editing replies.
Annotations may also have author-specific states associated with them, which allows users to review a proposed change to, for example, say whether they agree or disagree with the change.
PSPDFKit has built-in support to let users view, search, and add replies and reviews. There’s also a model-level API for programmatic access so that you can build custom features. PSPDFKit implements annotation replies and state conforming to the PDF specification and is fully compatible with the same features in Adobe Acrobat Reader.
Terminology
This is the terminology we use:
-
Reply — An annotation with an in-reply-to annotation set.
-
Text reply — A reply where the author-specific state is unspecified.
-
State reply/state annotation — A reply where the author-specific state is specified.
-
Review — A state reply where the author-specific state is set to a review state.
-
Comments — The list containing an annotation and all of its text replies. This means the first comment displays the
contents
of the parent annotation. This is mostly a user-facing term.
Licensing
Replies is a separate component in your PSPDFKit license. Without this component included in your license, your app won’t be able to view, search, or add annotation replies. Please contact our Sales team to add this component to your license.
Third-Party Compatibility
Replies were introduced in the PDF 1.6 specification and are fully compatible with Adobe Acrobat. Other PDF viewers might not be able to show all replies or simply fail to display status information.
Annotation Replies aren’t supported in Instant JSON.
User Interface
Users can tap an annotation and then tap Comments in the menu. This will present a NoteAnnotationViewController
, which shows all the comments on the annotation, as well as any reviews on those comments. Users can both add and edit the comments and add reviews.
Just like with other annotations, users can edit comments even if they didn’t create them.
If there are replies to replies, PSPDFKit will flatten the nested conversation into a single list sorted by date.
Disabling the Replies and Reviews UI
If your license includes annotation replies but you want to disable letting the user view and add replies and reviews, you can define a document features source and disable the replies and reviews feature:
class MyFeaturesSource: NSObject, PDFDocumentFeaturesSource { var features: PDFDocumentFeatures? var canShowAnnotationReplies: Bool { return false } }
@interface MyFeaturesSource : NSObject <PSPDFDocumentFeaturesSource> @end @implementation MyFeaturesSource @synthesize features = _features; - (BOOL)canShowAnnotationReplies { return NO; } @end
Add your features source to the document:
let document: Document = ... document.features.add([MyFeaturesSource()])
PSPDFDocument *document = ... [document.features addSources:@[[[MyFeaturesSource alloc] init]]];
Model API
Replies to an annotation are themselves annotations. A reply is linked to its parent by means of the inReplyToAnnotation
property. In the PDF, this is modeled with the IRT
entry in the annotation dictionary. There’s no direct connection in the reverse direction, i.e. between an annotation and its replies. Instead, all the annotations on the same page must be searched. However, if you use the UI provided by PSPDFKit, this is all taken care of.
The author-specific state associated with an annotation isn’t specified in the annotation itself, but rather in a separate text annotation that refers to the original annotation by means of the inReplyToAnnotation
property. Each time a user changes the state, a new note annotation is created and appended to the linked list of state changes. To find the complete set of states associated with an annotation, it’s necessary to repeatedly search all the annotations on the same page to follow the reverse of the inReplyToAnnotation
property. Again, this is already handled in PSPDFKit’s UI.
Since replies are annotations, they’re included when querying the annotations on a page. You can check if an annotation is a reply using the isReply
property. Doing this doesn’t require the Replies component in the license.
If you’ve licensed the Replies component, you can both read and set the annotation a reply is associated with using the inReplyToAnnotation
property. To add a text reply:
guard let document = originalAnnotation.document else { return } let textReply = NoteAnnotation(contents: "text of the comment") // Replies must be on the same page as their `inReplyToAnnotation` property. // For multiprovider documents, PSPDFKit will fix up the page index when the annotation is added to the document. textReply.pageIndex = originalAnnotation.absolutePageIndex // `originalAnnotation` must have already been added to the document. textReply.inReplyTo = originalAnnotation document.add(annotations: [textReply], options: nil)
if (originalAnnotation.document == nil) { return } PSPDFNoteAnnotation *textReply = [[PSPDFNoteAnnotation alloc] initWithContents:@"text of the comment"]; // Replies must be on the same page as their `inReplyToAnnotation` property. // For multiprovider documents, PSPDFKit will fix up the page index when the annotation is added to the document. textReply.pageIndex = originalAnnotation.absolutePageIndex; // originalAnnotation must have already been added to the document. textReply.inReplyToAnnotation = originalAnnotation; [originalAnnotation.document addAnnotations:@[textReply] options:nil];
You can read and set the author-specific state with the authorState
property on NoteAnnotation
. If the value of this property is .unspecified
, then the annotation isn’t in a state of reply.
For Objective-C, you’ll have to use the authorStateModel
property, along with authorState
. If the properties are PSPDFAnnotationAuthorStateModelUnspecified
and PSPDFAnnotationAuthorStateUnspecified
, then the annotation isn’t a state reply. These properties need to be kept consistent. To add a state reply, do the following:
guard let document = originalAnnotation.document else { return } let stateReply = NoteAnnotation() stateReply.authorState = .reviewing(.completed) // Replies must be on the same page as their `inReplyToAnnotation` property. // For multiprovider documents, PSPDFKit will fix up the page index when the annotation is added to the document. stateReply.pageIndex = originalAnnotation.absolutePageIndex // `originalAnnotation` must have already been added to the document. stateReply.inReplyTo = originalAnnotation document.add(annotations: [stateReply], options: nil)
if (originalAnnotation.document == nil) { return } PSPDFNoteAnnotation *stateReply = [[PSPDFNoteAnnotation alloc] init]; stateReply.authorStateModel = PSPDFAnnotationAuthorStateModelReview; stateReply.authorState = PSPDFAnnotationAuthorStateAccepted; // Replies must be on the same page as their `inReplyToAnnotation` property. // For multiprovider documents, PSPDFKit will fix up the page index when the annotation is added to the document. stateReply.pageIndex = originalAnnotation.absolutePageIndex; // `originalAnnotation` must have already been added to the document. stateReply.inReplyToAnnotation = originalAnnotation; [originalAnnotation.document addAnnotations:@[stateReply] options:nil];