iOS Line Chart无法显示图表条目问题求助:健身APP折线图二次进入页面后空白
解决折线图重复进入后空白的问题
看起来你遇到的问题主要是因为图表数据没有持久化存储、视图重复创建,还有几个逻辑上的小错误导致的。我帮你梳理一下问题点,然后给出修复后的方案:
问题分析
- 每次进入都重置数据数组:在
viewDidLayoutSubviews里每次都新建entries数组,没有从UserDefaults加载历史的图表条目,导致只有满足条件时才会有数据,否则数组为空,图表就空白了。 - 重复创建LineChartView:每次
viewDidLayoutSubviews都会创建新的linechart实例并添加到视图,不仅浪费资源,还可能导致旧视图被覆盖或者显示异常。 - 时间判断错误:你用了
Calendar.current.component(.minute, from: date)来获取当前小时,这其实拿到的是分钟数,应该用.hour才对,这会导致你的条件判断逻辑完全失效。 - UserDefaults键名错误:更新小时计数时用了
"daysofexerciserecorded",但你读取的是"hoursofexerciserecorded",导致计数没有正确保存。
修复方案
我们需要做这几件事:
- 将图表条目持久化存储,而不是每次都新建空数组
- 只初始化一次LineChartView,避免重复添加
- 修正时间判断的逻辑
- 修复UserDefaults的键名错误
修复后的代码
import UIKit import Charts class DaysViewController: UIViewController, ChartViewDelegate { @IBOutlet weak var graphsview: UIView! var linechart: LineChartView! // 用来存储历史图表条目,或者从UserDefaults加载 private var chartEntries: [ChartDataEntry] = [] override func viewDidLoad() { super.viewDidLoad() // 只初始化一次LineChartView setupLineChart() // 从UserDefaults加载历史条目 loadSavedEntries() // 检查是否需要添加新的条目 checkAndAddNewEntry() // 更新图表显示 updateChartData() } private func setupLineChart() { linechart = LineChartView(frame: graphsview.bounds) linechart.delegate = self linechart.center = graphsview.center graphsview.addSubview(linechart) // 添加到graphsview而不是view,避免布局混乱 } private func loadSavedEntries() { // 把条目数组转换成Data存储到UserDefaults,实现持久化 if let savedData = UserDefaults.standard.data(forKey: "chartEntries"), let entries = try? JSONDecoder().decode([ChartDataEntry].self, from: savedData) { chartEntries = entries } } private func saveEntries() { // 把条目数组转换成Data保存到UserDefaults if let data = try? JSONEncoder().encode(chartEntries) { UserDefaults.standard.set(data, forKey: "chartEntries") } } private func checkAndAddNewEntry() { let caloriesBurnt = UserDefaults.standard.double(forKey: "calories burnt today") var hoursRecorded = UserDefaults.standard.integer(forKey: "hoursofexerciserecorded") let date = Date() let calendar = Calendar.current let currentHour = calendar.component(.hour, from: date) // 修正为获取当前小时 let previousHour = UserDefaults.standard.integer(forKey: "hour") // 判断当前小时是否比记录的小时大,避免重复添加条目 if currentHour > previousHour { hoursRecorded += 1 let newEntry = ChartDataEntry(x: Double(hoursRecorded), y: caloriesBurnt) chartEntries.append(newEntry) // 更新UserDefaults的值,统一键名 UserDefaults.standard.set(hoursRecorded, forKey: "hoursofexerciserecorded") UserDefaults.standard.set(currentHour, forKey: "hour") // 保存新的条目 saveEntries() } } private func updateChartData() { let set = LineChartDataSet(entries: chartEntries) set.colors = ChartColorTemplates.colorful() let data = LineChartData(dataSet: set) linechart.data = data linechart.notifyDataSetChanged() // 通知图表更新数据 } // 处理视图布局变化时调整图表大小 override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() linechart.frame = graphsview.bounds } }
关键修改点说明
- 持久化图表条目:用
JSONEncoder和JSONDecoder把ChartDataEntry数组转换成Data存储到UserDefaults,这样每次进入ViewController都能加载之前的历史数据,不会丢失。 - 单一实例的LineChartView:在
viewDidLoad里只初始化一次图表,viewDidLayoutSubviews只负责调整大小,避免重复添加视图。 - 修正时间逻辑:把获取当前时间的组件改成
.hour,确保小时判断正确,只有当进入新的小时时才添加新的条目。 - 修复键名错误:统一使用
"hoursofexerciserecorded"作为小时计数的键名,确保数据正确保存和读取。 - 分离逻辑:把图表初始化、数据加载、添加新条目、更新图表的逻辑拆分成独立的方法,代码更清晰,也更容易维护。
这样修改后,无论你多少次通过segue进入这个ViewController,折线图都会显示所有历史的卡路里消耗数据点,并且每进入新的小时会自动添加新的条目。
内容的提问来源于stack exchange,提问作者Kiran Lim
相关产品推荐
相关产品推荐

