Skip to main content

Overview

The ClimateChartView component displays historical temperature data for the current date across multiple decades, visualizing local climate trends. It uses iOS Charts framework to render a bar chart showing how temperatures have changed over time, with color-coded bars indicating warming or cooling trends.

Visual Display

The chart presents:
  • Header with trend icon and description
  • Temperature unit toggle (Celsius/Fahrenheit)
  • Change summary showing temperature difference from baseline year
  • Bar chart with data points for each decade
  • Color-coded bars (blue for cooling, gray for neutral, orange/red for warming)
  • Value labels above each bar

Component Definition

struct ClimateChartView: View {
    let data: [ClimateDataPoint]
    @Binding var useFahrenheit: Bool
    let formatTemp: (Double) -> String
    let formatDiff: (Double) -> String
    
    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            // Header
            HStack {
                VStack(alignment: .leading, spacing: 4) {
                    HStack {
                        Image(systemName: "chart.line.uptrend.xyaxis")
                            .foregroundColor(.orange)
                        Text("Local Climate Trend")
                            .font(.headline)
                    }
                    
                    Text("Temperature on this day across decades")
                        .font(.caption)
                        .foregroundColor(.secondary)
                }
                
                Spacer()
                
                // Unit toggle
                HStack(spacing: 8) {
                    Text("°C")
                        .font(.caption)
                        .foregroundColor(!useFahrenheit ? .primary : .secondary)
                    
                    Toggle("", isOn: $useFahrenheit)
                        .labelsHidden()
                    
                    Text("°F")
                        .font(.caption)
                        .foregroundColor(useFahrenheit ? .primary : .secondary)
                }
            }
        }
        .padding()
        .background(Color.cardBackground)
        .clipShape(RoundedRectangle(cornerRadius: 16))
    }
}

Props / Parameters

data
[ClimateDataPoint]
required
Array of historical temperature data points. Each ClimateDataPoint contains:
  • year: The year (e.g., 1980, 1990, 2000)
  • temperature: Temperature in Celsius for that year
useFahrenheit
Binding<Bool>
required
Binding to control whether temperatures are displayed in Fahrenheit or Celsius. User can toggle this with the built-in switch.
formatTemp
(Double) -> String
required
Closure that formats temperature values for display. Should handle both Celsius and Fahrenheit formatting.
formatDiff
(Double) -> String
required
Closure that formats temperature differences (e.g., “+2.1°F”). Used for the change summary.

Key Features

Temperature Unit Toggle

iOS-style toggle switch for Celsius/Fahrenheit:
HStack(spacing: 8) {
    Text("°C")
        .font(.caption)
        .foregroundColor(!useFahrenheit ? .primary : .secondary)
    
    Toggle("", isOn: $useFahrenheit)
        .labelsHidden()
    
    Text("°F")
        .font(.caption)
        .foregroundColor(useFahrenheit ? .primary : .secondary)
}

Change Summary

Displays temperature change from baseline year:
private var temperatureChange: Double {
    guard let first = data.first, let last = data.last else { return 0 }
    return last.temperature - first.temperature
}

HStack {
    Text("Change since \(baselineYear)")
        .font(.subheadline)
        .foregroundColor(.secondary)
    
    Spacer()
    
    Text(formatDiff(temperatureChange))
        .font(.title2)
        .fontWeight(.bold)
        .foregroundColor(temperatureChange > 0 ? .red : .blue)
}

Bar Chart (iOS 16+)

Uses Swift Charts framework for modern visualization:
if #available(iOS 16.0, *) {
    Chart(data) { point in
        BarMark(
            x: .value("Year", String(point.year)),
            y: .value("Temp", displayTemperature(point.temperature))
        )
        .foregroundStyle(barColor(for: point))
        .cornerRadius(4)
        .annotation(position: .top) {
            Text(formatTemp(point.temperature))
                .font(.caption2)
                .foregroundColor(.secondary)
        }
    }
    .chartYAxis {
        AxisMarks(position: .leading)
    }
    .frame(height: 180)
}

Color Coding Logic

Bars are colored based on temperature change from baseline:
private func barColor(for point: ClimateDataPoint) -> Color {
    guard let baseline = data.first else { return .gray }
    let diff = point.temperature - baseline.temperature
    
    if diff > 2 {
        return .red
    } else if diff > 0.5 {
        return .orange
    } else if diff < -0.5 {
        return .blue
    } else {
        return .gray
    }
}
  • Red: +2°C or more warming
  • Orange: +0.5°C to +2°C warming
  • Gray: -0.5°C to +0.5°C (neutral)
  • Blue: More than -0.5°C cooling

Fallback for iOS 15 and Earlier

Manual bar chart rendering for older iOS versions:
else {
    // Fallback for older iOS
    HStack(alignment: .bottom, spacing: 8) {
        ForEach(data) { point in
            VStack {
                Text(formatTemp(point.temperature))
                    .font(.caption2)
                
                RoundedRectangle(cornerRadius: 4)
                    .fill(barColor(for: point))
                    .frame(height: barHeight(for: point))
                
                Text(String(point.year))
                    .font(.caption2)
            }
        }
    }
    .frame(height: 180)
}

Temperature Conversion

Automatic conversion based on unit preference:
private func displayTemperature(_ celsius: Double) -> Double {
    useFahrenheit ? (celsius * 9 / 5) + 32 : celsius
}

Usage in App

@State private var useFahrenheit = true

ClimateChartView(
    data: [
        ClimateDataPoint(year: 1980, temperature: 18.5),
        ClimateDataPoint(year: 1990, temperature: 19.2),
        ClimateDataPoint(year: 2000, temperature: 19.8),
        ClimateDataPoint(year: 2010, temperature: 20.1),
        ClimateDataPoint(year: 2020, temperature: 20.8),
        ClimateDataPoint(year: 2024, temperature: 21.2)
    ],
    useFahrenheit: $useFahrenheit,
    formatTemp: { temp in
        if useFahrenheit {
            return String(format: "%.1f°F", (temp * 9/5) + 32)
        } else {
            return String(format: "%.1f°C", temp)
        }
    },
    formatDiff: { diff in
        if useFahrenheit {
            return String(format: "%+.1f°F", diff * 9/5)
        } else {
            return String(format: "%+.1f°C", diff)
        }
    }
)

Data Context

The chart visualizes how temperatures on the current calendar date (e.g., March 4th) have changed across different decades, helping users understand local climate trends at a glance.

Requirements

  • iOS 16.0+ for Swift Charts support
  • Falls back to custom rendering on iOS 15 and earlier

Source Location

BreezeApp/Views/Environmental/ClimateChartView.swift:4

Build docs developers (and LLMs) love