Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift - Populate uitableview with dictionary of [String: [String: String]]

I'm new to Swift, and I am currently creating a diary app that asks the user questions. I'm storing the user's input like this:

dict = ["date": ["question1": "answer", "question2": "answer"]]

Now I need to display this data back to the user in a tableview, where "date" is a title and "question1" is the description.

I've looked online, but answers seem to reference "indexPath.row" for inputting information into a cell, but since this is a dictionary of strings, I can't do that.

Thank you for your help!

like image 264
Anthony Avatar asked Sep 13 '26 12:09

Anthony


2 Answers

Rather than using an array of dictionaries, you should consider using objects that better represent your data.

struct Question: {
    let question: String
    let answer: String
}

struct DiaryDay {
    let date: Date // Note this is a Date object, not a String
    let questions: [Question]
}

then you have

let diaryDays = DiaryDay(date: <date>, questions: 
    [Question(question: "question1": answer: "answer"),
     Question(question: "question2": answer: "answer")])

while there's a bit more code, going forward you'll find it easier to see what's happening.

It looks like you should have a section per diary day…

override func numberOfSections(in tableView: UITableView) -> Int {
    return diaryDays.count
}

and then one row per question…

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    let diaryDay = diaryDays[section]
    return diaryDay.questions.count
}

and then configure your cell…

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    // dequeue cell
    let diaryDay = diaryDays[indexPath.section]
    let question = diaryDay.questions[indexPath.row]    
    cell.question = question
    return cell
}

and show the date in the section header…

override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    let diaryDay = diaryDays[section]

    return // formatted diaryDay.date
}
like image 197
Ashley Mills Avatar answered Sep 16 '26 00:09

Ashley Mills


you will have to do a little preparation before you can display data from the dictionary type you are using. Also remember the dictionary is not order list so which order the data will be printed solely depends on system. One approach would be the following

var data = ["date1":["q1":"A1","q2":"A2","q3":"A3"],"date2":["q1":"A1","q2":"A2","q3":"A3"]] . //This is data from your example
var displayableData = [(title: String, qAndA: [(question: String, answer: String)])]() //this is what we will be needing


override func viewDidLoad() {
    super.viewDidLoad()

    //convert the whole dictionary to tuple
    displayableData = data.map { ($0.key, $0.value.map{ ($0.key, $0.value)})}

    //here we have converted the dictionary to what we need
}


override func numberOfSections(in tableView: UITableView) -> Int {
    return displayableData.count  
}

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

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

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
    let currentQA = displayableData[indexPath.section].qAndA[indexPath.row]
    cell.textLabel?.text = "\(currentQA.question) -> \(currentQA.answer)"
    return cell
}

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 30.0
}

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    let view = UIView(frame: CGRect(x: 0, y: 0, width: tableView.bounds.width, height: 30.0))
    view.backgroundColor = UIColor.lightGray
    let label = UILabel(frame: CGRect(x: 10, y: 0, width: view.bounds.width - 20, height: 30.0))
    label.text = displayableData[section].title
    view.addSubview(label)
    return view
}

This is the output

like image 28
kathayatnk Avatar answered Sep 16 '26 02:09

kathayatnk