Mallow's Blog

Simple example of Drag and Drop feature in iOS 11

We saw in brief about the Drag and Drop feature introduced by Apple during their WWDC a few months back. Today, we will have a deeper look on the feature through a sample code.

We will see how to implement the Drag and Drop feature in View and Table View. Create a new project and add 2 options View and TableView. When the user selects Custom View option, navigate to CustomViewController and design the UI like given below.

Custom view

In this screen, we are going to do a drag from one imageView to another imageView and from one textView text to another textView.

First, enable the user interaction for imageView. Then, we need to set a Drag interaction for ImageView with the DragInteraction delegate. In the delegate only we are going to specify the DragItems which we are going to drag.

</span></p>
<p class="p2"><span class="s1">        dragImageView.isUserInteractionEnabled = true</span></p>
<p class="p2"><span class="s1">        dropImageView.isUserInteractionEnabled = true</span></p>
<p class="p2"><span class="s1">        dragImageView.addInteraction(UIDragInteraction(delegate: self))</span></p>
<p class="p2"><span class="s1">        dragTextView.addInteraction(UIDragInteraction(delegate: self))

Once an object is ready for drag, the below delegate method will be called.

</span>

func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -&gt; [UIDragItem]<span class="s1">

We are going to drag the imageview and textview to be dragged so the code implementation will be as follows.

</span>

func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -&gt; [UIDragItem] {
if let textValue = interaction.view as? UITextView {
let provider = NSItemProvider(object: textValue.text! as NSString)
let item = UIDragItem(itemProvider: provider)
return [item]
} else if let imageView = interaction.view as? UIImageView {
guard let image = imageView.image else { return [] }
let provider = NSItemProvider(object: image)
let item = UIDragItem(itemProvider: provider)
return [item]
}
return []
}<span class="s1">

Spring Loaded:

This is to recognise the action for a button when we hover the dragging item above the button. Once you drag the image and hover the button it will unhide dropping area. Like below

</span></p>
<p class="p2"><span class="s1">        dropOffButton.isSpringLoaded = true

dragging

Drop interaction:

We need to set a Drop interaction for ImageView with the DropInteraction delegate. In the delegate only we are going to handle the Dropping operations. 

Once the dragging operation is started, the below drop interaction delegate method will be called.

</span>

func dropInteraction(_ interaction: UIDropInteraction, sessionDidUpdate session: UIDropSession) -&gt; UIDropProposal<span class="s1">

To check whether the image or text can be dropped in the particular frame the below code is used.

</span>

func dropInteraction(_ interaction: UIDropInteraction, sessionDidUpdate session: UIDropSession) -&gt; UIDropProposal {
let location = session.location(in: self.view)
let dropOperation: UIDropOperation?
if session.canLoadObjects(ofClass: String.self) {
if dropTextView.frame.contains(location) {
dropOperation = .copy
} else if dropImageView.frame.contains(location) {
dropOperation = .forbidden
} else {
dropOperation = .cancel
}
} else if session.canLoadObjects(ofClass: UIImage.self) {
if dropTextView.frame.contains(location) {
dropOperation = .forbidden
} else if dropImageView.frame.contains(location) {
dropOperation = .copy
} else {
dropOperation = .cancel
}
} else {
dropOperation = .cancel
}

return UIDropProposal(operation: dropOperation!)
}<span class="s1">

acceptable

not acceptable

To perform a drop operation, we need to implement the code below.

</span></p>
func dropInteraction(_ interaction: UIDropInteraction, performDrop session: UIDropSession) {
if session.canLoadObjects(ofClass: String.self) {
session.loadObjects(ofClass: String.self) { (items) in
if let values = items as? [String] {
self.dropTextView.text = values.last
}
}
} else if session.canLoadObjects(ofClass: UIImage.self) {
session.loadObjects(ofClass: UIImage.self) { (items) in
if let images = items as? [UIImage] {
self.dropImageView.image = images.last
}
}
}<span class="s1">

Second, we are going to look about the TablView Drag and Drop.

TableView Drag and Drop:

In table view we are having DataSource and Delegate method like now we have DragInteraction and DropInteraction delegate method. In Table view the drag and drop are mainly based on the index path. 

First, we need to set a drag and drop delegate.

</span>

tableView.dragDelegate = self
tableView.dropDelegate = self<span class="s1">

Similar to the method we did in custom view, we need to specify the drag item in the drag delegate. This is done by the below method.

</span>

func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -&gt; [UIDragItem]<span class="s1">

To drag a row, the following code is followed.

</span>

func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -&gt; [UIDragItem] {
let string = stringArray[indexPath.row]
let itemProvider = NSItemProvider(object: string as NSString)
return [UIDragItem(itemProvider: itemProvider)]
}<span class="s1">

Once the dragging action is begin the screen looks like this.

For drop, we need to check whether the table view can handle the dropping object or not. This will be done by the following code.

</span>

func tableView(_ tableView: UITableView, canHandle session: UIDropSession) -&gt; Bool {
return session.canLoadObjects(ofClass: NSString.self)
}<span class="s1">

Once the above condition is satisfied, then the below delegate method is called. In this delegate method we are going to handle the copy or move operation to the table view.

</span>

func tableView(_ tableView: UITableView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -&gt; UITableViewDropProposal {
return UITableViewDropProposal(operation: .copy, intent: .automatic)
}<span class="s1">

Once the object is ready for dropping then the below delegate method will be called.

</span>

func tableView(_ tableView: UITableView, performDropWith coordinator: UITableViewDropCoordinator)<span class="s1">

To handle the dropping operation for Text, the code will be as below.

</span>

func tableView(_ tableView: UITableView, performDropWith coordinator: UITableViewDropCoordinator) {
let destinationPath: IndexPath

if let indexPath = coordinator.destinationIndexPath {
destinationPath = indexPath
} else {
let section = tableView.numberOfSections - 1
let row = tableView.numberOfRows(inSection: section)
destinationPath = IndexPath(row: row, section: section)
}

coordinator.session.loadObjects(ofClass: NSString.self) { (items) in
if let values = items as? [String] {
var indexPathArray = [IndexPath]()
for (index, item) in values.enumerated() {
let indexPath = IndexPath(row: destinationPath.row + index, section: destinationPath.section)
self.stringArray.insert(item, at: indexPath.row)
indexPathArray.append(indexPath)
}

tableView.insertRows(at: indexPathArray, with: .automatic)
}
}
}<span class="s1">

We have seen the simple operation of drag and drop in this blog. In our upcoming blog we will see how we can incorporate animations and custom interactions in Drag and Drop. You can find the full source code of this sample program in the github.

Yogesh M,
iOS Team,
Mallow Technologies.

Leave a Comment

Your email address will not be published. Required fields are marked *