Simplifying animations using batch updates on iOS

Blog post

A lot of our work as iOS developers comes down to presenting data. In an effort to simplify the process, we often separate the data into small, workable chunks that we present in views called cells. We then present cells using table view or collection view, depending on our needs.

To update the data we can call reloadData()on the containing view. The problem with this approach is that it does not animate the changes.

To do the animations we need to manually determine which items need to be animated. This means calculating which items have changed and calling the following methods:

Screenshot 2023-04-27 at 16.03.49.png

What if we could skip this part and use something as simple as reloadData() and be sure our cells are animating nicely? We’ll try to accomplish just that!

Without animations

Let’s start with a simple example. We all love Bruce Willis, so let’s make an app showing a list of his most memorable movies. To simulate content change I will add an option to delete movies and an option to reset the whole content to its original state. The full example is available at: https://github.com/Rep2/BatchUpdatesExample_Swift.git

I will use a view controller — presenter architecture to better demonstrate the problem.

The view controller presents movies and passes user actions to its event handler, i.e. presenter. The presenter implements the event handler protocol and updates the view when needed.

Screenshot 2023-04-27 at 16.03.59.pngBasic implementation of the MovieViewController takes an array of movies and reloads the table view, while Async call is needed if the method gets called from the background thread.Screenshot 2023-04-27 at 16.04.08.pngWith manual animations

Let’s try to implement animations using the methods mentioned above.

To delete a movie we can use func deleteItems(at: [IndexPath]) In order to access it, we need to expose it in the MovieViewController protocol.

But what about resetting the list? We could keep track of all deleted movies and then use func insertItems(at: [IndexPath]) to put them all back in. Again, we need to expose the method so that it can be called by the presenter.

We need to add two new methods to the MovieViewController protocol:Screenshot 2023-04-27 at 16.06.23.pngand change the presenter so that it tracks which items it needs to animate:Screenshot 2023-04-27 at 16.06.31.png

Quite a bit of work, but we got our animations. The results are shown below, without animations on the left, and with animations on the right.

1_G90Wkg9WpMnN39NCHde-2A.gif

The main problem is, in my opinion, that we need to expose the inner implementation of view controllers data presentation. We are clearly stating that we present data in rows and we leave it to the presenter to animate these rows.

Finally, using batch updates

Is there a better way to do this? This is where batch updates come into play. Instead of manually determining which animations need to be done, ideally, we should try to find a way to get the work done for us

Let’s take a step back in the whole process. In the most basic terms, what are we doing? We are switching one data set with another. Whether we are deleting an item, inserting new ones or initially presenting data, we are switching one data set with another one.

If we can compare these sets, we can calculate which changes were made and we can animate those changes.

To calculate changes I use the function below. It calculates the difference between two arrays containing values implementing the Equatable protocol. The complexity of the function is O(n²) and it can be improved if the values also implement the Comparable protocol. So far we haven’t detected any performance issues while testing, but the function could probably be improved so please comment down bellow.Screenshot 2023-04-27 at 16.06.50.png

The function returns the BatchUpdates structure used to perform animations. The extensions bellow implement animations for the UICollectionView and UITableView using given the BatchUpdates structure.Screenshot 2023-04-27 at 16.06.58.pngNow, instead of manually inserting and deleting items we can just call present data and the view will animate the changes by itself. In fact, we can use the same code we used in the first example. The only thing that we need to change is the present method.

Screenshot 2023-04-27 at 16.07.05.png

We even got rid of the async call. Even better, this method works on any data change, as long as the elements we present are comparable. No matter what operation we do or how we change the data we can simply call the present method.

Thank you for reading, I hope you liked the post and found it useful. Feel free to comment or add suggestions for improvements.

Similar blog posts

Get in touch