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 框架构建一个实时更新的股票价格折线图。通过结合 ObservableObject 和 Timer,我们将实现动态数据模拟和可视化,展示 SwiftUI 在金融类应用中的实际应用场景。


实现步骤

1. 数据模型设计

struct StockPrice: Identifiable {
    let id = UUID()
    let timestamp: Date
    let price: Double
}

class StockDataModel: ObservableObject {
    @Published var prices: [StockPrice] = []
    private var timer: Timer?
    
    init() {
        startMockDataStream()
    }
    
    private func startMockDataStream() {
        timer = Timer.scheduledTimer(
            withTimeInterval: 1.0,
            repeats: true
        ) { [weak self] _ in
            self?.generateNewDataPoint()
        }
    }
    
    private func generateNewDataPoint() {
        let lastPrice = prices.last?.price ?? 100.0
        let newPrice = lastPrice + Double.random(in: -2.0...2.0)
        let newDataPoint = StockPrice(
            timestamp: Date(),
            price: max(0, newPrice) // 确保价格不为负
        
        prices.append(newDataPoint)
        if prices.count > 30 { // 只保留最近30个数据点
            prices.removeFirst()
        }
    }
}

2. 基础折线图实现

struct StockChartView: View {
    @StateObject private var model = StockDataModel()
    
    var body: some View {
        Chart(model.prices) { data in
            LineMark(
                x: .value("Time", data.timestamp),
                y: .value("Price", data.price)
            )
            .interpolationMethod(.catmullRom)
            
            PointMark(
                x: .value("Time", data.timestamp),
                y: .value("Price", data.price)
            )
            .symbolSize(50)
        }
        .chartXAxis {
            AxisMarks(values: .automatic) { value in
                AxisGridLine()
                AxisTick()
                AxisValueLabel {
                    if let date = value.as(Date.self) {
                        Text(date, style: .time)
                    }
                }
            }
        }
        .chartYScale(domain: [
            (model.prices.map { $0.price }.min() ?? 0) - 5,
            (model.prices.map { $0.price }.max() ?? 0) + 5
        ])
        .padding()
    }
}

3. 添加实时交互功能

struct InteractiveStockChart: View {
    @StateObject private var model = StockDataModel()
    @State private var selectedPrice: StockPrice?
    
    var body: some View {
        VStack {
            Chart(model.prices) { data in
                LineMark(...) // 同基础实现
                
                if let selectedPrice, selectedPrice.id == data.id {
                    RuleMark(x: .value("Selected", data.timestamp))
                        .foregroundStyle(.gray.opacity(0.3))
                        .annotation(position: .top) {
                            VStack(alignment: .leading) {
                                Text("Price: \(data.price.formatted(.number.precision(.fractionLength(2))))")
                                Text(data.timestamp, style: .time)
                            }
                            .padding()
                            .background {
                                RoundedRectangle(cornerRadius: 4)
                                    .fill(.background)
                                    .shadow(radius: 2)
                            }
                        }
                }
            }
            .chartOverlay { proxy in
                GeometryReader { geometry in
                    Rectangle()
                        .fill(.clear)
                        .contentShape(Rectangle())
                        .gesture(
                            DragGesture()
                                .onChanged { value in
                                    let xPosition = value.location.x
                                    if let date: Date = proxy.value(atX: xPosition) {
                                        selectedPrice = model.prices
                                            .sorted { abs($0.timestamp.timeIntervalSince(date)) < abs($1.timestamp.timeIntervalSince(date)) }
                                            .first
                                    }
                                }
                        )
                }
            }
            
            HStack {
                Button("Pause") { model.pauseUpdates() }
                Button("Resume") { model.resumeUpdates() }
            }
        }
    }
}

4. 性能优化技巧

  1. 数据采样:当数据点超过100个时,采用降采样显示
.samplingStrategy(.count(100))
  1. 使用 DrawingGroup 进行GPU加速:
.drawingGroup()
  1. 异步渲染:
.task {
    await model.fetchInitialData()
}

完整案例展示

![股票折线图示例]

关键知识点

  1. ObservableObject 实现数据动态更新
  2. LineMark 和 PointMark 的组合使用
  3. 手势交互与 chartOverlay 的结合
  4. 时间序列数据的X轴格式化
  5. 实时数据流的性能优化

扩展练习

  1. 添加成交量柱状图(双Y轴)
  2. 实现K线图(蜡烛图)展示
  3. 接入真实的股票API(如Alpha Vantage)
Last Updated:: 5/18/25, 10:48 AM