0

I currently have a json response from json which is a [NSDictionary]. This is displayed in a tableview and I was able to set the sorted dates into headers however I am having a difficulties in setting the uilabel in the cellForRowAt function. The tableview I was looking to display has a header title of sorted dates(which I already have) and under the section are the names with the same date as the header. I have provided the code for this below. Thank you for your help and suggestions.

import UIKit
import Alamofire

class JSONTableViewController: UITableViewController 

{

var responseValue:[NSDictionary] = []
var sortedResponsevalue:[NSDictionary] = []
var sectionHeaderArray:[String] = []
var rowTitle:[String] = []

override func viewDidLoad() {
    super.viewDidLoad()
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    for response in self.responseValue {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd"
        let responseDate = response.object(forKey: "date") as! String
        let date = dateFormatter.date(from: responseDate)
        print(date!.toString(dateFormat: "MMM d, yyyy"))
        self.sectionHeaderArray.append(date!.toString(dateFormat: "MMM d, yyyy"))
        self.rowTitle.append(response.object(forKey: "name") as! String)
    }
    print(self.sectionHeaderArray.count)
}

override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    return self.sectionHeaderArray[section]
}

// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
    // #warning Incomplete implementation, return the number of sections
    return self.sectionHeaderArray.count
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return self.sectionHeaderArray[section].count
}

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return 80
}


override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "jsonCell", for: indexPath) as! JSONTableViewCell

    if let nameString = self.responseValue[indexPath.row].object(forKey: "name") as? String {
        cell.jsonLabel.text = nameString

    }


      return cell
    }
}
jiren
  • 7
  • 6

2 Answers2

0

What I understand from your question is: You are getting the same title for "nameString" in cellForRow Method.

The problem is

if let nameString = self.responseValue[indexPath.row].object(forKey: "name") as? String {
        cell.jsonLabel.text = nameString
    }

You are passing the number of sections self.sectionHeaderArray.count. as the total section = 100 and each section has only 1 row, so title will always be remains the same.

TRY THIS : use indexPath.section instead of indexPath.row to get different titles

if let nameString = self.responseValue[indexPath.section].object(forKey: "name") as? String {
        cell.jsonLabel.text = nameString
    }

EDIT

class JSONTableViewController: UITableViewController
{
    var responseValue:[NSDictionary] = []
    var sortedResponsevalue:[NSDictionary] = []
    var sectionHeaderArray = [[String: Any]]()

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd"
        var currentDate: String? = nil
        var nameArray = [String]()
        for (index,response) in self.responseValue.enumerated() {
            let responseDate = response.object(forKey: "date") as! String
            let date = dateFormatter.date(from: responseDate)
            let dateString = date!.toString(dateFormat: "MMM d, yyyy")
            let nameString = response.object(forKey: "name") as! String
            if currentDate == nil {
                // FIRST TIME
                currentDate = dateString
                nameArray.append(nameString)

            } else {
                // CHECK IF DATES EQUAL, THEN KEEP ADDING
                if currentDate == dateString {
                    nameArray.append(nameString)
                    if index == self.responseValue.count - 1 {
                        let dictToAppend: [String : Any] = ["date": currentDate!, "names": nameArray]
                        self.sectionHeaderArray.append(dictToAppend)
                    }
                } else {
                    let dictToAppend: [String : Any] = ["date": currentDate!, "names": nameArray]
                    self.sectionHeaderArray.append(dictToAppend)
                    currentDate = dateString
                    nameArray.removeAll()
                    nameArray.append(nameString)
                }
            }
        }

    }

    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        let dictInfo = self.sectionHeaderArray[section]
        let dateString = dictInfo["date"]
        return dateString as? String
    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return self.sectionHeaderArray.count
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        let dictInfo = self.sectionHeaderArray[section]
        let namesArray = dictInfo["names"] as! [String]

        return namesArray.count
    }

    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 80
    }


    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "jsonCell", for: indexPath) as! JSONTableViewCell

        let dictInfo = self.sectionHeaderArray[indexPath.section]
        let namesArray = dictInfo["names"] as! [String]
        let nameString = namesArray[indexPath.row]
        cell.jsonLabel.text = nameString

        return cell
    }
}

Try and share the results.

Bhavin Kansagara
  • 2,866
  • 1
  • 16
  • 20
  • I will try this. I am not sure how to define the return value for `override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.sectionHeaderArray[section].count }` – jiren Jul 26 '18 at 13:52
  • `if let nameString = self.responseValue[indexPath.section].object(forKey: "name") as? String { cell.jsonLabel.text = nameString }` is already setting the correct values for cellForRowAt however the problem now is the correct number of rows for each result. Thank you for your help – jiren Jul 26 '18 at 13:57
  • numberOfRowsInSection should return 1, as there is one to one mapping for the dates and names. – Bhavin Kansagara Jul 26 '18 at 14:01
  • Is there a way to make it dynamic based on the json response? I have 2 items for the dates Jun 20, 2018, Apr 26, 2018, Apr 16, 2018, Feb 23, 2018 etc,. Would it be possible to return the number of names with the same date in the same section? thank you – jiren Jul 26 '18 at 14:06
  • For that, it need to update your datasource and formate the response, to separate it based on the dates and names for that particular date. – Bhavin Kansagara Jul 26 '18 at 14:17
  • 1
    You really saved me. This did the job just right. I cant thank you enough Bhavin. Thank you for your patience. – jiren Jul 26 '18 at 15:20
  • @jiren accept it as an answer, if this fixed your problem. – Bhavin Kansagara Jul 26 '18 at 15:23
  • Done. Thank you again Bhavin – jiren Jul 27 '18 at 01:06
0

This is a different approach using the Decodable protocol to decode the JSON into a struct and the optimized initializer Dictionary(grouping:by:) to group the items by date.

Create a struct (can be outside the view controller)

struct Response : Decodable {
    let id : Int
    let date : Date
    let name : String
}

Inside the view controller create a dateformatter for the header titles

let dateFormatter : DateFormatter = {
    let formatter = DateFormatter()
    formatter.locale = Locale(identifier: "en_US_POSIX")
    formatter.dateFormat = "MMM d, yyyy"
    return formatter
}()

and a native dictionary (never NSDictionary) for the data source and an array for the sections which are Date instances. The benefit of using Date is the ability to be sorted (the string date format cannot be sorted properly)

var sections = [Date]()
var dataSource = [Date:[Response]]()

Assuming data is the JSON data in viewDidLoad decode data into the struct and populate the data source. The method Dictionary(grouping:by:) groups the array of Response into a dictionary with the date as key.

override func viewDidLoad() {
    super.viewDidLoad()

    // put here the code to retrieve the JSON data in the variable `data`
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "yyyy-MM-dd"
    let decoder = JSONDecoder()
    decoder.dateDecodingStrategy = .formatted(dateFormatter)
    do {
        let result = try decoder.decode([Response].self, from: data)
        dataSource = Dictionary(grouping: result, by: {$0.date})
        sections = dataSource.keys.sorted()
        self.tableView.reloadData()
    } catch {
        print(error)
    }
}

The datasource and delegate methods are

override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    return dateFormatter.string(from: sections[section])
}

// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
    // #warning Incomplete implementation, return the number of sections
   return sections.count
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    let sectionDate = sections[section]
    return dataSource[sectionDate]!.count
}

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return 80
}


override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "jsonCell", for: indexPath) as! JSONTableViewCell

    let sectionDate = sections[indexPath.section]
    let item = dataSource[sectionDate]![indexPath.row]
    cell.jsonLabel.text = item.name

    return cell
}
vadian
  • 274,689
  • 30
  • 353
  • 361