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
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.
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:
@NativeClass()
since we are extending a platform native class, NSObject.implements UIDropInteractionDelegate
to provide rich TypeScript intellisense on all methods which are available from this interface to use.static ObjCProtocols = [UIDropInteractionDelegate]
to tell iOS our object conforms to UIDropInteractionDelegate.callback: (image: UIImage) => void
mainly for nice TypeScript usage to reference our callback in communicating when the drop occurred.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)
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.
The amazing thing is that it all works on StackBlitz right now! 👉 https://stackblitz.com/edit/nativescript-drag-n-drop-ios