Created
March 30, 2025 00:37
-
-
Save davidakoontz/1bc6e653dbbd4aaed35bea29028d522e to your computer and use it in GitHub Desktop.
Swift Charts Configuration Structs & Example Usage
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ExampleConfigChart.swift | |
// Portfolio | |
// | |
// Created by David on 3/29/25. | |
// | |
import SwiftUI | |
import Charts | |
// Proof of Concept: Write a Configuration Structure for use in Swift Charts framework. | |
// This set of structs should make learning and configuring the complex Charts framework, | |
// easy and virtually-fool proof! Well at least there is a good example of how all of | |
// these configuration values are used. | |
public struct ExampleConfigChart: View { | |
let legend = ChartLegend_Config() | |
let xAxis = Chart_X_Axis_Config() | |
let yaxis = Chart_Y_Axis_Config() | |
public var body: some View { | |
Group { | |
Chart(exData) { | |
LineMark(x: .value("x", $0.id) , y: .value("y", $0.amount)) | |
.interpolationMethod(.catmullRom) // wavey and curvey | |
.lineStyle(StrokeStyle(lineWidth: 1) ) | |
} | |
// this foregroundStyle with junk makes Legend visible | |
.chartForegroundStyleScale(["Any junk text": Color.green]) | |
// FB17044094 Chart Legend not visible until adding .chartForegroundStyleScale | |
// Attempt to use ChartLegend_Config | |
.chartLegend(legend.chartLegendVisibility) | |
.chartLegend(position: legend.chartLegendPosition, | |
alignment: legend.chartLegendAlignment, | |
spacing: legend.chartLegendSpacing) | |
{ | |
Text(legend.title) | |
} | |
// ========== X AXIS | |
// Attempt to use ChartAxis_Config for XAxis | |
.chartXScale(domain: [xAxis.minScaleDomain, xAxis.maxScaleDomain]) | |
.chartXAxisLabel(position: xAxis.axisLabelPosition, | |
alignment: xAxis.axisLabelAlignment, | |
spacing: xAxis.axisLabelSpacing) | |
{ | |
Text(xAxis.axisLabel) | |
} | |
.chartXAxis { | |
// Axis Marks No 1 | |
AxisMarks(position: xAxis.axisMarksNo1Position, values: xAxis.axisMarksNo1Values) | |
{ | |
AxisValueLabel(anchor: xAxis.axisValueLabelAnchor) | |
} | |
// Axis Marks No 2 | |
AxisMarks(position: xAxis.axisMarksNo2Position, values: xAxis.axisMarksNo2Values) { value in | |
if value.index != 0 { | |
// the leftmost value does NOT need a tick | |
AxisTick(length: xAxis.axisTickLength, | |
stroke: .init(lineWidth: xAxis.axisTickLineWidth) ) | |
.offset(y: xAxis.axisTickOffsetX) // Y negative value offset | |
} | |
} | |
// Axis Marks No 3 | |
AxisMarks(position: xAxis.axisMarksNo3Position, values: xAxis.axisMarksNo3ValuesArray) { | |
AxisGridLine(stroke: .init(lineWidth: xAxis.axisGridLineWidth)) | |
} | |
} // chartXaxis | |
// ========== Y AXIS | |
.chartYAxisLabel(position: yaxis.axisLabelPosition, alignment: yaxis.axisLabelAlignment) | |
{ | |
Text(yaxis.axisLabel) | |
} | |
.chartYScale(domain: [yaxis.minScaleDomain, yaxis.maxScaleDomain]) | |
// 3 layers pattern | |
.chartYAxis { | |
// Axis Marks No 1 | |
AxisMarks(position: yaxis.axisMarksNo1Position, | |
values: yaxis.axisMarksNo1Values) | |
{ | |
AxisValueLabel(anchor: yaxis.axisValueLabelAnchor) | |
} | |
// Axis Marks No 2 | |
AxisMarks(position: yaxis.axisMarksNo2Position, values: yaxis.axisMarksNo2Values) { value in | |
if value.index != 0 { | |
// the leftmost value does NOT need a tick | |
AxisTick(length: yaxis.axisTickLength, | |
stroke: .init(lineWidth: yaxis.axisTickLineWidth) ) | |
.offset(x: yaxis.axisTickOffsetY) // value offset | |
} | |
} | |
// Axis Marks No 3 | |
AxisMarks(position: yaxis.axisMarksNo3Position, | |
values: yaxis.axisMarksNo3ValuesArray) | |
{ | |
AxisGridLine(stroke: .init(lineWidth: yaxis.axisGridLineWidth)) | |
} // third layer - the bottom most grid line | |
} | |
.padding() | |
.aspectRatio(1, contentMode: .fit) | |
Text("Proof of Concept - Chart Config") | |
} // group | |
} | |
} | |
#Preview { | |
ExampleConfigChart() | |
} | |
// Foo | |
struct Bar: Identifiable { | |
let id: Double | |
let amount: Double | |
} | |
// computed data array - a difficult computation to get correct. | |
let exData: [Bar] = (0..<400).map { Bar(id: Double($0), amount: (log2(1 - Double.random(in: 0..<1) ) / -10 ) - 1) } | |
// | |
// Chart Configuration.swift | |
// Portfolio | |
// | |
// Created by David on 3/28/25. | |
// | |
struct ChartLegend_Config { | |
var title: String = "Chart Title" | |
// var chartLegendContent = Text("title") | |
var chartLegendPosition = AnnotationPosition.bottom // bottomLeading, topTrailing, overlay, automatic | |
var chartLegendAlignment = Alignment.topLeading | |
var chartLegendSpacing = CGFloat(8) | |
var chartLegendVisibility: Visibility = .visible // Enum: automatic, visible, hidden | |
// https://developer.apple.com/documentation/swiftui/view/chartlegend(position:alignment:spacing:) | |
init(title: String = "Chart Title", | |
chartLegendPosition: AnnotationPosition = AnnotationPosition.bottom, | |
chartLegendAlignment: Alignment = Alignment.topLeading, | |
chartLegendSpacing: CGFloat = CGFloat(8), | |
chartLegendVisibility: Visibility = .visible) { | |
self.title = title | |
// self.chartLegendContent = chartLegendContent | |
self.chartLegendPosition = chartLegendPosition | |
self.chartLegendAlignment = chartLegendAlignment | |
self.chartLegendSpacing = chartLegendSpacing | |
self.chartLegendVisibility = chartLegendVisibility | |
} | |
// To get Legend to show up - had to add this crap - to overcome some bug... | |
// this foregroundStyle with junk makes Legend visible | |
// .chartForegroundStyleScale(["Any junk text": Color.green]) | |
// FB17044094 Chart Legend not visible until adding .chartForegroundStyleScale | |
// The chartLegend modifier allows us to control the visibility of the legend view by passing one of these three options: automatic, visible, and hidden. | |
} | |
struct Chart_X_Axis_Config { | |
var minScaleDomain: Int = 0 | |
var maxScaleDomain: Int = 400 | |
// mabybe ScaleDomain values | |
var axisLabel: String = "X Axis" | |
var axisLabelPosition: AnnotationPosition = .bottom | |
var axisLabelAlignment: Alignment = .center | |
var axisLabelSpacing: CGFloat = 4 | |
// https://developer.apple.com/documentation/SwiftUI/View/chartXAxisLabel(position:alignment:spacing:content:) | |
var axisValueLabelAnchor: UnitPoint = .center | |
var axisValueLabelCentered: Bool = true | |
var axisValueLabel: String? = "Value" | |
// https://developer.apple.com/documentation/charts/axisvaluelabel/init(_:centered:anchor:multilabelalignment:collisionresolution:offsetsmarks:orientation:horizontalspacing:verticalspacing:)-9202h | |
// Axis Marks No 1... | |
var axisMarksNo1Position: AxisMarkPosition = .bottom // automatic, bottom, leading, top, trailing | |
var axisMarksNo1Values: AxisMarkValues = .stride(by: 100.0) | |
//var axisMarksNo1ValuesStrideBy: Int = 100 | |
// Axis Marks No 2... | |
var axisMarksNo2Position: AxisMarkPosition = .bottom | |
var axisMarksNo2Values: AxisMarkValues = .stride(by: 20.0) | |
// Axis Marks No 3... | |
var axisMarksNo3Position: AxisMarkPosition = .bottom | |
var axisMarksNo3ValuesArray: [Double] = [0] | |
var axisMarksNo3Values: AxisMarkValues = .stride(by: 5.0) | |
//======== used in Axis Marks No 2 | |
var axisTickLength: CGFloat = 8 | |
var axisTickLineWidth: CGFloat = 1 | |
var axisTickOffsetX: CGFloat = -4 | |
var axisTickOffsetY: CGFloat = 4 | |
// ======== used in Axis Marks No 3 | |
var axisGridLineWidth: CGFloat = 1 | |
} | |
struct Chart_Y_Axis_Config { | |
var minScaleDomain: Int = -2 | |
var maxScaleDomain: Int = 2 | |
// mabybe ScaleDomain values | |
var axisLabel: String = "Y Axis" | |
var axisLabelPosition: AnnotationPosition = .leading | |
var axisLabelAlignment: Alignment = .center | |
var axisLabelSpacing: CGFloat = 4 | |
// https://developer.apple.com/documentation/SwiftUI/View/chartXAxisLabel(position:alignment:spacing:content:) | |
var axisValueLabelAnchor: UnitPoint = .trailing | |
var axisValueLabelCentered: Bool = true | |
var axisValueLabel: String? = "Value" | |
// https://developer.apple.com/documentation/charts/axisvaluelabel/init(_:centered:anchor:multilabelalignment:collisionresolution:offsetsmarks:orientation:horizontalspacing:verticalspacing:)-9202h | |
// Axis Marks No 1... | |
var axisMarksNo1Position: AxisMarkPosition = .leading // automatic, bottom, leading, top, trailing | |
var axisMarksNo1Values: AxisMarkValues = .stride(by: 1) | |
//var axisMarksNo1ValuesStrideBy: Int = 100 | |
// Axis Marks No 2... | |
var axisMarksNo2Position: AxisMarkPosition = .leading | |
var axisMarksNo2Values: AxisMarkValues = .stride(by: 0.2) | |
// Axis Marks No 3... | |
var axisMarksNo3Position: AxisMarkPosition = .leading | |
var axisMarksNo3ValuesArray: [Double] = [-2] | |
var axisMarksNo3Values: AxisMarkValues = .stride(by: 5.0) | |
//======== used in Axis Marks No 2 | |
var axisTickLength: CGFloat = 8 | |
var axisTickLineWidth: CGFloat = 1 | |
var axisTickOffsetX: CGFloat = -4 | |
var axisTickOffsetY: CGFloat = 4 | |
// ======== used in Axis Marks No 3 | |
var axisGridLineWidth: CGFloat = 1 | |
} | |
struct Chart_Configuration { | |
var legendConfig: ChartLegend_Config = ChartLegend_Config(title: "Chart Legend", chartLegendVisibility: .visible) | |
var xAxisConfig: Chart_X_Axis_Config = Chart_X_Axis_Config() | |
var yAxisConfig: Chart_Y_Axis_Config = Chart_Y_Axis_Config() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment