Drag and Drop on iOS And MultiTasking - Digital Leaves
2914
post-template-default,single,single-post,postid-2914,single-format-standard,ajax_fade,page_not_loaded,,select-theme-ver-3.8,wpb-js-composer js-comp-ver-5.1.1,vc_responsive
Drag and Drop on iOS 11

Drag and Drop on iOS And MultiTasking

Welcome! This article will show you how to perform Drag and Drop on iOS. New in iOS 11, we have all the power of multitasking available for us developers. This includes dragging and dropping items inside our applications or between applications.

This article also presents a sample app called “Everation”, allowing you to get inspirational quotes or text from any other application (i.e: Safari) and add it as a text or quote note to your dashboard of inspirational texts.

Drag and Drop on iOS

Drag and Drop is a way of graphically move or copy data, within the same application or between applications.

Apple tries to transmit drag and drop to iOS in the context of its multi-gesture paradigm. Not only Drag and Drop on iOS can be done within multiple apps using the split view functionality of devices such as the iPad Pro. Also, it’s performed interactively, allowing you to manipulate the screen with all fingers. That supposedly allows you to manipulate other elements of the screen, change the split view apps, or even drag several types of items like text copied from Safari and images from your camera roll.

Honestly, while trying to perform such interactions, I found it kind of weird and unnatural for something more elaborate than just dragging and dropping a single piece of data.

As you can see in the video, I try to copy some text from Safari and then simultaneously some images from the camera roll into a note. While the images get to the note without a problem, the text disappears. Also, notice how the finger interaction in my right hand is kind of stressful when selecting multiple images.

So all in all, I think it’s a pretty useful feature. However, I don’t think people it’s going to be able to get the most out of this multi-finger, multi-gesture interactions (maybe centennials).

Support for Drag and Drop on iOS Devices

Drag&Drop is certainly a productivity feature. Thus, it makes more sense for the iPad. For this reason, Apple has decided to offer it on iPhone devices only in a limited form. Concretely, you can only use it in the same application.

Concepts Behind Drag and Drop on iOS

Drag and Drop on iOS has four differentiate phases:

Lift

The lift is when the user performs a long press, indicating the purpose of dragging and dropping something. A lift preview is generated for the item, and then the user starts dragging the finger.

Drag

The dragging is when the user is moving the object with the finger through the surface of the screen. During this phase, the preview of the item gets updated, and some interactions are allowed such as tapping another element to add it to the current dragging session.

Dropping

Dropping happens when the user lifts the finger. At that moment, two things can happen, either the drag is canceled, or the drop takes places into its target.

Data Transfer

If the dragging process is not canceled and a drop takes place, the data transfer is the phase in which the destination requests the data to the source.

Drag and Drop Interactions

Both dragging and dropping are implemented as interactions and attached to a view. Most usually, that view is the main view of our UIViewController or one of its subviews. As a result, our way of communicating with those interactions is by means of delegates.

Implementing Dragging

For dragging, we have a class UIDragInteraction, that calls its UIDragInteractionDelegate (i.e: our view controller). This delegate has only one mandatory method: dragInteraction(:itemsForBeginning:), that should return the items to be included when the drag animation begins. You can return no items here, in which case the Drag&Drop gets canceled.

These items are instances of UIDragItem, model objects that contain a Drag Preview (how the items look like when it’s been dragged) and an Item Provider (the source of the data). If the drop takes place, this Item Providers will be asked to provide the data to appear in the destination target.

Implementing Dropping

In order to enable dropping, we need to define a UIPasteConfiguration property for the target view. Now all UIResponders, including UIView, can define a UIPasteConfiguration, allowing us to paste inside elements of a specific type.

Here, when identifying the paste configuration, we need to provide the type of objects we will be accepting. In this case, it will be NSString.self for text. Note how we are using the Objective-C string instead of its Swift counterpart.

Next, we need to define the paste method. This method will be called both when we receive a paste from a copy/paste operation, and when we receive a drop from a drag&drop operation.

However, in order to have more control over the interaction, we usually want to become the UIDropInteractionDelegate for the UIDropInteraction.

The Drop Interaction Delegate

When the user lifts the finger after a drag, the drop operation can be canceled or accepted. If it gets canceled, an animation will be triggered showing the object going “back” to its initial position.

However, if the drop is accepted, the delegate will be called to perform the drop via the dropInteraction(:performDrop:). Only during this method is the delegate allowed to request the data that’s been dropped to present it in the proper view. This is done by asking the Item Provider for the Drag Items.

In order to accept the drop, we also need to implement the dropInteraction(:sessionDidUpdate:). This method is called when the drag enters the interaction’s view, moves inside it, or new items get added to the drag while inside the view. This method needs to return a UIDropProposal containing the operation to perform if the drop succeeds. Valid values to return are:

  • .cancel: if we want to cancel the operation.
  • .copy: if we want to copy the data when the drag enters the drop area. This is most of the times what you would want to return.
  • .move: similar to .copy, we use it within our application when we want the data to “move” instead of generating a new copy. Imagine that you have a list of items in your application and you want to allow drag & drop to move them along the list. Then, you would use .move instead of .copy, as the data is already there. You need to allow this by implementing UIDropSession’s delegate method allowsMoveOperation.
  • .forbidden: similar to .cancel, but used to indicate a temporary or contextual cancel. Use it for targets where you would normally allow a drop, but some situations (i.e: network unavailable, read-only folder, loading…) prevent the interaction from happening right now.

Performing The Drop

The drop takes place in the dropInteraction(:performDrop:) method. As we mentioned, here is where you can retrieve the data from the source. Let’s see an example of how to access text (NSString) elements.

Now let’s see a complete app making use of this technique.

The Everation App

Our sample application is very simple. It contains a dashboard with a collection view containing all the texts, quotes or notes we have collected from any other app. We allow drag and drop, as well as copy paste, so we can create a new everation from any piece of text in any other app.

You can see our “Drag and Drop on iOS” app in action in the video above. Let’s have a look at how to implement this.

Basic Structure

Our app only has one view controller, containing a collection view and a message with a “Paste” button. In our model, we define an enumeration called “EverItem”, with two cases:

  • First, a text item, containing a String.
  • Second, an image item, containing a UIImage.

Also, we define a cell subclass for each of these two cases, to display either a label with a text or a UIImageView with the image. Finally, we set our collection view data source/delegate methods and add some functions to calculate the size of the rows to display to make them look cool at any screen configuration.

Up until here, our app is just the standard collection view app. Now, let’s start adding the drag and drop functionality.

External Copy VS Internal Movement

We need to differentiate two cases:

  • If we are copying or dragging and dropping a text or an image from another application.
  • If we are moving cells within our own application.

This last scenario will allow us to easily reorder our cells by dragging and dropping them across the collection view. In order to track these two scenarios, we’ll use two variables:

  • originIndexPath: index path for the cell we started to drag. If the drag comes from an external source (like another app), this will be nil.
  • destinationIndexPath: index path where we will drop our item. We will use this variable to insert the cell in the middle of the collection. If the cell gets dropped at the end of the collection view, or in no index path in particular, this will be nil.

Enabling Paste and Inserting the New Item

First, we need to allow our view controller to receive (paste) items of type String or UIImage. We can specify this by setting the UIViewController’s pasteConfiguration property.

Second, we define the paste method. All UIViewControllers, being UIResponders, can override it. In our case, we receive an array of item providers and will call a method called loadContent to take care of loading all the content from all those providers.

Next, we need to define this loadContent method to copy or move the new item we paste or drop into our collection view. Generally speaking, this item will come from a NSItemProvider, the source entity where the data comes from. Both Drag&Drop operations (via its session items) and the Copy/Paste (via its UIPasteBoard items) will give you access to these item providers.

Loading Content from an Item Provider

Thus, we will define our loadContent method to generate and add an item from an item provider:

Here, we basically ask if the provider has some strings or images for us (via its canLoadObject method), and then load it and create the appropriate type of EverItem.

Adding and Removing the Items in our Collection View

The addNewItem method depends on the values of originIndexPath and destinationIndexPath:

  • If originIndexPath is present, we are moving an item from within our collection view. Thus, we will delete the cell (and associated data item) from the collection view.
  • if destinationIndexPath is present, we are moving or copying the item to a specific position in the collection view. In that case, we will insert it in between (using

Even though it might seem tedious, this method is actually quite simple. We just remove the items and associated cells from originIndexPath (if not nil), and then add the new items at the desired destinationIndexPath (if not nil) or at the very end of the collection view (otherwise). We wrap all these operations in a collectionView.performBatchUpdates to get a beautiful animation alongside our changes.

After adding the item, we need to set the origin and destination index paths to nil, via the clearPaths method.

Next, let’s have a look at how to control the dragging part of the equation.

The Drag Interaction

First, at viewDidLoad, we will set ourselves as the delegates of the dragging operation in collection view:

Then, the only mandatory item we need to provide for the delegate is the dragInteraction(:itemsForBeginning:). If we are managing a drag within our collection view, we will return here the item from the cell that’s being dragged. Otherwise, we return an empty array -because the interaction comes from an external app that provides the item-.

Two important things to notice here.

First, in order to determine if we are dragging one of our cells, we first extract the interaction point by using session’s method location(in:). Then, with that point, we can use collection view’s method indexPathForItem(at:) to get the index path for that exact location, if any. In that case, we are dragging from one of our cells, and we can set originIndexPath.

Second, in that case, we create a UIDragItem to return by building an NSItemProvider with the concrete object contained in the cell (of type EverItem), and wrapping it inside a UIDragItem.

Polishing Touches: Dragging Animations

During the dragging interaction, the UIDragInteractionDelegate is asked to specify how the dragging animation will work. This implies how to display the items while they are dragged and the “placeholder item” that is left while we are animating the drag.

Let’s have a look at those methods:

First, dragInteraction(:previewForLifting:session:) will define the preview that will be shown underneath the user’s finger while we are animating the object. You should define this method for dragging and dropping operations within the app itself (i.e: we are moving a cell). Thus, in our case, we will return the cell that’s being dragged, or nil if the interaction comes from an external app.

Next, we can define the animation for the placeholder that’s left in its original posotion while we drag the item. In our case, we can use a nice semi-transparent effect for the cell we are moving.

Finally, whether the interaction finishes successfully or gets canceled, we want to return the cell back to full opacity.

The Drop Interaction

Similarly, the first thing we need to take care of for the drop interaction is setting ourselves as its delegate.

Then, we need to specify what type of objects we accept. We do that in the dropInteraction(:canHandle:). We will return true if we are receiving an image or a text. In order to do that, we use the method canLoadObjects from UIDropSession.

Next, we also need to update our dropping session when appropriate. We need to distinguish if we are copying the item from an external application, or moving it within our own collection view. Also, we update our destinationIndexPath if it’s above one of our cells.

Finishing The Dropping Operation

Finally, we drop the item in the dropInteraction(:performDrop:) method:

As you can see, this method only updates the destination index path (just in case) and for every item contained in the session, calls loadContent.

The result is a dynamic, rich application that can receive text and images from external applications or move the ones already there. Drag and Drop on iOS really adds a new level of interactivity to the application.

In Summary

In this article, you learned about drag and drop on iOS 11, and how to add it to your applications.

Drag and Drop on iOS can really help your interfaces become more interactive and dynamic. As always, you must implement it only if it makes sense for your user experience, not just for the sake of it. Nevertheless, adding it can really help your app stand out from the competition.

If you want to know a little more about how I configured the collection view to display a specific number of rows, no matter the device, have a look at the Flawless collection and table views tutorial.

Also, as always, you can get the full source code for the project from my Github repository.

No Comments

Post a Comment

Before you continue...

Hi there! I created the Digital Tips List, the newsletter for Swift and iOS developers, to share my knowledge with you.


It features exclusive weekly tutorials on Swift and iOS development, Swift code snippets and the Digital Tip of the week.


Besides, If you join the list you'll receive the eBook: "Your First iOS App", that will teach you how to build your first iOS App in less than an hour with no prior Swift or iOS knowledge.

I hate spam, and I promise I'll keep your email address safe.