A better way to display an empty indicator when the table view data source is empty
Separating the logic inside the table view’s data source will make your code reusable and easier to maintain
I don’t need to have expert knowledge in user experience to realize that users don’t like to be left in ambiguity. Users require an immediate and clear visual indicator of the current state of the product they are using. In real life, when there is nothing on the table, we leave it tiny like that. But in an app, we want to show some custom content when the table view is empty to tell the user about that.
If you work in a team with designers, it is their responsibility to give you, the developer, instructions on what to display if there isn’t any data to display. However, it is up to you to decide the actual implementation of the portion of your table view that is not seen by the user.
UITableView usage by concept is very straightforward. You create a table view, assign its
.delegate property to the view controller or view that owns the table view. Then you can use
cellForRowAt provided by
UITableViewDataSource to determine the behavior of your table view.
For example, we perform the logic in
- If the data is empty, return one cell.
- Otherwise, return the count of data.
The same logic in
- If the data is empty, return our custom empty table view cell.
- Otherwise, return the normal table view cell to display data.
Writing logic in one place works, but it usually makes the code hard to maintain and not reusable. If we want to reuse the logic in multiple places, we will need to copy and paste the exact same logic. Imagine the designer rolled out a re-design of the empty table view cell, we will have to find and change the logic at all places.
case true: and
case false: look ugly? We have to always remember to keep both switch statements in sync in both methods.
UITableViewDataSource deserve to be independent. After all, all the data source does is provide instructions on how the table view is rendered. Remember how we initialize a table view, sometimes even blindly? We assign the
.dataSource attribute, and use the view controller as the only data source to the table view. What if we would change the data source on the fly, based on the type of the data to display?
For example, if we want to show a list of users, we provde the table view with a hypothetical user data source. If we want to show an empty table view cell, we can just feed the table view a different
.dataSource, then do a reload to refresh the cells. if later the backend tells us the user list is no longer empty, we could simply swap the data source again.
In theory, this sounds feasible. In reality, this is not difficult to implement.
We will need a specialized table view subclass to manage the data source. I will call it:
- The dependencies for the empty table view are injected during construction.
- It requires an
EmptyTableViewDelegate, a regular
UITableViewDataSource, and a regular
UITableViewDelegateto work properly.
- Both table view delegates can be omitted if you do not intend to interact with the table view.
Then we specify how empty table view data is displayed by constructing a standalone
- If you have a unified design that’s reused over the code base, it is often a good idea to enclose them in a struct to enforce the structure of the model.
EmptyTableViewCellModelis used as the data for our empty table view cell. In my case, we are displaying a default image, a default string of text, and occasionally an action button triggers the corresponding delegate method.
EmptyTableViewCellConfiguratoris a step further in making the table view cell reusable. For example, if the cells have the same UI elements such as an image, a label, and a button, but a different layout, we can create a cell configurator to handle adding layout constraints to the UI elements. The table view cell only creates and owns the subviews. So that the table view cell can be reused in multiple layouts.
Now the setup is complete, we can add two extension methods to show or hide the empty cell.
showEmptyCell(), both table view delegate and data source are changed from regular delegate and data source to empty delegate (if you injected during construction) and data source.
hideEmptyCell(), reset the table view’s delegate and data source to regular.
What’s left to do is to build your empty table view cell UI: