SwiftUI – Charts

By | 19/04/2023

In this post, we will see how to use the Charts framework added in SwiftUI 4.
But first of all, what is Charts?
From Apple developer site:
“Swift Charts is a powerful and concise SwiftUI framework for transforming your data into informative visualizations. With Swift Charts, you can build effective and customizable charts with minimal code. This framework provides marks, scales, axes, and legends as building blocks that you can combine to develop a broad range of data-driven charts.”
In a nutshell, the Charts framework helps us to create charts in a simple way.
To better understand it, we will create an application in which we will display weekly temperatures using five types of charts: Vertical/Horizontal Bar chart, Line chart, Point chart, and Area chart.

Let’s start opening Xcode and we create an IOS App project called TestCharts where, we will define a menu like this:

[CONTENTVIEW.SWIFT]

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack(spacing: 20) {
            Button(action: {
                // Action for button 1
            }) {
                Text("Vertical Bar Chart")
                    .font(.headline)
                    .foregroundColor(.white)
                    .padding()
                    .frame(maxWidth: .infinity)
                    .background(Color.blue)
                    .cornerRadius(10)
            }
            
            Button(action: {
                // Action for button 2
            }) {
                Text("Horizontal Bar Chart")
                    .font(.headline)
                    .foregroundColor(.white)
                    .padding()
                    .frame(maxWidth: .infinity)
                    .background(Color.green)
                    .cornerRadius(10)
            }
            
            Button(action: {
                // Action for button 3
            }) {
                Text("Line Chart")
                    .font(.headline)
                    .foregroundColor(.white)
                    .padding()
                    .frame(maxWidth: .infinity)
                    .background(Color.orange)
                    .cornerRadius(10)
            }
            
            Button(action: {
                // Action for button 4
            }) {
                Text("Point Chart")
                    .font(.headline)
                    .foregroundColor(.white)
                    .padding()
                    .frame(maxWidth: .infinity)
                    .background(Color.red)
                    .cornerRadius(10)
            }

            Button(action: {
                // Action for button 5
            }) {
                Text("Area Chart")
                    .font(.headline)
                    .foregroundColor(.white)
                    .padding()
                    .frame(maxWidth: .infinity)
                    .background(Color.yellow)
                    .cornerRadius(10)
            }
        }
        .padding()
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
    }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


If we run the application, the following will be the result:


Now, we define the Model and the ModelView that we will use in all charts.

[TEMPERATURE.SWIFT] – Model

import Foundation

struct Temperature: Identifiable {
    let id = UUID()
    let day: String
    let degree: Int
}


[TEMPERATUREVIEWMODEL.SWIFT] – ViewModel

import Foundation

class TemperatureViewModel {
     
    private var lstTemperature = [Temperature]()
     
    // constructor
    init() {
        lstTemperature.append(Temperature(day: "Monday", degree: 10))
        lstTemperature.append(Temperature(day: "Tuesday", degree: 8))
        lstTemperature.append(Temperature(day: "Wednesday", degree: 7))
        lstTemperature.append(Temperature(day: "Thursday", degree: 10))
        lstTemperature.append(Temperature(day: "Friday", degree: 12))
        lstTemperature.append(Temperature(day: "Saturday", degree: 11))
        lstTemperature.append(Temperature(day: "Sunday", degree: 9))
    }
     
    func GetTemperatures() -> [Temperature] {
        return lstTemperature
    }
}


Then, we create a View for every single chart:

[VERTICALBARVIEW.SWIFT]

import SwiftUI
import Charts

struct VerticalBarView: View {
    @State var objTemperatures = TemperatureViewModel()
    var body: some View {
        VStack {
            Text("Weekly Temperatures")
            // 1. Add a chart element
            Chart(objTemperatures.GetTemperatures()) { item in
                // 2. Bar mark
                BarMark(
                    x: .value("Day", item.day),
                    y: .value("Degree", item.degree)
                )
                .annotation {
                    Text("\(item.degree)°")
                }
            }
        }
        .padding()
    }
}

struct VerticalBarView_Previews: PreviewProvider {
    static var previews: some View {
        VerticalBarView()
    }
}


[HORIZONTALBARVIEW.SWIFT]

import SwiftUI
import Charts

struct HorizontalBarView: View {
    @State var objTemperatures = TemperatureViewModel()
    var body: some View {
        VStack {
            Text("Weekly Temperatures")
            // 1. Add a chart element
            Chart(objTemperatures.GetTemperatures()) { item in
                // 2. Bar mark
                BarMark(
                    x:.value("Degree", item.degree),
                    y: .value("Day", item.day)
                )
                .annotation {
                    Text("\(item.degree)°")
                }
            }
        }
        .padding()
    }
}

struct HorizontalBarView_Previews: PreviewProvider {
    static var previews: some View {
        HorizontalBarView()
    }
}


[LINECHARTVIEW.SWIFT]

import SwiftUI
import Charts

struct LineChartView: View {
    @State var objTemperatures = TemperatureViewModel()
    var body: some View {
        VStack {
            Text("Weekly Temperatures")
            // 1. Add a chart element
            Chart(objTemperatures.GetTemperatures()) { item in
                // 2. Line mark
                LineMark(
                    x: .value("Day", item.day),
                    y: .value("Degree", item.degree)
                )
                .annotation {
                    Text("\(item.degree)°")
                }
            }
        }
        .padding()
    }
}

struct LineChartView_Previews: PreviewProvider {
    static var previews: some View {
        LineChartView()
    }
}


[POINTCHARTVIEW.SWIFT]

import SwiftUI
import Charts

struct PointChartView: View {
    @State var objTemperatures = TemperatureViewModel()
    var body: some View {
        VStack {
            Text("Weekly Temperatures")
            // 1. Add a chart element
            Chart(objTemperatures.GetTemperatures()) { item in
                // 2. Point mark
                PointMark(
                    x: .value("Day", item.day),
                    y: .value("Degree", item.degree)
                )
                .annotation {
                    Text("\(item.degree)°")
                }
            }
        }
        .padding()
    }
}

struct PointChartView_Previews: PreviewProvider {
    static var previews: some View {
        PointChartView()
    }
}


[AREACHARTVIEW.SWIFT]


import SwiftUI
import Charts

struct AreaChartView: View {
    @State var objTemperatures = TemperatureViewModel()
    var body: some View {
        VStack {
            Text("Weekly Temperatures")
            // 1. Add a chart element
            Chart(objTemperatures.GetTemperatures()) { item in
                // 2. Area mark
                AreaMark(
                    x: .value("Day", item.day),
                    y: .value("Degree", item.degree)
                )
            }
        }
        .padding()
    }
}

struct AreaChartView_Previews: PreviewProvider {
    static var previews: some View {
        AreaChartView()
    }
}


Finally, we modify the ContentView file:

[CONTENTVIEW.SWIFT]

import SwiftUI

struct ContentView: View {
    @State private var showVerticalBar = false
    @State private var showHorizontalBar = false
    @State private var showLineChart = false
    @State private var showPointChart = false
    @State private var showAreaChart = false
    var body: some View {
        VStack(spacing: 20) {
            Button(action: {
                self.showHorizontalBar = false
                self.showLineChart = false
                self.showAreaChart = false
                self.showVerticalBar.toggle()
            }) {
                Text("Vertical Bar Chart")
                    .font(.headline)
                    .foregroundColor(.white)
                    .padding()
                    .frame(maxWidth: .infinity)
                    .background(Color.blue)
                    .cornerRadius(10)
            }
            
            Button(action: {
                self.showVerticalBar = false
                self.showLineChart = false
                self.showPointChart = false
                self.showAreaChart = false
                self.showHorizontalBar.toggle()
            }) {
                Text("Horizontal Bar Chart")
                    .font(.headline)
                    .foregroundColor(.white)
                    .padding()
                    .frame(maxWidth: .infinity)
                    .background(Color.green)
                    .cornerRadius(10)
            }
            
            Button(action: {
                self.showVerticalBar = false
                self.showHorizontalBar = false
                self.showPointChart = false
                self.showAreaChart = false
                self.showLineChart.toggle()
            }) {
                Text("Line Chart")
                    .font(.headline)
                    .foregroundColor(.white)
                    .padding()
                    .frame(maxWidth: .infinity)
                    .background(Color.orange)
                    .cornerRadius(10)
            }
            
            Button(action: {
                self.showVerticalBar = false
                self.showHorizontalBar = false
                self.showLineChart = false
                self.showAreaChart = false
                self.showPointChart.toggle()
            }) {
                Text("Point Chart")
                    .font(.headline)
                    .foregroundColor(.white)
                    .padding()
                    .frame(maxWidth: .infinity)
                    .background(Color.red)
                    .cornerRadius(10)
            }
            Button(action: {
                self.showVerticalBar = false
                self.showHorizontalBar = false
                self.showLineChart = false
                self.showPointChart = false
                self.showAreaChart.toggle()
            }) {
                Text("Area Chart")
                    .font(.headline)
                    .foregroundColor(.white)
                    .padding()
                    .frame(maxWidth: .infinity)
                    .background(Color.yellow)
                    .cornerRadius(10)
            }
        }
        .padding()
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
        
        .sheet(isPresented: $showVerticalBar) {
                        VerticalBarView()
                    }
        .sheet(isPresented: $showHorizontalBar) {
                    HorizontalBarView()
                    }
        .sheet(isPresented: $showLineChart) {
                    LineChartView()
                    }
        .sheet(isPresented: $showPointChart) {
                    PointChartView()
                    }
        .sheet(isPresented: $showAreaChart) {
                    AreaChartView()
                    }
    }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


We have done and now, if we run the application, the following will be the result:

VERTICAL BAR CHART


HORIZONTAL BAR CHART


LINE CHART


POINT CHART


AREA CHART



Leave a Reply

Your email address will not be published. Required fields are marked *