Drag and Drop Text or Images in Our iOS Viewer

When drag and drop was introduced in iOS 11, we made it a priority to support and incorporate this capability, beginning with PSPDFKit 7 for iOS. Now users can drag and drop various elements from and to an application that integrates PSPDFKit.

Drag Interactions

There are various elements users can initiate a drag from. The following interactions are currently possible with PSPDFKit:

  • Dragging images embedded on a document page

  • Dragging text from a document page

Drop Interactions

Additionally, users can drop elements on a document page, and these elements are then converted into PDF annotations. Annotations can be created, updated, and deleted if your license includes the Annotations component.

Here’s the list of drop interactions that are currently supported:

  • Dropping text on a document page. The text will be converted into a free text annotation.

  • Dropping an image on a document page. The image will be converted into an image stamp annotation.

  • Dropping a PDF on a document page. The PDF will be converted into an image stamp annotation of the first page of the dropped document.

Customizing Drag and Drop

All drag-and-drop interactions are enabled by default. However, you can selectively control specific drag-and-drop components using DragAndDropConfiguration. This might be interesting for you if you provide an application that serves documents with sensitive data.

DragAndDropConfiguration can be constructed the same way as the main PDFConfiguration class. To customize a drag-and-drop configuration, you can set a DragAndDropConfiguration object to the dragAndDropConfiguration property of PDFConfiguration, like so:

let pdfController = PDFViewController(document: document) {
    let dragAndDropConfiguration = PSPDFDragAndDropConfiguration { dragAndDropConfigurationBuilder in
        // Customize the drag-and-drop configuration.
    }
    $0.dragAndDropConfiguration = dragAndDropConfiguration
}
PSPDFConfiguration *configuration = [PSPDFConfiguration configurationWithBuilder:^(PSPDFConfigurationBuilder *builder) {
    PSPDFDragAndDropConfiguration *dragAndDropConfiguration = [PSPDFDragAndDropConfiguration configurationWithBuilder:^(PSPDFDragAndDropConfigurationBuilder *dragAndDropConfigurationBuilder) {
        // Customize the drag-and-drop configuration.
    }];
    builder.dragAndDropConfiguration = dragAndDropConfiguration;
}];
PSPDFViewController *pdfController = [[PSPDFViewController alloc] initWithDocument:document configuration:configuration];

Control Drag Elements

The configuration of elements that users can initiate a drag from can be controlled via allowedDragTypes. By default, all types are allowed.

If you only want to allow dragging text from a document, you can configure this as follows:

dragAndDropConfigurationBuilder.allowedDragTypes = .text
dragAndDropConfigurationBuilder.allowedDragTypes = PSPDFDragTypeText;

Control Drag Targets

Drags that were initiated from a source application can be dropped either into the same application or into another application.

To restrict dragging inside the source application, you can disable allowDraggingToExternalApps.

Control Drop Elements

Configuring elements that users can drop onto a document page can be controlled via acceptedDropTypes. By default, all types are accepted.

If you only want to accept dropping images and PDFs onto a document, you can configure this as follows:

dragAndDropConfigurationBuilder.acceptedDropTypes = [.image, .PDF]
dragAndDropConfigurationBuilder.acceptedDropTypes = PSPDFDropTypeImage | PSPDFDropTypePDF;

Control Drop Targets

Drop elements can be dropped on document pages, which can be customized via allowedDropTargets. By default, this is allowed for drags from any other page in the document, apart from the current one, and for drags from an external application.

To allow drops from all targets, including the document page where the drag was initiated from, use the following code:

dragAndDropConfigurationBuilder.allowedDropTargets = .all
dragAndDropConfigurationBuilder.allowedDropTargets = PSPDFDropTargetAll;

Custom Interactions

In case the built-in drag-and-drop support doesn’t meet your requirements, you can also handle drag-and-drop interactions yourself. Reasons for this might be to validate the interactions or to create custom annotations for a drop.

In such a case, you can use your own drag-and-drop interactions instead of using our default ones. This can be done by adding your custom interactions to a PDFPageView subclass and handling the drag or drop there. Additionally, you might want to disable our default interactions, so as to not cause conflicts with your custom interactions via DragAndDropConfiguration.

To add a custom interaction for handling drops, you can use the following logic:

class PageView: PDFPageView {
    override init(frame: CGRect) {
        super.init(frame: frame)

        let dropInteraction = UIDropInteraction(delegate: self)
        self.addInteraction(dropInteraction)
    }
}

extension PageView: UIDropInteractionDelegate {

    func dropInteraction(_ interaction: UIDropInteraction, canHandle session: UIDropSession) -> Bool {
        return true
    }

    func dropInteraction(_ interaction: UIDropInteraction, sessionDidUpdate session: UIDropSession) -> UIDropProposal {
        return UIDropProposal(operation: .copy)
    }

    func dropInteraction(_ interaction: UIDropInteraction, performDrop session: UIDropSession) {
        // Handle drop.
    }
}

let controller = PDFViewController(document: document) {
    let dragAndDropConfiguration = DragAndDropConfiguration { dragAndDropBuilder in
        dragAndDropBuilder.allowDraggingToExternalApps = false
        dragAndDropBuilder.acceptedDropTypes = []
        dragAndDropBuilder.allowedDragTypes = []
        dragAndDropBuilder.allowedDropTargets = []
    }
    $0.dragAndDropConfiguration = dragAndDropConfiguration

    $0.overrideClass(PDFPageView.self, with: PageView.self)
}
@interface PSCPageView : PSPDFPageView <UIDropInteractionDelegate> @end
@implementation PSCPageView

- (instancetype)initWithFrame:(CGRect)frame {
    if ((self = [super initWithFrame:frame])) {
        UIDropInteraction *dropInteraction = [[UIDropInteraction alloc] initWithDelegate:self];
        [self addInteraction:dropInteraction];
    }
    return self;
}

- (BOOL)dropInteraction:(UIDropInteraction *)interaction canHandleSession:(id<UIDropSession>)session {
    return YES;
}

- (UIDropProposal *)dropInteraction:(UIDropInteraction *)interaction sessionDidUpdate:(id<UIDropSession>)session {
    return [[UIDropProposal alloc] initWithDropOperation:UIDropOperationCopy];
}

- (void)dropInteraction:(UIDropInteraction *)interaction performDrop:(id<UIDropSession>)session {
    // Handle drop.
}

@end

PSPDFConfiguration *configuration = [PSPDFConfiguration configurationWithBuilder:^(PSPDFConfigurationBuilder *builder) {
    PSPDFDragAndDropConfiguration *dragAndDropConfiguration = [PSPDFDragAndDropConfiguration configurationWithBuilder:^(PSPDFDragAndDropConfigurationBuilder *dragAndDropBuilder) {
        dragAndDropBuilder.allowDraggingToExternalApps = NO;
        dragAndDropBuilder.acceptedDropTypes = PSPDFDropTypeNone;
        dragAndDropBuilder.allowedDragTypes = PSPDFDragTypeNone;
        dragAndDropBuilder.allowedDropTargets = PSPDFDropTargetNone;
    }];
    builder.dragAndDropConfiguration = dragAndDropConfiguration;

    [builder overrideClass:PSPDFPageView.class withClass:PSCPageView.class];
}];
PSPDFViewController *controller = [[PSCKioskPDFViewController alloc] initWithDocument:document configuration:configuration];