【问题标题】:Show JSON as TableView Section and TableView row in Swift 4.2 without Decodable?在 Swift 4.2 中将 JSON 显示为 TableView 部分和 TableView 行而不可解码?
【发布时间】:2020-09-16 22:02:47
【问题描述】:

我有下面带有关键“事件”的 JSON 响应数组

{
  "events": [
 {
  "name": "event foo",
  "date": "2020-05-22",
  "time": "7:00",
  "am_or_pm": "PM",
  "day": "Saturday",
  "description": "test "
},
{
  "name": "event bar",
  "date": "2020-05-22",
  "time": "7:00",
  "am_or_pm": "PM",
  "day": "Saturday",
  "description": "test2"
},
{
  "name": "event foobar",
  "date": "2020-05-24",
  "time": "11:00",
  "am_or_pm": "PM",
  "day": "Saturday",
  "description": "test3"
}, 
{
  "name": "event foobar",
  "date": "2020-05-24",
  "time": "11:00",
  "am_or_pm": "PM",
  "day": "Saturday",
  "description": "test3"
 }
]
}

我想在 TableView 中将 JSON 响应显示为: “日期”键应该是 TableView Section 分组和 Rows 作为“名称”键

能不能帮我一下,谢谢。

【问题讨论】:

    标签: tableview swift4.2


    【解决方案1】:

    首先,您需要创建一个模型,以便将您的 JSON 转换为可用的对象。为此,好的做法是使用 Codable 协议,它会自动将您的 JSON 键映射到结构/类变量

    struct Event: Codable {
        var name: String!
        var date: String!
        var time: String!
        var am_or_pm: String!
        var day: String!
        var description: String!
    }
    
    struct Events: Codable {
        var events: [Event]!
    }
    

    现在您已经有了将从 JSON 生成的模型,您需要将 JSON 解码为您的模型。有很多方法可以做到,我喜欢使用 JSONEncoder/JSONDecoder。 因此,假设您的 JSON 字符串存储在变量 myJsonString 中,您需要

    if let jsonData = myJsonString.data(using: .utf8) {
        do {
            let events = try JSONDecoder().decode(Events.self, from: jsonData)
        } catch {
            print("Error decoding json!", error.localizedDescription)
        }
    } else {
        print("Failed to get bytes from string!")
    }
    

    现在,我们可以将该代码块转换为返回事件的函数,如果它无法解码字符串,则返回 nil。

    func getEvents(from jsonString: String) -> Events? {
        guard let jsonData = myJsonString.data(using: .utf8) else { return nil }
        return try? JSONDecoder().decode(Events.self, from: jsonData)
    }
    

    酷!我们现在可以轻松地基于 JSON 字符串获取我们的事件!我们接下来要做的就是填充 tableView! 为此,我们可以在 Events 结构中创建一些函数,这些函数将返回填充我们部分所需的内容。第一个函数将返回我们的 tableView 的部分,第二个函数将返回给定部分的所有项目。让我们修改Events 结构体

    struct Events: Codable {
        var events: [Event]!
    
        func getSections() -> [String] {
            return Array(Set(self.events.map { $0.date }))
        }
    
        func getItems(forSection dateSection: String) -> [Section] {
            return self.events.filter {$0.date == dateSection}
        }
    }
    

    现在,在您的 TableView 数据源类中,您需要使用我们创建的模型。我给你举个例子

    class YourTableView: UIViewController, UITableViewDataSource, UITableViewDelegate {
        // This is loaded from the getEvents function!
        var events: Events!
    
       func numberOfSections (in tableView: UITableView) -> Int {
            return self.events.getSections().count
        }
    
       func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            let dateSection = self.events.getSections()[section]
            return self.events.getItems(forSection: dateSection ).count
        }
    
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let dateSection = self.events.getSections()[indexPath.section]
            let currentEvent = self.getItems(forSection: dateSection)
    
             // Build your cell using currentEvent
        }
    }
    

    您可能从 Web 获取这些 JSON,因此您必须处理“加载”状态,即从 API 返回 JSON。这可以通过将events var 转换为可选项,在获取 JSON 并从 tableView 重新加载数据时设置它来轻松完成

    【讨论】:

    • 当然让我试试
    【解决方案2】:

    我使用Alamofire 5.0 完成了上述解决方案,以下是我的完整解决方案,以便它可以帮助某人:

    // 创建一个名为“Event”的模态类

    class Event {
        var name: String?
        var date: String?
        var time: String?
        var amOrPm: String?
        var day: String?
        var description: String?
    
    
    
    init(dic: [String: Any]) {
        if let name = dic["name"] as? String {
            self.name = name
        }
        if let date = dic["date"] as? String {
            self.date = date
        }
        if let time = dic["time"] as? String {
            self.time = time
        }
        if let amOrPm = dic["am_or_pm"] as? String {
            self.amOrPm = amOrPm
        }
        if let day = dic["day"] as? String {
            self.day = day
        }
        if let description = dic["description"] as? String {
            self.description = description
        }
        }
    }
    

    // 为 URL 创建一个全局类

    import Alamofire
    
    class HttpManager {
        struct Constants {
            static let baseUrl = "https://my-json-server.typicode.com/JCkshone/jsonTest"
        }
    
        func getEvents(handledResponse: @escaping (_ data: [[String: Any]])->()) {
            let request = AF.request("\(Constants.baseUrl)/db")
    
            request.responseJSON { (response: AFDataResponse<Any>) in
                guard let data = response.value as? [String: Any] else { return }
                if let events: [[String: Any]] = data["events"] as? [[String: Any]] {
                    handledResponse(events)
                }
            }
        }
    }
    

    // 最后是 ViewController.swift

    struct SectionEvent {
        var sectionName: String
        var evenst: [Event]
    }
    
    class ViewController: UIViewController {
        @IBOutlet weak var tableView: UITableView!
        let http  = HttpManager()
        var events: [Event] = []
        var tableViewData: [SectionEvent] = []
        let cell = "cellId"
    
        override func viewDidLoad() {
            super.viewDidLoad()
            fetchData()
            initTableView()
        }
    
        func fetchData() {
            http.getEvents { (data: [[String : Any]]) in
                data.forEach { item in
                    self.events.append(Event(dic: item))
                }
                self.buildData()
            }
        }
    
        func buildData() {
            events.forEach { event in
    
            let validation = validateEventExist(event: event)
            if !validation.exist {
                tableViewData.append(SectionEvent(sectionName: event.date ?? "", evenst: [event]))
            } else {
                tableViewData[validation.position].evenst.append(event)
            }
        }
        self.tableView.reloadData()
    }
    
    func validateEventExist(event: Event) -> (exist: Bool, position: Int) {
        let filterData = tableViewData.filter {$0.sectionName == event.date}
        let index = tableViewData.firstIndex { $0.sectionName == event.date}
        return (filterData.count > 0, index ?? 0)
    }
    
        func initTableView() {
            tableView.register(UITableViewCell.self, forCellReuseIdentifier: cell)
            tableView.tableHeaderView = UIView()
        }
    }
    
    extension ViewController: UITableViewDataSource, UITableViewDelegate {
    
    
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        tableViewData[section].sectionName
    }
    
    func numberOfSections(in tableView: UITableView) -> Int {
        tableViewData.count
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        tableViewData[section].evenst.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for: indexPath)
        if let name = tableViewData[indexPath.section].evenst[indexPath.row].name, let description = tableViewData[indexPath.section].evenst[indexPath.row].description {
            cell.textLabel?.text = "\(name) \n\(description)"
            cell.textLabel?.numberOfLines = 0
        }
    
        return cell
       }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-10-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-25
      相关资源
      最近更新 更多