Chart Symbol Shape in Charts Framework & SwiftUI 4

DevTechie Inc
Jul 13, 2023

Chart Symbol Shape in Charts Framework & SwiftUI 4

Apple’s Charts framework was introduced with iOS 16 and it’s the easiest way to add data visualization to your app.

Learn more about charts here:

Mastering Charts Framework in SwiftUI 4 & iOS 16
At WWDC 22 Apple announced brand new framework for data visualization called Charts Framework. In this course we will…www.devtechie.com

Charts framework provides many customization options and one of the options is to choose the chart symbol to represent plotted values. Let’s look at this with an example.

We will start with a simple data structure to capture daily workout routines.

struct Workout: Identifiable {
    let id = UUID()
    let day: String
    let minutes: Int
}

We will add sample data to plot on the chart.

extension Workout {
    static var sample: [Workout] {
        [
            .init(day: "Mon", minutes: Int.random(in: 20..<60)),
            .init(day: "Tue", minutes: Int.random(in: 20..<60)),
            .init(day: "Wed", minutes: Int.random(in: 20..<60)),
            .init(day: "Thu", minutes: Int.random(in: 20..<60)),
            .init(day: "Fri", minutes: Int.random(in: 20..<60)),
            .init(day: "Sat", minutes: Int.random(in: 20..<60)),
            .init(day: "Sun", minutes: Int.random(in: 20..<60)),
        ]
    }
}

Next, we will add a chart view. We will plot line charts combined with point charts to plot daily workouts.

import Charts

struct ChartSymbolExample: View {
    
    var body: some View {
        NavigationStack {
            Chart(Workout.sample) { workout in
                LineMark(x: .value("Day", workout.day), y: .value("Minutes", workout.minutes))
                    .interpolationMethod(.catmullRom)
                PointMark(x: .value("Day", workout.day), y: .value("Minutes", workout.minutes))
                    .annotation {
                        Text("\(workout.minutes)")
                    }
            }
            .frame(height: 400)
            .navigationTitle("DevTechie")
        }
    }
}

The circle shape added below the minutes is the chart symbol shape.

Charts add these chart symbol shapes if not specified, but we can customize them with .symbol(symbol: ChartSymbolShape) modifier.

There are predefined values under ChartSymbolShape and we can choose from the list. So if we want to change the symbol to be a diamond, we can change it as shown below:

PointMark(x: .value("Day", workout.day), y: .value("Minutes", workout.minutes))
                    .symbol(.diamond)

Complete code:

import Charts

struct ChartSymbolExample: View {
    
    var body: some View {
        NavigationStack {
            Chart(Workout.sample) { workout in
                LineMark(x: .value("Day", workout.day), y: .value("Minutes", workout.minutes))
                    .interpolationMethod(.catmullRom)
                PointMark(x: .value("Day", workout.day), y: .value("Minutes", workout.minutes))
                    .symbol(.diamond)
                    .annotation {
                        Text("\(workout.minutes)")
                    }
            }
            .frame(height: 400)
            .navigationTitle("DevTechie")
        }
    }
}

Other options can be found under the ChartShapeSymbol extension.

extension ChartSymbolShape where Self == BasicChartSymbolShape {

    /// Circle symbol.
    public static var circle: BasicChartSymbolShape { get }

    /// Square symbol.
    public static var square: BasicChartSymbolShape { get }

    /// Triangle symbol.
    public static var triangle: BasicChartSymbolShape { get }

    /// Diamond symbol.
    public static var diamond: BasicChartSymbolShape { get }

    /// Pentagon symbol.
    public static var pentagon: BasicChartSymbolShape { get }

    /// Plus symbol.
    public static var plus: BasicChartSymbolShape { get }

    /// Cross symbol.
    public static var cross: BasicChartSymbolShape { get }

    /// Asterisk symbol.
    public static var asterisk: BasicChartSymbolShape { get }
}

Chart Symbol Size

We can change the size of a symbol with the help of symbolSize modifier which takes CGSize parameter.

Modifier:

.symbolSize(CGSize(width: 20, height: 20))

Code:

import Charts

struct ChartSymbolExample: View {
    
    var body: some View {
        NavigationStack {
            Chart(Workout.sample) { workout in
                LineMark(x: .value("Day", workout.day), y: .value("Minutes", workout.minutes))
                    .interpolationMethod(.catmullRom)
                PointMark(x: .value("Day", workout.day), y: .value("Minutes", workout.minutes))
                    .symbol(.diamond)
                    .symbolSize(CGSize(width: 20, height: 20))
                    .annotation {
                        Text("\(workout.minutes)")
                    }
            }
            .frame(height: 400)
            .navigationTitle("DevTechie")
        }
    }
}

Chart Symbol Border

By default, symbols are drawn as solid fill, but we can change that to only draw the border of the shape by chaining .strokeBorder() modifier to the chart symbol shape.

.symbol(.asterisk.strokeBorder()):
import Charts

struct ChartSymbolExample: View {
    
    var body: some View {
        NavigationStack {
            Chart(Workout.sample) { workout in
                LineMark(x: .value("Day", workout.day), y: .value("Minutes", workout.minutes))
                    .interpolationMethod(.catmullRom)
                PointMark(x: .value("Day", workout.day), y: .value("Minutes", workout.minutes))
                    .symbol(.diamond.strokeBorder())
                    .symbolSize(CGSize(width: 20, height: 20))
                    .annotation {
                        Text("\(workout.minutes)")
                    }
            }
            .frame(height: 400)
            .navigationTitle("DevTechie")
        }
    }
}

We can change the border width by specifying the line width.

.symbol(.diamond.strokeBorder(lineWidth: 5))