willdale / swiftuicharts Goto Github PK
View Code? Open in Web Editor NEWA charts / plotting library for SwiftUI. Works on macOS, iOS, watchOS, and tvOS and has accessibility features built in.
License: MIT License
A charts / plotting library for SwiftUI. Works on macOS, iOS, watchOS, and tvOS and has accessibility features built in.
License: MIT License
I think the problem is in YAxisLabelCell.swift
.onAppear { chartData.viewData.xAxisLabelHeights.append(width) }
changing width to height solved the problem for me.
Because the corner radius is set before the stretching is applied, longer bars have more corner radius than shorter bars.
Not sure how to approach this, maybe stretch first, then apply the corner radius (if I tried this, I didn't get any rectangle). Other approach might be to apply a different corner radius for x and y, and not use a circle to make the corner, but use an ellipse?
Screenshot attached for clarity:
I'm getting an error when I install SwiftUICharts which is:
"the Package.resolved file is most likely severely out-of-date and is preventing correct resolution; delete the resolved file and try again."
Please, provide an API to
customize markerType, especially color and stroke width.
customize DotStyle - ideally, accept view builder. At least change the way building dot. Now users could specify dot color and stroke. But it would be more flexible to set background color and radius and foreground color and radius.
Thanks
Is there a way to do multiline charts?
This library is great! I try to use it in a current project but have some issues.
I tried to use the FloatingInfoBox with the MultiLineChart, but it made some trouble for me. For some reason it didn't show up, the Touch Overlay however worked like expected. Am I doing something wrong or is there an issue in the library? Here is my code:
MultiLineChart(chartData: MultiLineChartData(dataSets:
MultiLineDataSet(dataSets: [
LineDataSet(
dataPoints:DataService.getData().monthlyWithoutIncrease,
style: LineStyle(lineColour: ColourStyle(colour: .gray), lineType: .curvedLine)
),
LineDataSet(
dataPoints:DataService.getData().monthlyWithIncrease,
style: LineStyle(lineColour: ColourStyle(colour: Color(hex: "#54FFF2")), lineType: .curvedLine)
)
])))
.touchOverlay(chartData: MultiLineChartData(dataSets: MultiLineDataSet(dataSets: [LineDataSet(
dataPoints:DataService.getData().monthlyWithIncrease
)])))
.floatingInfoBox(chartData: LineChartData(dataSets: LineDataSet(dataPoints: DataService.getData().monthlyWithIncrease)))
I noticed in your demo project for a ranged bar chart, you hard code the xAxisLabels xAxisLabels: ["00:00", "12:00", "00:00"]
but I would like to load them dynamically from my data points as in xAxisLabelsFrom : .dataPoint(rotation: .degrees(0))
but they aren't showing up. Am I doing something wrong or is this not possible yet? Here is a gist of my code, apologies for the cruft, work in progress...
https://gist.github.com/gesabo/9dc3758cd167544c953953e70a31b4b6
I couldn't get once scenario to compile due to missing 'LineChartData'.
Section(header: Text("Line Chart")) {
// NavigationLink(destination: LineChartDemoView()) {
// Text("Line Chart - Week")
// }
When pie charts are fed values < 0 (e.g. decimals representing % summing to 1.0) the touch info box value is rounded to an int 0. Fixed by just multiplying by 100 in the case of a percentage in the view code.
Would be nice to be able to: 1) Feed in a specifier to allow for formatting of the info box number in the intended format.
Unrelated - would be nice to be able to label the slices in a way that did not require touch to display the slice value.
Food for thought.
Is it possible to draw a custom Legend for a bar chart?
I have many bars but only two conditions and want to place only those two conditions in a legend below the chart.
What I get is a Legend with one Item for each bar.
In the Docs it says that legends is part of BarChartData but I can't add id there.
When I add a line chart with the touchoverlay enabled into a scrollview, I find that I can only scroll when I start the scroll gesture outside of the chart. Is this expected behavior?
This is be a very useful feature for chart
import SwiftUI
import SwiftUICharts
struct ProgressMain: View {
let data : ChartData = weekOfData()
var body: some View {
LineChart()
.xAxisGrid()
.yAxisGrid()
.xAxisLabels()
.yAxisLabels()
.environmentObject(data)
.frame(height: 200, alignment: .center)
.padding()
}
}
extension ProgressMain {
static func weekOfData() -> ChartData {
let data : [ChartDataPoint] = [
ChartDataPoint(value: 20, xAxisLabel: "Jun 18"),
ChartDataPoint(value: 90),
ChartDataPoint(value: 100),
ChartDataPoint(value: 75),
ChartDataPoint(value: 160),
ChartDataPoint(value: 110),
ChartDataPoint(value: 90, xAxisLabel: "Sep 20")
]
let gridStyle = GridStyle(numberOfLines : 6,
lineColour : Color(.lightGray).opacity(0.25),
dash : [4])
let chartStyle = ChartStyle(xAxisGridStyle : gridStyle,
yAxisGridStyle : gridStyle,
yAxisNumberOfLabels : 1)
let lineStyle = LineStyle(colour : .accent,
lineType : .line,
strokeStyle : StrokeStyle(lineWidth: 3, lineCap: .round, lineJoin: .round))
return ChartData(dataPoints : data,
chartStyle : chartStyle,
lineStyle : lineStyle)
}
}
Would be great if we could have a picker or something similar to be able to filter the data set.
Sometimes it's good to display the data table under the graph. Is there a way to achieve this with this library?
I am thinking of doing a chart like:
I could not find an example at https://github.com/willdale/SwiftUICharts-Demo nor in the README. Is this currently possible?
While using the library in my app, I noticed the Y Labels were odd. I investigated the demo add to see if I could reproduce the error and I noticed the following in StackedBarChartDemoView.swift
:
For a given MultiBarDataSet
, we can see the labels for each point just fine, but the overall Y Label has a wrong value. For instance, look at this code:
MultiBarDataSet(dataPoints: [
MultiBarChartDataPoint(value: 100, description: "Q1 Sales", group: Group.england.data),
MultiBarChartDataPoint(value: 500, description: "Q1 Sales", group: Group.scotland.data),
MultiBarChartDataPoint(value: 300, description: "Q1 Sales", group: Group.wales.data),
], setTitle: "Q1"),
When I select the labels, I can see 100, 500 and 300 just fine:
But notice that if we sum 100 + 500 + 300, we get 900 for Q1, which does not match the chart above. For Q4, we should sum 400 + 800 + 200 = 1400, which definitely does not match the max Y Label of 800 there.
FWIW I reviewed wikipedia for Bar Charts and a random article to see if I was misunderstanding what a stacked bar chart should do, but I think I am understanding it correctly.
This was on the demo repo on commit 75b2b878ad908b706eb8d6c10a8ecd7cef667af5 from Wed Mar 31 09:55:27 2021 +0100. I had commented a line with topLine: .maximum(of: 900)
, but you can see the same behaviour with this line enabled too.
My enter view refreshes on @State
change except the chart.
@State var component: Calendar.Component = .month
@State var periodCount: Int = 0
The chart does not refresh until I drag on it.
LineChart()
.touchOverlay()
.xAxisGrid()
.yAxisGrid()
.xAxisLabels()
.yAxisLabels()
.environmentObject(
ChartData(dataPoints : chartDataPoints(periodChange: periodCount, periodComponent: component) )
)
.frame(height: horizontalSizeClass == .compact ? 200 : 250)
.padding()
Please fix this.
Thanks so much for this amazing SwiftUI library.
Issue updating to v2.6.
Hi There,
I just wanted to say this is the best SwiftUI Chart module I've used so far. Thanks for sharing.
I am using a FilledLineChart which is wrapped inside of a navigationView. Once I use the .averageLine. The whole view starts animating from top left corner when it appears the first time. I am not sure if I made any sense but the video attached shows the behavior.
FilledLineChart(chartData: chartData)
.touchOverlay(chartData: chartData, specifier: "%.0f")
.averageLine(chartData: chartData,strokeStyle: StrokeStyle(lineWidth: 1, dash: [5,10]))
.yAxisGrid(chartData: chartData)
.yAxisLabels(chartData: chartData)
.infoBox(chartData: chartData)
.floatingInfoBox(chartData: chartData)
.headerBox(chartData: chartData)
.legends(chartData: chartData, columns: [GridItem(.flexible()), GridItem(.flexible())])
.frame(minWidth: 150, maxWidth: 900, minHeight: 350, idealHeight: 350, maxHeight: 400, alignment: .center)
.padding()
Hi, I am using your library and found the bug on xAxisLabels
at LineChartData
: in case of data set like this [1,3,4,4,4], three last entries would be stacked to the end. To fix this it is better to change it to
case .chartData:
if let labelArray = self.xAxisLabels {
HStack(spacing: 0) {
ForEach(labelArray.indices, id: \.self) { index in
YAxisChartDataCell(chartData: self, label: labelArray[index])
.foregroundColor(self.chartStyle.xAxisLabelColour)
.accessibilityLabel(Text("X Axis Label"))
.accessibilityValue(Text("\(labelArray[index])"))
if index != labelArray.count - 1 {
Spacer()
.frame(minWidth: 0, maxWidth: 500)
}
}
}
.padding(.horizontal, -4)
}
}
P.S. sorry for posting it as an issue and not PR.
Much of this library has docstrings. It would be nice to be able to see those presented nicely in a docs website.
The actual publishing of that website for new versions of the library (using e.g. GitHub actions) can be a different piece of work
Hi,
first of all: Great Library! It's by far the most flexible out there in pure SwiftUI!
I ran into one issue with the bar chart, that is hopefully easy to fix although I didn't find the right place, yet.
If you have many data points the bars extend the space of the diagram. You can transform the bars as such by giving it a bar width <1 but that leads to huge spaces between the bars (of course). Is there a way to make the entire bars "thinner"?
Examples
Kind regards!
Same code as this issue except more data points added.
I'd like to be able to set the first and last x-axis labels and keep them the same size as the y-axis labels.
If labels, legends or other string values can be modified on view layer, not the data model layer. Users may have ability to modify chart view to adapt their screen.
Also when I wanted to add price unit, near my touch overlay data but in this structure it is not easy.
I think there may be some improvements to move some properties to view layer and have a simple data model layer.
It seems that the y-axis values are automatically generated to fall within the minimum and maximum values in the data points. Is it possible to set these values independently of the data? Specifically, I'd like to set the minimum to zero.
My Xcode says it's iOS 14.0.
I was hoping for iOS 13.0 for my iPhone 6s Plus.
I have a chart where i stack 5 different values. On the demo app, I can see all 3 legends being displayed on the graph (England, Wales & Scotland). But when I try to adapt to my data, I can only show 2 of the 5 in the legend.
Note: This is the legend only, the stacked graph is showing all 5 correctly.
For some reason 2.6 is not pulling in Xcode via swift package manager. No matter whether I reset the package cache and/or force update to latest version. Any ideas?
Just deleted the dependency and re-added - still get 2.5
Originally posted by @kennedycraig in #64 (comment)
When you are wanting to do a stacked bar chart with a grouping of 2, the chart returns "No Data"
Is it somehow possible to set the x and yAxisTitleColor?
The label text truncates, makes it a little harder to read at a glance as users would have to tap each bar to see the full text. Would be great to have an option to display X-axis labels rotated at n-degrees and showing full text (no truncating)
Working with 2 data sets that should be displayed with a bar chart the grouped chart is perfect. Is it possible with the current setup to display a bar chart with a line chart overlaid?
Environment: SwiftUI via Xcode 12.4
One band-aid approach is to launch another LineChart graph in place of the original for the new data.
This works; but is totally inefficient for multiple sets of data; and I still can't re-render already-rendered LineCharts().
Which means I have to have a LineChart() for EACH set of chartData.
The data is loaded from a class object and imported via @ObservedObject var dataSource = DataStuff()'s Published var.
Here's the code snippet with two different data sources:
...
VStack {
if dataSource.chartData != nil {
if graphToggle {
LineChart()
.touchOverlay(specifier: "%.2f")
.yAxisGrid()
.xAxisLabels()
.yAxisLabels()
.headerBox()
.legends()
.environmentObject(dataSource.chartData!)
.frame(minWidth: 300, maxWidth: 900, minHeight: 300, idealHeight: 400, maxHeight: 500, alignment: .center)
.padding(.all, 24)
.padding(.top, 12)
} else {
LineChart()
.touchOverlay(specifier: "%.2f")
.yAxisGrid()
.xAxisLabels()
.yAxisLabels()
.headerBox()
.legends()
.environmentObject(dataSource.chartData!)
.frame(minWidth: 300, maxWidth: 900, minHeight: 300, idealHeight: 400, maxHeight: 500, alignment: .center)
.padding(.all, 24)
.padding(.top, 12)
}
Spacer()
}
Spacer()
...
Do you know how I can reuse this code with fresh data within SwiftUI?
P.S. I tried to create a replacement instance of LineChart() for the new graph; doesn't work:
Button {
lineChart = nil
lineChart = LineChart()
dataSource.loadChartData1()
}
Hey @UncleRic, I had a similar issue.
Try @willdale's solution on the issue #5
@State var selectedGraph = "" // <---- the binding of that wheel picker
@State var data = ChartData(dataPoints: [])
func chartDataPoints() -> [dataPoint] {
....
}
....
LineChart()
.touchOverlay(specifier: "%.2f")
.yAxisGrid()
.xAxisLabels()
.yAxisLabels()
.headerBox()
.legends()
.environmentObject(data)
.id(UUID()) // <---- add an id to the chart
// Re-Initialise data at `onAppear`
.onAppear {
data.dataPoints = chartDataPoints()
}
// Update the data of the chart whenever the user selects something else from the wheel picker.
.onChange(of: selectedGraph, perform: { value in
data.dataPoints = chartDataPoints()
})
In addition to @willdale's solution, add an id
to the chart to force it to refresh when something changes.
Originally posted by @USBA in #7 (comment)
I would like to transform the y-axis labels from [0,...,5] to something like ["ok","good",...,"poor"].
Is this possible?
Related to #67, if 2 data sets require different scales, either with a grouped bar chart or multi-line chart, supporting a left and right y-axis would be super handy.
None of the examples will work as they will not build.
A closed issue indicated this was fixed but it is still broken.
Really enjoying this library! Thanks for such excellent work! 🙌
I am trying to understand .linearTrendLine. I see that it takes chartDate as a parameter, but then also takes a firstValue and lastValue. As far as I can tell the line is just a straight line drawn from firstValue to lastValue?
I was wondering if this was something you might come back to to add support for a trend line with curves more along the lines of a moving average? I also tried .averageLine but this just gave me a flat horizontal line across the data.
Thanks again for for a great library! 🍻
I'd like to be able to set a maximum value for the baseline while computing the minimum value from the data points.
For example, if I set the maximum baseline value to 200, the baseline would be:
200 with data points of [250.0, 245.0, 238.0]
190 with data points of [250.0, 245.0, 238.0, 190.0]
Something like this:
enum Baseline {
case minimum
case minimumWithMaximum(of: Double) // New enum case
case zero
}
var baseline: Baseline
var data = [250.0, 245.0, 238.0]
var minValue: Double
baseline = .minimumWithMaximum(of: 200)
switch baseline {
case .minimum:
minValue = data.min() ?? 0
case .minimumWithMaximum(let maximum):
minValue = min(data.min() ?? 0, maximum)
case .zero:
minValue = 0
}
print(minValue)
This would print 200.0.
Changing var data = [250.0, 245.0, 238.0] to var data = [250.0, 245.0, 238.0, 190.0] would print 190.0
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.