Jeremy Hao-Yang Choi
Jeremy Hao-Yang Choi

Reputation: 27

Implement a TableView with Sections and populating it with data from a struct

I'm mostly new to iOS and Xcode dev, so I'm just here asking for some handholding for now...

I'm making an app with a TableView of medical symptoms. I've managed to populate the tableview with data from an array and separate that into sections depending medical specialty. However, I've run into a brick wall with regards to being able to checkmark multiple symptoms in the table, and save that for later use in another ViewController.

The easiest method for me to understand is to create a dictionary with the whole list of symptoms, and then assign a boolean value to each of the symptoms depending on whether it is checked or unchecked. But I have no idea how to implement a struct that houses all the data, as well as being able to separate it into sections.

I'm also wondering whether it would be better to just keep all the data in an external file and allow the app to access the data.

I can add the code I've written so far if needed.

Help would be much appreciated!

Below is my current implementation of the data source

    let SymptomsSections = ["General", "Respiratory", "Cardiac", "Abdominal", "Neurological", "Psychiatrical"]

    let SymptomsList =
        [
                ["Dry mouth", "Malaise", "Asthenia"],
                ["Dyspnoea", "Wheezing"],
                ["Orthopnoea", "Angina"],
                ["Coffee ground vomiting", "Melaena"],
                ["Agnosia", "Apraxia"]
        ]

and the code to write it in tableView

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

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

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

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

    cell.textLabel?.text = SymptomsList[indexPath.section][indexPath.row]
    return cell
}

Upvotes: 0

Views: 1306

Answers (1)

iOSer
iOSer

Reputation: 2326

Updated answer

I ran the code shared by OP and noticed that it was crashing. The main reason why it does so is because SymptomsSections & SymptomsList does not have same number of contents, thus resulting in the out of index range crash for the smaller array which in our case is SymptomsList. For workaround I have added the line SymptomsList.indices.contains(index) which basically checks whether any object exists at the given index before accessing it. This resolved our crash. Then I proceeded to update the dictionaries and the accessing method. Also attached is the output screen for your understanding.

Please go through the code

let SymptomsSections = ["General", "Respiratory", "Cardiac", "Abdominal", "Neurological", "Psychiatrical"]

let SymptomsList = [[["name":"Dry mouth", "checked" : true], ["name":"Malaise", "checked" : false], ["name":"Asthenia", "checked" : true]],
                    [["name":"Dyspnoea", "checked" : true], ["name":"Wheezing", "checked" : false]],
                    [["name":"Orthopnoea", "checked" : false], ["name":"Angina", "checked" : true]],
                    [["name":"Coffee ground vomiting", "checked" : true], ["name":"Melaena", "checked" : true]],
                    [["name":"Agnosia", "checked" : false], ["name":"Apraxia", "checked" : true]]]


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


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

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if SymptomsList.indices.contains(section) {
        return SymptomsList[section].count
    } else {
        return 0
    }
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "SymptomsCell", for: indexPath)
    if SymptomsList.indices.contains(indexPath.section) {
        cell.textLabel?.text = SymptomsList[indexPath.section][indexPath.row]["name"] as? String
        if SymptomsList[indexPath.section][indexPath.row]["checked"] as! Bool {
            cell.accessoryType = UITableViewCellAccessoryType.checkmark
        } else {
            cell.accessoryType = UITableViewCellAccessoryType.none
        }
    } else {
        cell.textLabel?.text = nil
       cell.accessoryType = UITableViewCellAccessoryType.none
    }

    return cell
}

enter image description here

Original answer

Well, to show something you need to know that thing. Not sure how you are getting your data. But I can imagine your datasource for tableview looks something like this:

let symptoms = ["fever", "cold", "heart ache"]

You're correct when you said that the easiest way out would be using dictionary. Imagine having the updated data source being an array of dictionaries, like so:

let symptoms = [["name":"fever", "checked":true], ["name":"cold", "checked":true], ["name":"heart ache", "checked":false]]

While assigning the cell in cellForRow method. You can use something like below:

cell.titleLabel.text = symptoms[indexPath.row]["name"] 
if symptoms[indexPath.row]["checked"] {
   cell.accessoryType = UITableViewCellAccessoryType.checkmark
} else {
   cell.accessoryType = UITableViewCellAccessoryType.none
}

Upvotes: 3

Related Questions