You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 20:36:18