Swift中UITableView无法加载HealthKit运动数据的问题求助
问题描述
想要在UITableView中展示用户运动信息(活动名称、开始日期、时长),但表格始终无法填充数据。已确认dataValues数组有正确数据(点击按钮能打印10条数据),尝试通过按钮调用reloadData()、在异步请求后刷新表格均无效。用静态数组填充表格正常,说明表格配置无问题。
相关代码
import UIKit import HealthKit class ViewController: UIViewController, UITableViewDataSource{ /* --------Global variables for queries ------------*/ let healthStore = HealthData.healthStore let workoutType = HKObjectType.workoutType() //Creating the table that shows activities @IBOutlet weak var workoutTable: UITableView! /* ---------- UI element definitions--------------- */ @IBAction func checkData(_ sender: UIButton){ print(dataValues.count) //this prints 10, which is correct print(dataValues) //this prints the correct values. DispatchQueue.main.async{ self.workoutTable.reloadData() //this doesn't work for some reason. } } /*--------- Variables for the table objects --------*/ struct workoutStruct { var workoutActivity: String var workoutDate: Date var workoutDuration: Double } var dataValues = [workoutStruct]() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. requestAuthorization() async{ let dataValues = await workoutQueryii() self.dataValues = dataValues DispatchQueue.main.async { self.workoutTable.reloadData() } } } //function for setting the number of rows in the UI table func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return dataValues.count } //function for mapping the data from dataValues array to the table view. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { //use the current row as the array value let workout = dataValues[indexPath.row] //identify the cell in the table view let cell = workoutTable.dequeueReusableCell(withIdentifier: "workoutCell", for: indexPath) as! CustomTableViewCell cell.activityLabel.text = workout.workoutActivity cell.dateLabel.text = funcDateFormatter(date: workout.workoutDate) cell.durationLabel.text = workout.workoutDuration.formatted() return cell } func workoutQueryii() async -> [workoutStruct] { let sort = [NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)] //this query finds the users workout samples based on the parameters set below. let query = HKSampleQuery( sampleType: workoutType, predicate: nil, limit: 10, sortDescriptors: sort) { (query, resultsOrNil, error) in self.dataValues = [] //process results //create an array for the HKWorkout objects to be passed to in the query var workoutsQ: [HKWorkout] = [] if let results = resultsOrNil { for result in results { if let workout = result as? HKWorkout { //here's a workout object workoutsQ.append(workout) var dataValue = workoutStruct( workoutActivity: workout.workoutActivityType.name, workoutDate: workout.startDate, workoutDuration: workout.duration ) self.dataValues.append(dataValue) } } } } self.healthStore.execute(query) print(dataValues.count) //this returns 0 return dataValues } //End of class }
解决方案
核心问题
workoutQueryii函数中,HKSampleQuery是异步执行的,但你在调用healthStore.execute(query)后立刻返回dataValues,此时查询尚未完成,返回的是空数组。后续虽然在查询回调中给self.dataValues赋值,但viewDidLoad里已经把这个空数组重新赋值给了全局的dataValues,导致表格始终拿到空数据。
修复步骤
正确异步等待查询结果
使用withCheckedContinuation等待查询完成后再返回数据,避免提前返回空数组:func workoutQueryii() async -> [workoutStruct] { return await withCheckedContinuation { continuation in let sort = [NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)] let query = HKSampleQuery( sampleType: workoutType, predicate: nil, limit: 10, sortDescriptors: sort) { (query, resultsOrNil, error) in var tempData = [workoutStruct]() guard let results = resultsOrNil else { continuation.resume(returning: tempData) return } for result in results { if let workout = result as? HKWorkout { let dataValue = workoutStruct( workoutActivity: workout.workoutActivityType.name, workoutDate: workout.startDate, workoutDuration: workout.duration ) tempData.append(dataValue) } } continuation.resume(returning: tempData) } self.healthStore.execute(query) } }移除冗余的全局数组操作
原代码在查询回调中修改self.dataValues属于多余操作,直接通过continuation返回处理好的临时数组即可,避免数组被多次修改导致的逻辑混乱。确认表格数据源代理
在viewDidLoad中添加数据源绑定,确保表格能正确获取数据:override func viewDidLoad() { super.viewDidLoad() workoutTable.dataSource = self // 新增这行 requestAuthorization() async{ let dataValues = await workoutQueryii() self.dataValues = dataValues DispatchQueue.main.async { self.workoutTable.reloadData() } } }
效果验证
修改后,viewDidLoad中的异步调用会拿到查询完成后的有效数据,赋值给dataValues后刷新表格就能正常显示内容;点击checkData按钮时,reloadData也能正常刷新表格。
内容的提问来源于stack exchange,提问作者SkimoBen
相关产品推荐
相关产品推荐

