Using Drag and Drop on UITableView for reorder
If we use custom cells for a UITableView, and we want to re-order using drag and drop, we need to make our cell model conform to NSItemProviderWriting, NSItemProviderReading and NSObject. To move an object using drag and drop, we need to to be able to serialize and deserialize it. Also, we need to have a drag/drop delegate. Usually, this could be our ViewController.
First, let’s set up our delegate.
UITableViewDragDelegate and UITalbeViewDropDelegate
If we are using our ViewController as the delegate for the drag and drop we need to conform to the protocols UITableViewDragDelegate and UITableViewDropDelegate. To comply with the protocols, we need the following methods:
itemsForBeginning - This method returns an array. The elements on the array represent the cells being dragged. As you see (in the code below), they come from an NSItemProvider. In turn, the NSItemProvider expects an object that implements NSItemProviderWriting.
| |
dropSessionDidUpdate - This method indicates that we want to “accomplish” (or our “intent”) for the drop action.
| |
perforDropWith - This is the method is in charge of the drop action. Note that the drop action could occur asynchronously, also remember that we always use the main queue when handling the UI.
| |
That’s all we are going to do on our drag and drop delegate.
Let’s now review the changes we need to make to our model. Start by making sure we are implementing the following protocols: NSItemProviderWriting, NSItemProviderReading and NSObject.
| |
To comply with NSItemProviderReading we will need to implement the following method:
| |
In the NSItemProviderReading protocol definition, the method returns Self (capital S). Self is used to represent the Class of the object implementing the protocol. When we implement the protocol, we replace the Self with our current model’s class (Counter in my case).
| |
The class should be final because the compiler needs to make sure that it can statically (using the information in the code, not dynamically at runtime) infer what the Self object is. If the class were not final, and it is inherited, the child class will have a conflicting method. The child class will have one method inherited from the parent with the Self being the parent class and its method where Self is the child class. To avoid that problem, we need to make our class final. (see this StackOverflow thread)
| |
In my case I’ll be serializing and deserializing the Counter object using Codable, that is the reason you saw Codable in the class signature.
To implement the NSItemProviderWriting protocol, we need to indicate the format that the serialization supports. In our case, we are going to serialize the object into JSON and then transmitting it as a String. We could use other formats, but “text” is simple enough. So we need to have the following property.
| |
When the data is going to be sent, the loadData method is called. This method is in charge of preparing the model to be sent. The loadData method returns a Progress object. We use a Progress object to indicate the status of loading the data. In our case, the loading of data is “immediate”, that is why we go straight to 100.
| |
That’s all we need to comply with NSItemProviderWriting. Now to comply with NSItemProviderReading, we also need to make clear the data type we support. We do this by setting the following property:
| |
When we receive the data, we use the method object(withItemProviderData:typeIdentifier) to unarchive the data and build an instance of our model object.
| |
That should be enough to implement the drag and drop reordering for TableView.
Final Thoughts
If we are already implementing drag and drop on our app, we can also use the capability to reorder our table view. It might seem complicated at first, but once you’ve done it a couple of times, you’ll see the logic behind it.
Remember to import MobileCoreServices so we can have access to the data types identifiers (i.e. kUTTypeUTF8PlainText)
| |
To better identify the possible errors, we can create an enum with the following errors:
| |
Resources / Notes
- Drag and Drop with Collection and Table View
- WWDC 2017 Data Delivery with Drag and Drop
- UTI Text Types
- To understand why wee need our class to be final when implementing the
NSItemProviderReadingprotocol you can read the following resources