Skip to content

Markers, overlays, tooltips, etc.

Ivan Schütz edited this page May 4, 2017 · 5 revisions

SwiftCharts doesn't have anything specific for markers or similar because it doesn't need it. We can display any kind of views on the chart as long as we have a domain position that can be referenced. These views can represent actual data points or be markers - it doesn't matter.

The easiest and most flexible way to produce markers is using ChartPointsViewsLayer, which is the same generic layer that we use to display chart points as UIViews. The basic function of this layer is to transform chart points in UIViews that will be placed in chart.

Say that we have a line and want to display some circles over its data points. We don't have to touch the line layer - we just create a ChartPointsViewsLayer dedicated to generate these circles, to which we pass the same chart points which we passed to the line layer:

let viewGenerator = {(chartPointModel: ChartPointLayerModel, layer: ChartPointsViewsLayer, chart: Chart) -> UIView? in
    let center = chartPointModel.screenLoc
    // create a UIView, customize however you want, add labels, borders, images, animations, etc. to it.
    // you also can show it at a different position than center
    return myView
}
let myCirclesLayer = ChartPointsViewsLayer(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, chartPoints: chartPoints, viewGenerator: viewGenerator, mode: .translate)

ChartPointLayerModel contains the screen location as well as the chart point, i.e. the model data, so we can add logic to the generator to generate different types of markers depending on the model data, or return nil if we don't want to show a marker for a particular chart point.

Finally, we pass this layer to the chart, after the line layer, in order for the points to appear in front of it:

let chart = Chart(
    frame: chartFrame,
    innerFrame: innerFrame,
    settings: chartSettings,
    layers: [
        //...
        myLineLayer,
        myCirclesLayer
    ]
)

What's also very nice about this, is that since our markers are decoupled from the line layer, we can reuse them on any other kind of layer.

Layer specific markers

Some layers, depending on how they are implemented, may offer functionality to show markers on their own. Though in most cases the idea is to just notify the user of the layer that an interaction occurred, passing as much data as possible to describe the event, which allows to show easily a marker, but without the layer doing this itself. Some examples:

  • ChartPointsLineTrackerLayer has a positionUpdateHandler closure, to which it passes a ChartTrackerSelectedChartPoint instance, which contains the screen location of the intersection, line model where it occurred, etc. TrackerExample uses this closure to show markers, which it manages on its own. In SwiftCharts pre-0.6 ChartPointsLineTrackerLayer showed these markers itself but this turns to be a little inflexible so it was decided to remove them from the responsibilities of the layer.

  • ChartStackedBarsLayer has a tapHandler to which it passes the selected bar, stack frame view, model data, etc. which allows the user of the layer to position an overlay over it when the bar is tapped. We can see this in use in StackedBarsExample.

Dynamic markers

SwiftCharts right now doesn't have capabilities to add chart points (or markers & similar, which as was explained before are handled in a similar way) dynamically, but not everything is lost! - we can add dynamic overlays to the chart with a little improvisation, using directly its content views. As long as we know where to position the overlays (i.e. have an x and y axis at hand from which we can get the screen coordinates) and manage them ourselves everything is fine.

An example of this can be found in AreasExample. In this example, we show an info bubble over a chart point when we tap on it. To get this working, we embed the generation of the info bubbles in the closure used to generate the tappable circles. This works because the tap handler is also a closure, so everything is contained in the circle generator closure which has access to the layer and the chart, and with it to the needed axes and content view where we add the info bubbles. We also manage the bubbles ourselves, i.e. have an external popup array where we store the current bubbles and clear it when we show a new info bubble or on zooming/panning.

Ideally we should be able to add dynamic overlays as "official" chart inhabitants (i.e. chart points), such that they are affected by the chart's transform - we don't necessarily want the info bubbles disappearing on pan/zoom. But this requires the capability to add chart points dynamically, which is not implemented yet. Don't despair though! It's one of the next features that will be implemented (where "next" means it has prio, as soon as there's time/resources). The task for this can be found here.