Показать JSON как раздел TableView и строку TableView в Swift 4.2 без Decodable? - PullRequest
0 голосов
/ 29 мая 2020

У меня ниже 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"
 }
]
}

Я хочу показать выше JSON ответ в TableView как: ключ «дата» должен быть сгруппирован в разделе TableView, а строки - как Клавиша "name"

Могут ли мне помочь, как это сделать? Заранее спасибо.

Ответы [ 2 ]

1 голос
/ 30 мая 2020

Прежде всего, вам нужно создать модель, чтобы вы могли преобразовать свой 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!")
}

. Теперь мы можем превратить этот блок кода в функцию, которая возвращает события, или ноль, если он не может декодировать строку.

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

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
    }
}

Вероятно, вы получаете эти JSON из Интернета, поэтому вам нужно обрабатывать состояние «загрузка», когда вы возвращаете JSON из API. Это легко сделать, превратив events var в необязательный, установив его при получении JSON и перезагрузив данные из tableView

0 голосов
/ 06 июня 2020

Я сделал вышеуказанные решения, используя 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
   }
}
...