Animating Your Bar Chart With Combine Messages
By: Mark Lucking
Tags: Swift, Mobile, Animation, Programming, iOS

Animating Your Bar Chart With Combine Messages

Part 2 of “Linking Animations Together to Build Bar Charts in SwiftUI”

Native Swift UI animated bar chart

I wrote an article the other day in which I talked about building basic bar charts: “Linking Animations together to Build Bar Charts in SwiftUI.” I am not going to post the final code again, but I will show what we managed to build.

In short, it’s an animated, auto-coloured, data-refreshing bar chart. I kept it sweet and short, but couldn’t help but feel I wanted to do more.

Let’s do a new brief based on the bar chart theme. Here is a wish list:

Currently, the values are the labels. I want to add a second textual label.

Currently, I have no legends on either axis. Let’s try to add one.

I want to be able to dynamically remove bars leaving the others in place.

I want to be able to dynamically insert bars leaving the others in place.

I want to be able to present the bars sorted, largest first or last (visually, ideally).

I want to be able to change the colours used for bars (i.e., override the auto-colouring.)

Some of these things are easy, some less so — and at least one is going to be almost impossible — so let’s get stuck in.

I going to start with text labels since bar charts showing just values aren’t that useful. I want to add an axis label while I’m about it, too. We’re after this.

Animated bar chart with axis and labels

OK, it’s not perfect… the axis shouldn’t really be animated like that, but it’s a move forward. How did I do it? Obviously, it’s based on the code presented in the previous article, only I swapped the axis, added a text field as a second published object, and made a few minor changes to the layout code.

Bon, that’s half the wish list already — told you some of the wishes are not so difficult. What about the second half? To do that, I am going to use the Combine framework available in SwiftUI 1.0 and create a collection of actions with it.

var hideColumn = PassthroughSubject<String,Never>()

var showColumn = PassthroughSubject<String,Never>()

var changeColumn = PassthroughSubject<(String,String),Never>()

var changeFigure = PassthroughSubject<(String,Int),Never>()

And indeed, add code to match those actions and change our bar chart. Now I use a switch statement to test everything (SwiftUI 2.0 only).

The code will tell the bar chart to hide two columns, bring one back, change a couple of column names, and then change some of the values held against the grapes, pineapples, and peaches categories. Let’s watch it in action.

Animated bar chart with name changes, value changes, column changes

There is still work to do, but we’re making progress. A couple of outstanding issues:

When we hide a column, unless we remove it, it continues to take up space.

Obviously, if we hide and remove a column, then, unfortunately, there isn’t a means to insert it back — yet.

When we change the values of the bars, the moving of the index is not only very evident, it’s positively annoying.

Let’s keep going. Continuing on the same path, I can use the Combine framework to add three more actions to our code.

enum sortDirection {

case accending, decending

} var changeColour = PassthroughSubject<(String, Color),Never>()

var insertColumn = PassthroughSubject<(String,Bool),Never>()

var sortColumns = PassthroughSubject<sortDirection,Never>()

Although wait — this shows up a flaw in our design. You see, although you can easily insert columns and/or sort them, the current data model treats these two items as individual elements, strings and integers. The old code looked like the following:

final class Figures: ObservableObject {

@Published var text = ["Apples","Apricot","Bananas","Blueberry","Figs","Papaya","Pears","Pineapples","Peaches","Plums"]

@Published var numbers = [90,80,70,60,50,40,30,20,10]

static var shared = Figures()

}

This is code we need to refactor; obviously, this also impacts the rest of our code too, a little.

Given the numbers are now contained within a structure alongside the description, we can easily sort them. Let’s run through some more tests using our case statement.

case 0:

insertColumn.send(("Cherries",45))

case 1:

sortColumns.send(.accending)

case 2:

changeColour.send(("Cherries",.yellow))

The first click will tell our app to add a bar for cherries to the chart. The second click will tell it to sort the bar chart numerically, and the third will manually change the colour of the new cherries bar.

Animated bar chart showing columns added, columns sorted, column colour changed

As before, it isn’t perfect. Ideally, we wanted to sort the bars on-screen, and we’ve still got that axis moving across the screen. Although that said, I fear I have again run out of time. We did manage to make an initial implementation of all six of the items on the wish list. Pretty good going.

Here is the complete code for the project for you to look through and play with. I may yet look into doing some more charts, different types of charts. Follow me and keep in the loop.

Homework: The sort is purely numeric, but you could create one that was alphabetic. The sort simply sorts ascending values, so you could add a descending option. The labels on the axis need fixing so that they stop moving about wildly. An on-screen sort is maybe possible, although I am not sure how yet — maybe you can find a solution. The colours are linked to the values, and it would be better to link them to the text labels.

Keep calm, keep coding.