Tailwind CSSTailwind CSS
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
  • 案例:天气预报图表应用

案例:天气预报图表应用

项目概述

在本案例中,我们将构建一个完整的天气预报应用,展示如何使用 SwiftUI Charts 框架可视化气象数据。该应用将:

  • 从公共天气API获取实时数据
  • 解析JSON响应并映射到图表模型
  • 展示多维度气象数据(温度、降水、风速)
  • 实现用户交互和动态更新

技术栈

  • SwiftUI Charts 框架
  • URLSession 网络请求
  • async/await 异步编程
  • Codable 数据解析
  • MVVM 架构模式

实现步骤

1. 数据模型设计

struct WeatherData: Codable {
    let date: Date
    let temperature: Double
    let precipitation: Double
    let windSpeed: Double
    let condition: WeatherCondition
    
    enum WeatherCondition: String, Codable {
        case sunny, cloudy, rainy, snowy
    }
}

class WeatherViewModel: ObservableObject {
    @Published var hourlyData: [WeatherData] = []
    @Published var dailyData: [WeatherData] = []
    @Published var isLoading = false
    @Published var error: Error?
}

2. API 数据获取

extension WeatherViewModel {
    func fetchWeatherData(location: String) async {
        isLoading = true
        defer { isLoading = false }
        
        guard let url = URL(string: "https://api.weatherapi.com/forecast?q=\(location)") else {
            error = APIError.invalidURL
            return
        }
        
        do {
            let (data, _) = try await URLSession.shared.data(from: url)
            let decoder = JSONDecoder()
            decoder.dateDecodingStrategy = .iso8601
            let response = try decoder.decode(WeatherAPIResponse.self, from: data)
            
            await MainActor.run {
                hourlyData = response.hourly
                dailyData = response.daily
            }
        } catch {
            self.error = error
        }
    }
}

3. 图表视图实现

struct TemperatureChartView: View {
    let data: [WeatherData]
    
    var body: some View {
        Chart(data) { item in
            LineMark(
                x: .value("Time", item.date),
                y: .value("Temperature", item.temperature)
            )
            .interpolationMethod(.catmullRom)
            .foregroundStyle(by: .value("Condition", item.condition.rawValue))
            
            PointMark(
                x: .value("Time", item.date),
                y: .value("Temperature", item.temperature)
            )
            .symbol(by: .value("Condition", item.condition.rawValue))
        }
        .chartXAxis {
            AxisMarks(values: .stride(by: .hour)) { value in
                AxisGridLine()
                AxisTick()
                AxisValueLabel(format: .dateTime.hour())
            }
        }
        .chartYAxis {
            AxisMarks(position: .leading)
        }
        .chartLegend(position: .bottom)
    }
}

4. 多图表组合

struct WeatherDashboardView: View {
    @StateObject var viewModel = WeatherViewModel()
    @State private var selectedTab: WeatherTab = .temperature
    
    enum WeatherTab: String, CaseIterable {
        case temperature = "Temperature"
        case precipitation = "Precipitation"
        case wind = "Wind Speed"
    }
    
    var body: some View {
        VStack {
            Picker("Chart Type", selection: $selectedTab) {
                ForEach(WeatherTab.allCases, id: \.self) { tab in
                    Text(tab.rawValue).tag(tab)
                }
            }
            .pickerStyle(.segmented)
            .padding()
            
            Group {
                switch selectedTab {
                case .temperature:
                    TemperatureChartView(data: viewModel.hourlyData)
                case .precipitation:
                    PrecipitationChartView(data: viewModel.dailyData)
                case .wind:
                    WindSpeedChartView(data: viewModel.hourlyData)
                }
            }
            .frame(height: 300)
            .padding()
            
            Spacer()
        }
        .task {
            await viewModel.fetchWeatherData(location: "San Francisco")
        }
        .overlay {
            if viewModel.isLoading {
                ProgressView()
            }
        }
    }
}

5. 高级功能实现

  • 天气条件图标:为不同天气条件添加自定义符号
.symbol {
    switch item.condition {
    case .sunny: Image(systemName: "sun.max.fill")
    case .cloudy: Image(systemName: "cloud.fill")
    case .rainy: Image(systemName: "cloud.rain.fill")
    case .snowy: Image(systemName: "snow")
    }
}
  • 交互式工具提示:
.chartOverlay { proxy in
    GeometryReader { geometry in
        Rectangle().fill(.clear).contentShape(Rectangle())
            .gesture(
                DragGesture()
                    .onChanged { value in
                        let x = value.location.x - geometry[proxy.plotAreaFrame].origin.x
                        if let date = proxy.value(atX: x) as Date? {
                            // 显示选定时间点的详细数据
                        }
                    }
            )
    }
}

最佳实践

  1. 数据缓存:实现本地缓存减少API调用
  2. 错误处理:提供用户友好的错误提示
  3. 性能优化:对大数据集进行采样处理
  4. 可访问性:为图表添加VoiceOver支持
  5. 主题适配:支持深色/浅色模式切换

扩展练习

  1. 添加位置搜索功能
  2. 实现温度单位切换(℃/℉)
  3. 开发watchOS小组件显示关键数据
  4. 添加天气预警通知功能
Last Updated:: 5/18/25, 10:44 AM