Building a Trello-like iOS App with Drag & Drop API
By: Alfian Losari
Tags: Cocoa, Trello, Drag Drop, iOS, iOS App Development

Setup Board Collection View Cell

Create a new File and name it BoardCollectionViewCell . Type or paste this snippet of code below into the file.

Here are the brief explanation of what the code does:

Create a UICollectionViewCell subclass with the name of BoardCollectionViewCell . We declare 3 instance properties: the tableView , board model, and weak reference to the BoardCollectionViewController . Override awakeFromNib method so that we can set the cell’s layer to make the corners a bit rounded. Next, we set the table view’s delegate and datasource to the BoardCollectionViewCell . We create an IBAction method which is invoked when the user taps on the Add button. Inside the method, we create a UIAlertController with alert style, and add a UITextField to enter the name of the item to be added to the board. When a user adds the item, we just append it to the board model items array. Then, we tell the Table View to insert the new row at the bottom and scroll to the specified IndexPath . Create the setupWithBoard method that accepts a Board model as the parameter. It stores the passed board into the instance property, then invoke the Table View reloadData method to update the view. Create an extension that implements the UITableViewDataSource and UITableViewDelegate . For the tableView:numberOfRowsInSection: , we just return the number of items in our board. In tableView:cellForRowAtIndexPath: , we dequeue the Table View Cell with identifier we set in storyboard, get the item using the IndexPath row from the board items, and set the cell textLabel text property with the item to display it.

That’s it, make sure you have set the class of the UICollectionViewCell in the storyboard as BoardCollectionViewCell . Then connect the Add Button touchUpInside action to the addTapped: selector.

Setup Board Collection View Controller

Create a new File and name it BoardCollectionViewController . Type or paste this snippet of code below into the file.

Here is a brief summary of what the code actually do:

Create a subclass of UICollectionViewController with the name of BoardCollectionViewController . It also implements the UICollectionDelegateFlowLayout because we have set our Collection View Controller to use the flow layout in the storyboard. In viewDidLoad , we invoke the setupAddButtonItem method. This method adds an UIBarButtonItem to the NavigationBar right bar button item. We set the target action selector to the addListTapped: method. At last, we invoke the updateCollectionViewItem passing the size of our view to set the item size of the cell. In updateCollectionViewItem method, we calculate the height of Collection View Cell item dynamically to be 0.8 of the view height. The width of the cell is fixed at 225 points. This method will also be invoked when the screen get rotated passing the new size of the view . When addListTapped is invoked, we create an UIAlertController with alert style. Next, we add the UITextField to enter the name of the board that the user wants to create. After the user fills the Text Field and confirm, we create a new Board with the text from the user as the title property and append it to the Board array. Lastly, we tell the Collection View to insert the new item to the last indexPath and scroll automatically to the new position. For the collectionView:numberOfItemsInSection: , we simply return the size of the boards array. For the collectionView:cellForItemAtIndexPath: , we dequeue the cell using the identifier we set in storyboard and cast it to BoardCollectionViewCell . Then, we get the board using the indexPath item and invoke the cell’s setupWithBoard: method passing the board to the cell.

Run the app for the first time

Try to build and run the app for the first time, you should see the list of boards with their items. Also, try to add new boards and items, and scroll horizontally around the screen. Next, we are going to implement the Drag & Drop API into the Table View to move the items around the boards.

Running the app for the first time

Handle Dragging an Item in UITableView with UITableViewDragDelegate

Adding the drag support to the Table View is pretty straightforward. All we need to do is set 2 properties, the dragInteractionEnabled to true and dragDelegate to the BoardCollectionViewCell . Open the BoardCollectionViewCell and go to the awakeFromNib method and add these lines:

...

override func awakeFromNib() {

....

tableView.dragInteractionEnabled = true

tableView.dragDelegate = self

}

...

The compiler will complain because the BoardCollectionViewCell has not implemented the UITableViewDragDelegate yet. So, go to the bottom of the file and add the snippet code below as the extension.

Here are the explanations of what the UITableViewDragDelegate does:

UIDragItem Representing Drag item Data Diagram. Courtesy of Apple Inc.

The UITableViewDragDelegate requires the delegate to implement the method tableView:itemsForBeginningSession:atIndexPath: that returns the array of UIDragItem objects. This method will be used by the Table View to begin drag session with a given indexPath . We need to return the array of the UIDragItem that is not empty if we want to start the drag session . The UIDragItem is initialized by passing the NSDragItemProvider . The NSDragItemProvider is the data representation of the items we want to drag and drop to. It uses the UTIs (Uniform Type Identifiers to represent data such as Plain Text , Image , Contact Card and many others. It acts as a contract/promise between the drag source app and the drop destination app to handle and load those data based on those representations. Inside the method, we get the item from the board using the indexPath and convert it to the Data type using utf8 as the encoding. We initialize the NSItemProvider passing the data and set its type identifier to kUTTypePlainText . To be able to use the constant, you need to import MobileCoreServices at the top of your source file. Next, we initialize the UIDragItem with the item provider. Also, we set the localContext property to attach additional information to the drag item. Here, we assign the tuple containing the board, indexPath, and the table view which will be used later when we drop an item to different Table View . At last, we return the array containing the UIDragItem we created.

Try to build and run the app, then perform dragging of an item inside the board. Voila! You can initiate the dragging of an item around the screen. Surely, dropping the item won’t do anything right now. Let’s implement the dropping of an item next!

Dragging an item for first time!

Handle Dropping an Item in UITableView with UITableViewDropDelegate

Next, we are going to add the drop support to the Table View . It’s almost the same with adding drag support. We just need to set the Table View dropDelegate property to the BoardCollectionViewCell . Go to the BoardCollectionViewCell awakeFromNib method and add this at the bottom.

...

override func awakeFromNib() {

....

tableView.dropDelegate = self

}

...

The compiler will complain because the BoardCollectionViewCell has not implemented the UITableViewDropDelegate yet. So, go to the bottom of the file, and add the snippet code below as the extension.

Here is a brief summary of what the code do:

We implement the required method tableView:dropSessionDidUpdateSession:withDestinationIndexPath: . What this method does is telling the system how we want to consume the dropped item data via the UITableViewDragProposal in the specified indexPath when user dragging the item around the Table View . The UITableViewDragProposal accepts 3 types of operations, copy , move , or cancel . In our app case, we only want to move the data, so we return the UITableViewDragProposal with the move as the operation and set the intent to insertAtDestinationIndexPath . At last, we need to implement tableView:performDropWithCoordinator: . This method will be invoked when user lift the finger from the screen. Inside the method, we have the opportunity to load dragged item data representation from the coordinator’s session drag items. We also need to handle syncing of our datasource. Then, we ask the Table View to perform animations of the move operation. Remember that we can drag item between different Table Views. Inside the method, we asks the coordinator’s session if it has an item that conforms to the kUTTypePlainText . If yes, we load the object using the loadObjectOfClass method passing the NSString as the type. The system will perform the loading of the dragged item data and pass it into a closure. We use a switch conditional statement for the dragged item source indexPath and the session destination indexPath . If the source indexPath and destination indexPath exist, that means the user is dragging and dropping within the same Table View . Here, we just remove the source item from the board using the source indexPath, then insert the item using the destination indexPath. We then tell the Table View to reload the rows between the source and destination indexPath. If the sourceIndexPath is nil and the destination indexPath exists, that means the user is dragging and dropping between different Table View in same app. To handle this, we can retrieve the source table view, source IndexPath, and source board using the localContext from the drag session that we attach in UITableViewDragDelegate method. We remove the items from the source board using the source indexPath row and tell the source table view to delete the row. At last, we insert the item to the destination board using destination indexPath row and tell the destination table view to insert the row. The last case is when the destination and source indexPath are nil . This happens when user drag a text from a different app to our app. In this case we just need to insert the item into our destination board and tells the Table View to insert the item.

Try to build and run the app, you should be able to move the item around the boards using Drag & Drop! If you want more challenges, you can check the project GitHub repository to see how to handle the deletion of an item by dropping the item to custom UIView with UIDragDelegate .

Conclusion

Congratulations, you made it! We have finished building our Trello like app using the Drag & Drop API. The API is really simple and powerful to use and we only scratch the surface of the what the API can do here. There are many more features to explore, like creating a placeholder when dropping an item that will took time to load the data, drag and drop multiple items, and many other awesome features. So what are you waiting for? Let’s keep learning and build new things. Improve our world using technology to make it a better place for us to live. Stay calm and keep on 😘 Cocoa.