优化Swift Charts性能:降低高CPU占用并维持屏幕刷新率
优化Swift Chart高CPU占用的解决方案
你的代码CPU占用居高不下的核心原因是每帧全量替换数据源+View层重复计算,导致三个Chart被迫频繁执行全量重绘。以下是针对性的优化方案:
1. 避免全量替换数据源,改用滚动增量更新
原代码中_regen每次生成全新的300个DataPoint数组并替换@Published var data,SwiftUI会判定整个数据源完全变更,触发三个Chart的全量重绘。改成滚动更新:保持数组引用不变,每次添加新数据时移除最旧的数据,仅更新局部内容。
2. 提前预处理计算值,减少View层开销
原Chart中$0.x - viewModel.data[0].x和各类y轴变换计算,会在每帧重绘时重复执行。把这些计算逻辑移到ViewModel中提前处理,直接给View层提供最终可用的数值。
3. 同步屏幕刷新率,避免过度更新
用CADisplayLink替代Task.sleep,确保更新频率与屏幕刷新率完全同步(比如60Hz/120Hz),避免不必要的额外更新操作。
修改后的完整代码
import SwiftUI import Charts import UIKit struct DataPoint: Identifiable { let id: Int let relativeX: Int // 提前计算的相对X值 let baseY: Float let transformedY1: Float // 预处理1 - y let transformedY2: Float // 预处理y * 0.2 } class ViewModel: ObservableObject { @Published var data: [DataPoint] = [] private var displayLink: CADisplayLink? private let maxDataCount = 300 private var currentX = 0 init() { // 初始化初始数据集 for x in 0..<maxDataCount { let y = sin(Float(x) / 10.0) let point = DataPoint( id: x, relativeX: x, baseY: y, transformedY1: 1 - y, transformedY2: y * 0.2 ) data.append(point) } currentX = maxDataCount // 启动与屏幕刷新率同步的更新链路 setupDisplayLink() } private func setupDisplayLink() { displayLink = CADisplayLink(target: self, selector: #selector(updateData)) displayLink?.add(to: .main, forMode: .common) } @objc private func updateData() { currentX += 1 let y = sin(Float(currentX) / 10.0) let newPoint = DataPoint( id: currentX, relativeX: currentX - (currentX - maxDataCount), // 维持相对X从0开始 baseY: y, transformedY1: 1 - y, transformedY2: y * 0.2 ) // 滚动更新:移除最旧数据,添加新数据 DispatchQueue.main.async { [weak self] in guard let self = self else { return } if self.data.count >= self.maxDataCount { self.data.removeFirst() } self.data.append(newPoint) } } deinit { displayLink?.invalidate() } } struct ContentView: View { @StateObject var viewModel: ViewModel = ViewModel() var body: some View { VStack { Chart(viewModel.data) { point in LineMark( x: .value("", point.relativeX), y: .value("Value", point.transformedY1) ) } Chart(viewModel.data) { point in LineMark( x: .value("", point.relativeX), y: .value("Value", point.transformedY2) ) } Chart(viewModel.data) { point in LineMark( x: .value("", point.relativeX), y: .value("Value", point.baseY) ) } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
额外优化建议
- 如果业务不需要严格每帧更新,可以降低更新频率(比如每秒30次),进一步降低CPU占用。
- 给Chart添加
chartXScale(domain:)固定X轴范围,避免Chart每次重绘时重新计算轴范围的开销。 - 确保
DataPoint的id唯一且稳定,避免SwiftUI错误识别元素变更导致的无效重绘。
内容的提问来源于stack exchange,提问作者niklassaers
相关产品推荐
相关产品推荐

