Skip to content

Instantly share code, notes, and snippets.

@davidakoontz
Created March 30, 2025 00:37
Show Gist options
  • Save davidakoontz/1bc6e653dbbd4aaed35bea29028d522e to your computer and use it in GitHub Desktop.
Save davidakoontz/1bc6e653dbbd4aaed35bea29028d522e to your computer and use it in GitHub Desktop.
Swift Charts Configuration Structs & Example Usage
//
// 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