Closed iain-reid closed 2 years ago
Hey @iain-reid, The main issue is the order of the View Modifiers, id
should be at the end it seams.
var body: some View {
LineChart(chartData: chartData)
.yAxisPOI(chartData: chartData,
markerName: "Target",
markerValue: 450,
labelPosition: .center(specifier: "Target %.0f"),
labelColour: Color.white,
labelBackground: Color.gray,
lineColour: Color.gray,
strokeStyle: StrokeStyle(lineWidth: 1, dash: [2, 2]))
.xAxisGrid(chartData: chartData)
.yAxisGrid(chartData: chartData)
.xAxisLabels(chartData: chartData)
.yAxisLabels(chartData: chartData)
.id(chartData.id)
}
There were some other small layout issues with the implementation. They were solved by changing how the data is declared.
var chartData: LineChartData = {
let readings = Readings()
var chartStyle: LineChartStyle {
let yAxisGridStyle = GridStyle(
numberOfLines: 5,
lineColour: Color.blue,
lineWidth: 1,
dash: [1000]
)
return LineChartStyle(
xAxisLabelPosition: .bottom,
xAxisLabelsFrom: .chartData(rotation: .degrees(0)),
yAxisGridStyle: yAxisGridStyle,
yAxisNumberOfLabels: 5,
globalAnimation: .default)
}
var dataSet: LineDataSet {
let lineStyle = LineStyle(
lineColour: ColourStyle(colour: .blue),
lineType: .line,
strokeStyle: Stroke(lineWidth: 2, lineCap: .round, lineJoin: .round)
)
return LineDataSet(dataPoints: readings.readings.map {
LineChartDataPoint(value: Double($0))
}, style: lineStyle)
}
return LineChartData(
dataSets: dataSet,
metadata: ChartMetadata(title: "Peak Flow"),
xAxisLabels: ["M", "T", "W", "T", "F"],
chartStyle: chartStyle,
noDataText: Text("No Data"))
}()
Hi Will,
Thanks for the help, much appreciated! - I've managed to get the layout looking better:
With the following code:
import SwiftUI
import SwiftUICharts
struct PeakFlowChart: View {
var lineWidth: CGFloat = 2
var lineColor: Color = Color.Brand.royalBlue
var readings: PeakFlowReadings = []
var curvedLine: Bool = false
var ignoreZero: Bool = true
//
private var baseline: Double {
let baseline = Double(readings.minimumValue() - (readings.valueRange() / 2))
if baseline <= 0 {
return 0
}
return baseline
}
private var topLine: Double {
return Double(readings.maximumValue() + (readings.valueRange() / 2))
}
private var chartData: LineChartData {
var chartStyle: LineChartStyle {
let yAxisGridStyle = GridStyle(
numberOfLines: 5,
lineColour: Color.Brand.sky,
lineWidth: 1,
dash: [1000]
)
return LineChartStyle(
xAxisLabelPosition: .bottom,
xAxisLabelFont: Font.Primary.metadata,
xAxisLabelColour: Color.UI.midGrey,
xAxisLabelsFrom: .chartData(rotation: .degrees(0)),
yAxisGridStyle: yAxisGridStyle,
yAxisLabelPosition: .leading,
yAxisLabelFont: Font.Primary.metadata,
yAxisLabelColour: Color.UI.midGrey,
yAxisNumberOfLabels: 5,
yAxisLabelType: .numeric,
baseline: .minimumWithMaximum(of: baseline),
topLine: .maximum(of: topLine),
globalAnimation: .default)
}
var dataSet: LineDataSet {
let lineStyle = LineStyle(
lineColour: ColourStyle(colour: lineColor),
lineType: curvedLine ? .curvedLine : .line,
strokeStyle: Stroke(lineWidth: 2, lineCap: .round, lineJoin: .round),
ignoreZero: ignoreZero)
return LineDataSet(dataPoints: readings.map { (value: UInt, date: Date?) in
LineChartDataPoint(value: Double(value), date: date)
}, style: lineStyle)
}
return LineChartData(dataSets: dataSet,
metadata: ChartMetadata(title: "Peak Flow"),
xAxisLabels: ["M", "T", "W", "T", "F"],
chartStyle: chartStyle ,
noDataText: Text("No Data"))
}
var body: some View {
LineChart(chartData: chartData)
.yAxisPOI(chartData: chartData,
markerName: "Target",
markerValue: 440,
labelPosition: .center(specifier: "Target %.0f"),
labelColour: Color.Brand.white,
labelBackground: Color.UI.midGrey,
lineColour: Color.UI.midGrey,
strokeStyle: StrokeStyle(lineWidth: 1, dash: [2, 2]))
.yAxisGrid(chartData: chartData)
.xAxisLabels(chartData: chartData)
.yAxisLabels(chartData: chartData)
.id(chartData.id)
}
}
struct PeakFlowChart_Previews: PreviewProvider {
static var previews: some View {
VStack {
Spacer()
PeakFlowChart(readings: ChartDummyData.peakFlowReadings)
.frame(height: 300)
Spacer()
}
}
}
However - the x-axis labels, and y-axis grid still seem a little misaligned. I was able to remedy by adding negative padding when adding the sequence of modifiers:
LineChart(chartData: chartData)
.yAxisPOI(chartData: chartData,
markerName: "Target",
markerValue: 440,
labelPosition: .center(specifier: "Target %.0f"),
labelColour: Color.Brand.white,
labelBackground: Color.UI.midGrey,
lineColour: Color.UI.midGrey,
strokeStyle: StrokeStyle(lineWidth: 1, dash: [2, 2]))
.yAxisGrid(chartData: chartData)
.xAxisLabels(chartData: chartData)
.padding(.bottom, -15)
.yAxisLabels(chartData: chartData)
.id(chartData.id)
Is there a better solution to this?
Hi @iain-reid, It's the way that the LineChartData
is initialised. As it's a computed property, a new LineChartData
gets create each time.
There are calculations in the library that, it appears, can't run when declared this way. When I print out the padding required to layout to place the bottom label it returns 0.
When I change the in declaration to use a closure in the code snippet below, it lays out properly.
private var chartData: LineChartData = {
var lineWidth: CGFloat = 2
var lineColor: Color = Color.blue
var curvedLine: Bool = false
var ignoreZero: Bool = true
var readings = Readings()
var chartStyle: LineChartStyle {
var baseline: Double {
let baseline = Double(readings.minimumValue() - (readings.valueRange() / 2))
if baseline <= 0 {
return 0
}
return baseline
}
var topLine: Double {
return Double(readings.maximumValue() + (readings.valueRange() / 2))
}
let yAxisGridStyle = GridStyle(
numberOfLines: 5,
lineColour: Color.blue,
lineWidth: 1,
dash: [1000]
)
return LineChartStyle(
xAxisLabelPosition: .bottom,
xAxisLabelsFrom: .chartData(rotation: .degrees(0)),
yAxisGridStyle: yAxisGridStyle,
yAxisLabelPosition: .leading,
yAxisNumberOfLabels: 5,
yAxisLabelType: .numeric,
baseline: .minimumWithMaximum(of: baseline),
topLine: .maximum(of: topLine),
globalAnimation: .default)
}
var dataSet: LineDataSet {
let lineStyle = LineStyle(
lineColour: ColourStyle(colour: lineColor),
lineType: curvedLine ? .curvedLine : .line,
strokeStyle: Stroke(lineWidth: 2, lineCap: .round, lineJoin: .round),
ignoreZero: ignoreZero)
return LineDataSet(dataPoints: readings.readings.map {
LineChartDataPoint(value: Double($0))
}, style: lineStyle)
}
return LineChartData(dataSets: dataSet,
metadata: ChartMetadata(title: "Peak Flow"),
xAxisLabels: ["M", "T", "W", "T", "F"],
chartStyle: chartStyle ,
noDataText: Text("No Data"))
}()
Edit
Edit 2
I just saw that it is still slightly misaligned.... For the moment to get the bottom label to line up set LineChartStyle -> xAxisTitle: ""
I may be misunderstanding the implementation - but I don't seem to be able to get the x-axis labels to align at the bottom of the chart.
I've set the style to make use of the
.chartData
, and provided those labels in theLineChartData
. When I add this to the Chart itself, the yAxis labels remain the same - but the yAxis grid compresses to the top 50% of the view, as does the line - and the x-axis labels are centered!Before I add
.xAxisLabels(chartData: chartData)
:-After I add
.xAxisLabels(chartData: chartData)
:-And this is how I have implemented :-
Any help at all would be appreciated - Not sure if it is a bug, but would be ace if you could feedback, love the library and keen to use. Many thanks :-)