如何在UITableView中展示嵌套结构体解析的Topic与Description?
Swift UITableView展示JSON数据的实现逻辑检查
一、现有代码与需求说明
要在UITableView里展示topic和description字段,目前写了嵌套的Decodable结构体解析JSON,通过POST请求拿数据,用do/try/catch解析赋值,还实现了UITableView的数据源方法,下面是现有代码和示例JSON:
解析结构体代码
struct Topics: Decodable { let subtopics: [subTopic] struct subTopic: Decodable { var topic = "" var description = "" } }
数据请求与解析片段
// 初始化 let Topics = Topics() // 解析赋值 self.Topics = try JSONDecoder().decode(Topics.self, from: data)
UITableView数据源方法
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "topicCell") as! topicCell var subTopic = Topics?.subtopics[indexPath.row] cell.topicLabel?.text = subTopic.topic cell.descriptionTextView?.text = subTopic.description return cell } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return Topics?.subtopics.count }
示例JSON
{"subtopics":[{"topic":"Computer Science", "description":"beginner course"},{"topic":"Data Analysis", "description":"beginner course"}]}
二、存在的问题及修正方案
1. 命名规范问题
Swift里类型名(结构体、类)得用大驼峰,变量名用小驼峰。现在subTopic结构体名要改成SubTopic,自定义Cell类topicCell改成TopicCell,同时变量名别和类型名重复(比如let Topics = Topics()里的变量名和结构体名重了)。
另外结构体里的属性没必要给默认值="",示例JSON里字段都齐全,直接声明类型就行:
struct Topics: Decodable { let subtopics: [SubTopic] struct SubTopic: Decodable { let topic: String let description: String } }
2. 数据存储变量的问题
- 原代码
let Topics = Topics()会编译报错,因为Topics结构体的subtopics是let且没默认值,没有无参初始化器。得把变量声明成可选类型,初始值设为nil:
var topics: Topics?
- 解析完数据后,必须回到主线程刷新表格,不然UI不会更新:
self.topics = try JSONDecoder().decode(Topics.self, from: data) DispatchQueue.main.async { self.tableView.reloadData() }
3. UITableView数据源方法的错误
numberOfRowsInSection返回值必须是Int,但topics?.subtopics.count是可选值,直接返回会报错,得用空合运算符处理:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return topics?.subtopics.count ?? 0 }
cellForRowAt里的可选值没解包,直接用subTopic.topic会报错,得先解包:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "topicCell", for: indexPath) as! TopicCell guard let subTopic = topics?.subtopics[indexPath.row] else { return cell } cell.topicLabel?.text = subTopic.topic cell.descriptionTextView?.text = subTopic.description return cell }
4. 网络请求的注意点
网络请求要放在后台线程,别卡主线程,完整的请求示例大概是这样:
func fetchTopics() { guard let url = URL(string: "你的请求地址") else { return } var request = URLRequest(url: url) request.httpMethod = "POST" // 如果要传JSON参数,加上下面的代码 let params = ["paramKey": "paramValue"] request.httpBody = try? JSONSerialization.data(withJSONObject: params) request.setValue("application/json", forHTTPHeaderField: "Content-Type") URLSession.shared.dataTask(with: request) { [weak self] data, response, error in guard let self = self else { return } if let error = error { print("请求出错:\(error)") return } guard let data = data else { print("没拿到返回数据") return } do { self.topics = try JSONDecoder().decode(Topics.self, from: data) DispatchQueue.main.async { self.tableView.reloadData() } } catch { print("解析失败:\(error)") } }.resume() }
三、修正后的核心代码示例
把上面的修正点整合后,核心代码大概是这样:
// 解析结构体 struct Topics: Decodable { let subtopics: [SubTopic] struct SubTopic: Decodable { let topic: String let description: String } } // 视图控制器内的变量和方法 class YourViewController: UIViewController { var topics: Topics? @IBOutlet weak var tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() tableView.dataSource = self fetchTopics() } func fetchTopics() { guard let url = URL(string: "你的请求URL") else { return } var request = URLRequest(url: url) request.httpMethod = "POST" // 按需添加请求参数 let params = ["key": "value"] request.httpBody = try? JSONSerialization.data(withJSONObject: params) request.setValue("application/json", forHTTPHeaderField: "Content-Type") URLSession.shared.dataTask(with: request) { [weak self] data, response, error in guard let self = self else { return } guard error == nil, let data = data else { return } do { self.topics = try JSONDecoder().decode(Topics.self, from: data) DispatchQueue.main.async { self.tableView.reloadData() } } catch { print("解析失败:\(error.localizedDescription)") } }.resume() } } // UITableView数据源扩展 extension YourViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { topics?.subtopics.count ?? 0 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "topicCell", for: indexPath) as! TopicCell guard let subTopic = topics?.subtopics[indexPath.row] else { return cell } cell.topicLabel.text = subTopic.topic cell.descriptionTextView.text = subTopic.description return cell } }
内容的提问来源于stack exchange,提问作者user19549234
相关产品推荐
相关产品推荐

