Declarative charting and visualization to use with SwiftUI

Overview

Chart

Declarative charting and visualization to use with SwiftUI

[work in progress]

The package is in open development with a goal of making a declarative charting library for use with SwiftUI. If you found this looking for a charting library you can use immediately, there are several packages immediately available that may provide what you need:

Inspiration

The goal of this library is to provide a declarative chart view that can be used directly as a SwiftUI view, and following the pattern of describing the charts akin to what's been done with Observable's Plot or Vega-lite.

The core idea being describing the views not in terms of the kind of chart, but in terms of the kinds of marks used to represent the data you have, describing visually. This core idea is explored in quite a bit of detail within the following papers:

Goals

The excellent implementations already out there that do this all do so with dynamic languages (primarily Javascript or TypeScript), but I'd like to be able to build the same kinds of charts using that kind of grammar built to work inside SwiftUI, and leveraging the Swift language and standard library.

Open Development, Open Source

The project is in development in the open, with design, development notes, and documentation included within this repository. The project itself is open source, provided under the MIT license - collaborators are very welcome. As a starting point, feel free to ask questions within the Github Discussions for this repository, and read through the contributing guide.

Comments
  • Axis label

    Axis label

    resolves #29

    • [x] basic labels rendering
    • [x] axis rect needs to be enlarged to accommodate label, if it's not empty
    • [x] tick labels are "always on" by default - need a mechanism to control on/off for them, and use that value to determine a base level of offset for the axis label
    • [x] need to verify that the rotation effort works as expected for Y axis labels
    • [x] consider passing a Text view instead of a string for the label - both to allow more programmatic control, and that uses GraphicsContext.ResolvedSymbol for the drawing
    • [x] consider allowing a font declaration to control the sizing of the tick labels
    • [x] consider allowing a font declaration to control the sizing of the axis label
    opened by heckj 1
  • open question - should inset values be calculated from the labels.

    open question - should inset values be calculated from the labels.

    You can see the change when the labels are displayed for the leading edge: testXAxisBottom 1

    vs

    testAxisOptionsDefault 1

    (debugging shows the areas identified more easily): testAxisOptionsDefaultDebug 1

    That small gap between the axis and the chart area is inset calculated from the height and width of the labels of the scales, and set automatically so that there will be sufficient space to display the labels without clipping them, for the case when there's no margin.

    So maybe rather than insets, these adjustments should be made to the margins of the chart, which would still keep the labels visible and clear, but not "loosen up" the chart from the axis.

    opened by heckj 1
  • reset where labels are placed for tick options inner

    reset where labels are placed for tick options inner

    I made the first cut of "inner" oriented ticks having the labels match the orientation of the ticks, but that looks pretty poor:

    Chart(margin: 10, inset: 10, _options: [.all]) {
        LineMark(data: self.data,
                 x: QuantitativeVisualChannel(\.xValue),
                 y: QuantitativeVisualChannel(\.yValue))
            .xAxis(tickOrientation: .inner)
            .yAxis(tickOrientation: .inner)
    }
    

    results: testAxisOptionsInner 1

    I think it'd be more sane to stash the labels to the outside of the chart, rather than intruding into the chart area.

    Likewise, I think we should inset the rule when the tick orientation is inner so that it all draws within the area that we defined for the axis to be displayed. We shouldn't critically need an inset value there to keep things looking sane, which we currently do.

    opened by heckj 1
  • expand margin from default when axis is defined

    expand margin from default when axis is defined

    Currently margin has a default value of 0, and when you define an axis, there's not enough space for the labels to be fully displayed within the canvas area due to them being centered.

    One option is to inset the top and bottom, but I think it would look more consistent to keep them centered and to infer a bit of extra margin based on the space needed for the largest or tallest labels.

    For example:

    Chart(_options: [.all]) {
        LineMark(data: middleData,
                 x: QuantitativeVisualChannel(\.xValue),
                 y: QuantitativeVisualChannel(\.yValue))
            .yAxis()
    }
    

    results today in:

    testLineChartYAxisSimpleRendering 1

    If we take the max of the Y labels and add that to the margin on the vertical sides, we'll have enough space to display the labels, even outside the "axis" area.

    opened by heckj 1
  • drawing coordinates need fixin' - GraphicsContext

    drawing coordinates need fixin' - GraphicsContext

    The GraphicsContext I'm using for drawing into SwiftUI Canvas views has the origin in the upper left, growing down and to the right. So far I've been writing code with the origin in the lower left - so all the various drawing calculations need to be tweaked and fixed to be sane.

    I also need to triple check that GraphicsContext drawing coordinates are consistent across the platforms - so that I can accommodate any insanity for macOS vs. iOS drawing, which historically had different drawing coordinates.

    opened by heckj 1
  • Set up time-to-compute and render benchmarks

    Set up time-to-compute and render benchmarks

    The process of rendering a chart could be impacted by different implementations of how a Mark's declaration is set up, and how it is used to render as a template against some data to render a series of glyphs or shapes onto a Canvas.

    It's worthwhile to set up a benchmark for the pathological cases of huge charts - 10,000 to 1_000_000 (or more) data points - being rendered into charts, and the time it takes to spend on the data processing that happens through the channel, picking the visual property, any potential transformations, and scaling the resulting value into the ranges appropriate for the canvas, categories, etc.

    Ideally the goal would be to get some baselines as we development, and most importantly ensure that we didn't accidentally enable any n^2 algorithms (or worse) when processing a chart into a visual end-result.

    [ ] A baseline that shows amount of time vs. size (# of values) of the data set being processed would be a good starting point.

    opened by heckj 1
  • domain calculations for scales are off for default imports of data

    domain calculations for scales are off for default imports of data

    While setting up the examples, I kicked in

    Chart(_options: [.all]) {
        PointMark(data: SFTemps.provideData(), x: QuantitativeVisualChannel(\SFTemps.high), y: QuantitativeVisualChannel(\SFTemps.low))
            .xAxis()
            .yAxis()
    

    which used the SF_temps fixture data and presented the data as such: Screen Shot 2022-06-02 at 2 39 17 PM

    It's pretty clear from this example the the lower end of the domain isn't getting set appropriately, potentially stemming from a problem with the Scale package and the default "nice" values - which in this case (I'd think) should be defaulting to a lower value of 0, but aren't - even when the data sinks well below the bottom value.

    opened by heckj 0
  • loading of CSV files for benchmark and examples

    loading of CSV files for benchmark and examples

    go through the CSV reading options and pick out something to load and process CSV files.

    • https://github.com/dehesa/CodableCSV (variety of modes)
    • https://github.com/yaslab/CSV.swift (lazy reader)
    • https://github.com/swiftcsv/SwiftCSV (all in memory)

    Ideally I'd like something with decodable support to get from CSV into struct w/ Int, Double, etc, and I'm kind of leaning towards https://github.com/dehesa/CodableCSV with just an initial glance through the offerings listed up in Swift Package Index.

    I'd like to use this both in the benchmarking code to load larger data files for image generation, but also to use with some examples since CSV is a super-common data format.

    An alternative from Apple is maybe the TabularData framework, but it doesn't appear to easily map things into structs that you specify, instead capturing the pieces into a generic DataFrame - and I'm not clear how to get from dataframe to specific types for each property/column.

    opened by heckj 0
  • fix mark rendering now that scales have `reverse`

    fix mark rendering now that scales have `reverse`

    initial development did crazy math to deal with inverted Y coordinates - now that scales have a reverse mode, update the drawing routines to use a reversed scale instead of the crazy extra math in the renderer.

    opened by heckj 0
  • Axis modifiers

    Axis modifiers

    The size of the label (font used) and the size of ticks (again, font used) are hardcoded. This, in particular, seems like an excellent place to enable an Axis specific modifier to update those values

    opened by heckj 0
  • define mechanism to control which tick values appear on an axis

    define mechanism to control which tick values appear on an axis

    Axis has requestedTickLabels, with an array of values, but today that's not used.

    • [ ] Wire that through so that - if provided - these values are used to drive the specific tick labels and locations
    • [ ] rework Axis and the .xAxis() and .yAxis() modifiers to default to nil for those values, and use that differently from an empty list, the empty list being "don't show any ticks or labels"
    opened by heckj 0
  • VisualChannel doesn't deal with a mapping to an optional property

    VisualChannel doesn't deal with a mapping to an optional property

    Currently Chart's visualChannel setup doesn't handle optional values within KeyPaths. For example, \.something that's a Double is fine, but one that's Double? isn't.

    While messing around with CodableCSV, I found it useful to want to reference an optional property, with the goal of skipping the data if it returned nil, but that seems... to not be working at the moment.

    opened by heckj 0
  • look at collapsing/renaming the visual channels

    look at collapsing/renaming the visual channels

    Right now there's 3 different visual channels in the public API, which is rather confusing as well as cumbersome:

    • continuous
    • discrete (point)
    • discrete (band)

    And that's not yet counting adding any histogram/binning capability to convert a continuous value into a discrete collection, continuous or discrete mechanisms that map values into color ranges, or handling the odd "tick value" complexities of Dates as a continuous or discrete value.

    In my ideal world, the public API would just be VisualChannel, but the signatures on the channels themselves make that hard to navigate. Continuous resolves internally to Double, and discrete uses String as a placeholder, but is ultimately more about a collection of hashable entities that can be distinguished from each other.

    The computations to make a band from a discrete channel are specific to spacing and depend entirely on the size of the range, but rather feel like its additional complexity in the scales OutputType that I could be moving up the stack, away from Scale itself and having a specific Scale type that returns a Band to a means of computing a band on a discrete scale and not having a separate type.

    That, in turn, would collapse the number of types down to two - at which point potentially renaming them as ContinuousChannel and DiscreteChannel might be more than sufficient.

    The specific type that I need, and what I do with it, is defined by the Mark that's using the channel - so Bar is currently using this scale that outputs a Band, but I found for labelling the bars, I ended up needing that middle value that would be returned by PointScale as well, which make the whole Band thing feel excessively complicated.

    opened by heckj 0
Owner
null
SwiftChart - A simple line and area charting library for iOS.

SwiftChart A simple line and area charting library for iOS. ?? Line and area charts ?? Multiple series ?? Partially filled series ?? Works with signed

Giampaolo Bellavite 1k Jan 2, 2023
iOS-based charting library for both line and bar graphs.

JBChartView Introducing JBChartView - Jawbone's iOS-based charting library for both line and bar graphs. It is easy to set-up, and highly customizable

Jawbone 3.8k Jan 1, 2023
A charting library to visualize and interact with a vector map on iOS. It's like Geochart but for iOS!

FSInteractiveMap A charting library to visualize data on a map. It's like geochart but for iOS! The idea behind this library is to load a SVG file of

Arthur 544 Dec 30, 2022
Elegant Line Graphs for iOS. (Charting library)

BEMSimpleLineGraph BEMSimpleLineGraph makes it easy to create and customize line graphs for iOS. BEMSimpleLineGraph is a charting library that makes i

Boris Emorine 2.7k Dec 26, 2022
SwiftCharts - Easy to use and highly customizable charts library for iOS

SwiftCharts Easy to use and highly customizable charts library for iOS Features: Bars - plain, stacked, grouped, horizontal, vertical Scatter Lines (s

Ivan Schütz 2.4k Jan 4, 2023
Dr-Charts Easy to use, customizable and interactive charts library for iOS in Objective-C

dr-charts Easy to use, customizable and interactive charts library for iOS in Objective-C Features: Multiple chart types Line / Multiple lines / Lines

Zomato 93 Oct 10, 2022
Easy to use and highly customizable pie charts library for iOS

PieCharts Easy to use and highly customizable pie charts library for iOS Swift 4.2, iOS 8+ Video Features: Customizable slices Add overlays using simp

null 503 Dec 6, 2022
This is pie chart that is very easy to use and customizable design.

CSPieChart Example To run the example project, clone the repo, and run pod install from the Example directory first. Requirements Installation CSPieCh

iOSCS 40 Nov 29, 2022
FLCharts: Easy to use and highly customizable charts library for iOS

FLCharts Requirements Xcode 11 / Swift 5 iOS >= 11.0 Installation FLCharts is av

Francesco Leoni 250 Dec 26, 2022
Easy to use and highly customizable pie charts library for iOS

PieCharts Easy to use and highly customizable pie charts library for iOS Swift 4.2, iOS 8+ Video Features: Customizable slices Add overlays using simp

null 503 Dec 6, 2022
ANDLineChartView is easy to use view-based class for displaying animated line chart.

ANDLineChartView for iOS ANDLineChartView is easy to use view-based class for displaying animated line chart. Usage API is simple. Just implement foll

Andrzej Naglik 421 Dec 11, 2022
Easy to use Spider (Radar) Chart library for iOS written in Swift.

DDSpiderChart Easy to use Spider (Radar) Chart library for iOS written in Swift. Requirements iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+ Xcode

Deniz Adalar 82 Nov 14, 2022
MSBBarChart is an easy to use bar chart library for iOS

MSBBarChart MSBBarChart is an easy to use bar chart library for iOS. Usage if you want to hide label above bar barChart.setOptions([.isHiddenLabelAbov

misyobun 45 May 3, 2022
SwiftUICharts - ChartView made in SwiftUI

SwiftUICharts Swift package for displaying charts effortlessly. V2 Beta 1 is released, if you would like to try it out you can find a demo project her

Andras Samu 4.7k Jan 1, 2023
🎉 SwiftUI stock charts for iOS

SwiftUI Stock Charts Display interactive stock charts easily ?? Instalation In Xcode go to File -> Swift packages -> Add package dependency Copy and p

Dennis Concepción Martín 94 Dec 26, 2022
🎉 SwiftUI stock charts for iOS

?? SwiftUI stock charts for iOS

Dennis Concepción Martín 94 Dec 26, 2022
SwiftUI Charts with custom styles

SwiftUI Charts Build custom charts with SwiftUI Styles Line Chart(data: [0.1, 0.3, 0.2, 0.5, 0.4, 0.9, 0.1]) .chartStyle( LineChartStyle(.

SpaceNation 609 Jan 6, 2023
SwiftUI library to easily render diagrams given a tree of objects. Similar to ring chart, sunburst chart, multilevel pie chart.

Swift Sunburst Diagram Sunburst diagram is a library written with SwiftUI to easily render diagrams given a tree of objects. Similar to ring chart, su

Ludovic Landry 494 Dec 19, 2022