Swift Charts Tutorial: Getting Started

Learn how to use Swift Charts to transform data into elegant and accessible graphs. By Vidhur Voora.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

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.

Showing snowfall data with Point Chart and a scaled axis

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:

  1. Run WeatherChart then select a weather station.
  2. Tap Temperatures to view a list that shows daily high and low temperatures for a year.

List showing raw temperature data

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:

  1. You define monthlyAvgTemperatureView, which will populate the monthly temperature view.
  2. You add a List to show the monthly temperature charts.
  3. Inside the list, VStack shows the temperature chart and the name of the month below it.
  4. The Chart gets weather information for the corresponding month.
  5. You use LineMark to create a line chart. For each day within the month, you add a LineMark. The x-axis indicates the day and the y-axis the day's average temperature.
  6. You set the color of the line chart to orange using .foregroundStyle.
  7. 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.

Line chart showing monthly temperature data

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:

  1. Add a .chartForegroundStyleScale modifier to define how the average maps to the foreground style and add a legend below the line chart.
  2. 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.
  3. 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:
    1. Set AxisMarks minimum stride to 7, as each week consists of 7 days.
    2. Use AxisGridLine to show a line across the plot area.
    3. Use AxisTick to draw tick marks.
    4. Set AxisValueLabel to be the week of the month as a number.
  4. Adjust the y-axis with .chartYAxis and AxisMarks to snap it to the leading edge of the chart instead of the default trailing edge.
  1. Set AxisMarks minimum stride to 7, as each week consists of 7 days.
  2. Use AxisGridLine to show a line across the plot area.
  3. Use AxisTick to draw tick marks.
  4. 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.

Line Chart showing monthly temperature data

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:

  1. Import the Charts framework.
  2. Store weather data with measurements.
  3. Store the month number for which you want to view daily temperature data with month.
  4. Colors for average, lowest and highest temperatures, respectively.
  5. Create the weeklyTemperatureView computed variable to hold the contents of the chart. You'll use it in the view body.
  6. 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:

  1. measurementsByMonth(_:) returns an array of the daily weather information for the specified month.
  2. 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.