Before you implement drag and drop in the Augmented Reality gallery, let's see how you can implement a simple version of drag and drop with a simple view and an image. In the code bundle for this chapter, you'll find a sample project named PlainDragDrop. Open the starting version for this project and run it on an iPad simulator. You should see the user interface shown in the following screenshot:
The goal of this example is to allow users to perform the following actions:
- Drag the image to the drop area.
- Drag an external image to the drop area.
- Drag the bottom image to an external app.
While this might sound like you are going to have to do a lot of work, it's quite simple to implement all three features at once. Simply implementing the first feature enables the other two! Quite convenient, right? You are going to implement drag and drop for this example in just three simple steps:
- Make ViewController conform to UIDragInteractionDelegate.
- Make ViewController conform to UIDropInteractionDelegate.
- Add interactions to the image and drop area.
If you feel confident that the theoretical explanation from the previous section left you with enough knowledge to implement these steps on your own, that's great! You should give implementing this on your own a go and then refer back to the following code snippets if you get stuck or to check the work you've done. If you prefer to follow along instead, that's great too.
The first step to enable drag and drop in the sample app is to make DragDropViewController conform to UIDragInteractionDelegate. To do this, you only have to implement a single method. As shown in the explanation about the drag and drop experience, the only required method on UIDragInteractionDelegate is dragInteraction(_:itemsForBeginning:). This method requires you to provide a UIDragItem for the drag session. Since this example will not support dragging multiple items from the app, there is no need to implement other delegate methods.
Add the following extension to DragDropViewController.swift to implement UIDragInteractionDelegate:
extension DragDropViewController: UIDragInteractionDelegate { func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] { guard let image = imageView.image else { return [] } let itemProvider = NSItemProvider(object: image) return [UIDragItem(itemProvider: itemProvider)] } }
The preceding code should look familiar to you since it closely resembles the example code from the previous section. Now that DragDropViewConroller conforms to UIDragInteractionDelegate, let's make it conform to UIDropInteractionDelegate as well. Add the following extension to DragDropViewController.swift:
extension DragDropViewController: UIDropInteractionDelegate { func dropInteraction(_ interaction: UIDropInteraction, sessionDidUpdate session: UIDropSession) -> UIDropProposal { return UIDropProposal(operation: .copy) } func dropInteraction(_ interaction: UIDropInteraction, performDrop session: UIDropSession) { guard let itemProvider = session.items.first?.itemProvider, itemProvider.canLoadObject(ofClass: UIImage.self) else { return } itemProvider.loadObject(ofClass: UIImage.self) { [weak self] loadedItem, error in guard let image = loadedItem as? UIImage else { return } DispatchQueue.main.async { self?.dropArea.image = image } } } }
Again, this implementation should look familiar to you. The first method, dropInteraction(_:sessionDidUpdate:), returns a copy proposal since we don't want to move data around. The second method is dropInteraction(_:performDrop:). This method retrieves the image from itemProvider that has been created in UIDragInteractionDelegate and sets the loaded image as the image for dropArea.
The next step is to add the drag and drop interaction objects to the correct views, as follows:
override func viewDidLoad() { super.viewDidLoad() let dragInteraction = UIDragInteraction(delegate: self) imageView.addInteraction(dragInteraction) let dropInteraction = UIDropInteraction(delegate: self) dropArea.addInteraction(dropInteraction) }
Now that all interactions are set up, go ahead and run the app on an iPad. You'll be able to drag the bottom image to the top section, and the image will appear in the top area. If you run the Photos app alongside PlainDragDrop, you can drag the bottom image to the photos app, and it will be added to photos. If you drag an image from the Photos app to the top section, the image from Photos will be set as the image for the drop area. Pretty cool stuff! And it was pretty simple to cover all these cases.
Even though the iPhone does not have full drag and drop support, you can still allow a user to drag the image from the top area to the bottom area. Since the iPhone does not support a full drag and drop experience, all drag and drop interaction objects you create are disabled by default. If you want to allow users to use drag and drop in your iPhone app, you must explicitly enable the interaction objects you would like to support. Add the following code to viewDidLoad in DragDropViewController to enable the drag interaction in the PlainDragDrop app:
dragInteraction.isEnabled = true
Go ahead and test the app on an iPhone; you should be able to drag the top image around. Now that you know how to enable drag and drop for a plain view, let's see how you can add it to a collection view.