Swift Charts Tutorial: Getting Started
Learn how to use Swift Charts to transform data into elegant and accessible graphs. By Vidhur Voora.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Swift Charts Tutorial: Getting Started
35 mins
- Getting Started
- Getting Aquainted with Swift Charts
- Developing Charts
- Creating a Bar Chart
- Adding the Bar Chart
- Tidying up the Bar Chart
- Changing to a Horizontal Bar Chart
- Customizing the Bar Chart
- Using the Variants Feature in Xcode
- Fixing the Annotation
- Supporting Accessibility
- Putting it together
- Adding a Point Chart
- Customizing the Point Chart
- Adding a Line Chart
- Calculating and Creating the Line Chart
- Showing the Line Chart
- Customizing the Line Chart
- Finishing Up the Line Chart
- Combining Marks in a Line Chart
- Adding Drill-Down Functionality
- Adding Chart Type Picker
- Adding Multiple Marks
- Visualizing Multiple Data Points
- Where to Go From Here?
Customizing the Point Chart
Open SnowfallChart.swift again, and add the following to Chart{}
:
.chartYScale(domain: 0...10)
You've just set y-axis scale to always start at 0 and end at 10.
Next, you’ll customize the background color of this chart.
Just below .chartYScale(domain: 0...10)
add:
.chartPlotStyle { plotArea in
plotArea.background(.blue.opacity(0.2))
}
Here, you change the background of the plot area to blue with an opacity of 0.2 by using .chartPlotStyle
.
Below charPlotStyle{}
add:
.chartYAxisLabel("Inches")
This adds a label to the y-axis that specifies the unit of measure.
Build and run.
Take a moment to compare the snowfall data between different weather stations.
Notice the y-axis scale is the same for every chart and the background color is blue. It only took a few lines of code to do all that!
Next, you'll learn how to create a line chart and combine different marks.
Adding a Line Chart
Of all the charts you've built so far, this one will be the fanciest.
Take a peek at the data you're working with:
- Run WeatherChart then select a weather station.
- Tap Temperatures to view a list that shows daily high and low temperatures for a year.
This list isn't user-friendly. It's hard to say how it changed as you scroll.
Temperature readings look great in a line chart because they fluctuate over time. You can almost feel the temperature changes as your eyes trace the line.
You could show high and low temperatures separately, but that'd make it harder to compare month to month.
But if you first calculate average temperatures, you could feed just one set of data into a chart for each month and show one line.
In the next few steps, you'll build a line chart that shows multiple months side by side with clearly marked axes to indicate each week and the temperature readings.
Calculating and Creating the Line Chart
In the Project navigator, find and expand the Charts group. Open MonthlyTemperatureChart.swift.
Similar to the previous charts you've built, add the following after import SwiftUI
:
import Charts
Add the following variable to MonthlyTemperatureChart
:
var measurements: [DayInfo]
Replace the contents of previews
in MonthlyTemperatureChart_Previews
with:
// swiftlint:disable force_unwrapping
MonthlyTemperatureChart(
measurements: WeatherInformation()!.stations[2].measurements)
Add the following utility method in MonthlyTemperatureChart
:
func measurementsByMonth(_ month: Int) -> [DayInfo] {
return self.measurements.filter {
Calendar.current.component(.month, from: $0.date) == month + 1
}
}
You're telling your new method measurementsByMonth(_:)
to return an array of daily weather information for the specified month.
Next, add the following in MonthlyTemperatureChart
:
// 1
var monthlyAvgTemperatureView: some View {
// 2
List(0..<12) { month in
// 3
VStack {
// 4
Chart(measurementsByMonth(month)) { dayInfo in
// 5
LineMark(
x: .value("Day", dayInfo.date),
y: .value("Temperature", dayInfo.temp(type: .avg))
)
// 6
.foregroundStyle(.orange)
// 7
.interpolationMethod(.catmullRom)
}
Text(Calendar.current.monthSymbols[month])
}
.frame(height: 150)
}
.listStyle(.plain)
}
There are a lot of cool things happening in this computed variable:
- You define
monthlyAvgTemperatureView
, which will populate the monthly temperature view. - You add a
List
to show the monthly temperature charts. - Inside the list,
VStack
shows the temperature chart and the name of the month below it. - The
Chart
gets weather information for the corresponding month. - You use
LineMark
to create a line chart. For each day within the month, you add aLineMark
. The x-axis indicates the day and the y-axis the day's average temperature. - You set the color of the line chart to orange using
.foregroundStyle.
- To smooth the rendered line, you use
.interpolationMethod
and call a Catmull-Rom spline to interpolate the data points.
Showing the Line Chart
Now, replace the contents of body
with the following:
monthlyAvgTemperatureView
You've just set your fancy new computed variable to be the body
content.
Check your work in the preview window.
Now that's clean! Your line charts elegantly show the average temperature for each month. Great job!
Customizing the Line Chart
Still in MonthlyTemperatureChart.swift, find Chart{}
within the implementation of monthlyAvgTemperatureView
. Add the following:
// 1
.chartForegroundStyleScale([
TemperatureTypes.avg.rawValue: .orange
])
// 2
.chartXAxisLabel("Weeks", alignment: .center)
.chartYAxisLabel("ºF")
// 3
.chartXAxis {
AxisMarks(values: .automatic(minimumStride: 7)) { _ in
AxisGridLine()
AxisTick()
AxisValueLabel(
format: .dateTime.week(.weekOfMonth)
)
}
}
// 4
.chartYAxis {
AxisMarks( preset: .extended, position: .leading)
}
Here’s what you do here:
- Add a
.chartForegroundStyleScale
modifier to define how the average maps to the foreground style and add a legend below the line chart. - Make a label for both the x- and y-axis and specify the alignment of the x-axis so it doesn't overlap the legend.
- Modify the x-axis with
.chartXAxis
to display the week of the month instead of the default. Set the visual marks on the x-axis to show the week number:- Set
AxisMarks
minimum stride to 7, as each week consists of 7 days. - Use
AxisGridLine
to show a line across the plot area. - Use
AxisTick
to draw tick marks. - Set
AxisValueLabel
to be the week of the month as a number.
- Set
- Adjust the y-axis with
.chartYAxis
andAxisMarks
to snap it to the leading edge of the chart instead of the default trailing edge.
- Set
AxisMarks
minimum stride to 7, as each week consists of 7 days. - Use
AxisGridLine
to show a line across the plot area. - Use
AxisTick
to draw tick marks. - Set
AxisValueLabel
to be the week of the month as a number.
You have more options to customize the chart. For example, you could also use different fonts or foreground styles for axes.
Finishing Up the Line Chart
Open TemperatureTab.swift. Replace the content of body
with the following:
VStack {
Text("Temperature for 2018")
MonthlyTemperatureChart(measurements: self.station.measurements)
}
You've just plugged in your newly created MonthlyTemperatureChart
, and passed in the weather measurements.
Build and run.
Select a weather station and navigate to the Temperature tab to play with your fancy new line charts that show the average temperature for each week and month.
Now your brain can quickly read and compare differences. Congratulations. :]
But your work isn't quite finished.
In the next section, you'll combine different marks to create a more meaningful chart.
Combining Marks in a Line Chart
In this section, you'll illustrate to yourself how to use both RectangleMark
and AreaMark
to show low, high and average temperatures, as well as adding a drill-down functionality so the user can see the details for each day.
Find and open WeeklyTemperatureChart.swift under the Charts group.
Replace the contents of the entire file with the following:
import SwiftUI
// 1
import Charts
struct WeeklyTemperatureChart: View {
// 2
var measurements: [DayInfo]
// 3
var month: Int
// 4
let colorForAverageTemperature: Color = .red
let colorForLowestTemperature: Color = .blue.opacity(0.3)
let colorForHighestTemperature: Color = .yellow.opacity(0.4)
var body: some View {
// 5
weeklyTemperatureView
}
var weeklyTemperatureView: some View {
// TODO: Chart will be added here
}
}
struct WeeklyTemperatureChart_Previews: PreviewProvider {
static var previews: some View {
// swiftlint:disable force_unwrapping
// 6
WeeklyTemperatureChart(
measurements: WeatherInformation()!.stations[2].measurements, month: 1)
}
}
Here’s a breakdown:
- Import the
Charts
framework. - Store weather data with
measurements
. - Store the month number for which you want to view daily temperature data with
month
. - Colors for average, lowest and highest temperatures, respectively.
- Create the
weeklyTemperatureView
computed variable to hold the contents of the chart. You'll use it in the viewbody
. - Pass in weather data for the preview.
Add the following utility methods to WeeklyTemperatureChart
:
// 1
func measurementsByMonth(_ month: Int) -> [DayInfo] {
return self.measurements
.filter {
Calendar.current.component(.month, from: $0.date) == month + 1
}
}
// 2
func measurementsBy(month: Int, week: Int) -> [DayInfo] {
return self.measurementsByMonth(month)
.filter {
let day = Calendar.current.component(.day, from: $0.date)
if week == 1 {
return day <= 7
} else if week == 2 {
return (day > 7 && day <= 14)
} else if week == 3 {
return (day > 14 && day <= 21)
} else if week == 4 {
return (day > 21 && day <= 28)
} else {
return day > 28
}
}
}
Here’s what these new methods do:
-
measurementsByMonth(_:)
returns an array of the daily weather information for the specified month. -
measurementsBy(month:week:)
returns an array of the daily weather information for the specified week of the month — you need this to show the chart for each week.