How to enable drag and drop with iOS...from even outside the app!
Enabling drag and drop on iOS can be powerful in connecting desirable user experience flows.
Enabling drag and drop on iOS can be powerful in connecting desirable user experience flows. Let's look at how straight forward it can be to enable this behavior in your NativeScript app.
The basis of the details provided here were all taken directly from the official iOS platform documentation, Apple Developer Docs.
You can even try it right now in your hands on StackBlitz: 👉 https://stackblitz.com/edit/nativescript-drag-n-drop-ios
Setup a drop zone utility
Let's setup a utility that will take any View and wire up the drop interaction for us.
import { View } from '@nativescript/core'
export function createDropZone(
view: View,
callback: (image: UIImage) => void
) {
// 1. Create a delegate with our callback reference
const dropDelegate = DropDelegate.new() as DropDelegate
dropDelegate.callback = callback
// 2. Add desired interaction configured with the delegate to our View
const drop = UIDropInteraction.alloc().initWithDelegate(dropDelegate)
(view.ios as UIView).addInteraction(drop)
}
The first argument will allow us to pass in any NativeScript View instance. The second argument will allow us to pass in a callback with the image that was dropped on our View.
Following Apple Developer Docs, we want to setup a UIDropInteractionDelegate with the methods outlined and create a UIDropInteraction bound to that delegate.
Create a delegate with our callback
We can setup such a delegate directly in TypeScript as follows:
@NativeClass()
export class DropDelegate
extends NSObject
implements UIDropInteractionDelegate
{
// This tells iOS our object conforms to UIDropInteractionDelegate
static ObjCProtocols = [UIDropInteractionDelegate]
// A reference to our callback to communicate when the drop occurred
callback: (image: UIImage) => void
// iOS delegate implementation details
dropInteractionCanHandleSession(
interaction: UIDropInteraction,
session: UIDropSession
): boolean {
return session.canLoadObjectsOfClass(UIImage.class())
}
dropInteractionSessionDidUpdate(
interaction: UIDropInteraction,
session: UIDropSession
): UIDropProposal {
return UIDropProposal.alloc().initWithDropOperation(UIDropOperation.Copy)
}
dropInteractionPerformDrop(
interaction: UIDropInteraction,
session: UIDropSession
): void {
session.loadObjectsOfClassCompletion(UIImage.class(), (imageItems) => {
const image = imageItems.objectAtIndex(0)
this.callback(image as UIImage)
});
}
}
Let's break down what's happening in this implementation:
- We decorate the class with
@NativeClass()since we are extending a platform native class, NSObject. - We use
implements UIDropInteractionDelegateto provide rich TypeScript intellisense on all methods which are available from this interface to use. - We declare
static ObjCProtocols = [UIDropInteractionDelegate]to tell iOS our object conforms to UIDropInteractionDelegate. - We declare
callback: (image: UIImage) => voidmainly for nice TypeScript usage to reference our callback in communicating when the drop occurred. - We implement the methods as described by the official platform Apple Developer Docs.
What our utility does
With those details setup, our JavaScript utility function can now create delegates for any View desired to add a Drop interaction to.
// 1. Create a delegate with our callback reference
const dropDelegate = DropDelegate.new() as DropDelegate
dropDelegate.callback = callback
// 2. Add desired interaction configured with the delegate to our View
const drop = UIDropInteraction.alloc().initWithDelegate(dropDelegate)
(view.ios as UIView).addInteraction(drop)
Wire up a View FTW 🎉
We can take any View and use it's loaded event to setup the wiring.
<GridLayout (loaded)="loadedDropZone($event)">
<Image [src]="droppedImage" />
</GridLayout>
When the loaded event fires it passes along the View reference to our utility:
function loadedDropZone(event) {
createDropZone(event.object, image => {
this.droppedImage = image
})
}
Using any flavor of choice with it's own binding syntax, this will work anywhere under any conditions.
Try for yourself on StackBlitz
The amazing thing is that it all works on StackBlitz right now! 👉 https://stackblitz.com/edit/nativescript-drag-n-drop-ios