Populating Tableview with Multiple Sections and Multiple Dictionary in an Array in Swift

Populating TableView with multiple sections and multiple dictionary in an array in Swift

If itemA array for Category A, itemB array for Category B and so on then you can return array count in numberOfRowsInSection this way.

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch (section) {
case 0:
return itemsA.count
case 1:
return itemsB.count
default:
return itemsC.count
}
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "StoreCell") as! UITableViewCell
switch (indexPath.section) {
case 0:
//Access itemsA[indexPath.row]
case 1:
//Access itemsB[indexPath.row]
default:
//Access itemsC[indexPath.row]
}
return cell
}

Note: It is batter if you create single Array of struct or custom class that will reduce all your array with single array.

Swift - Populating TableViews with multiple sections from multidimensional array

First off: A dictionary is a poor choice for storing table view contents. Dictionaries are inherently unordered, so you have to keep sorting the keys. If your data gets beyond a small number of items that sort process will take appreciable time.

If you are going to use a dictionary anyway you should refactor your code so that you keep the sorted keys and use them over and over unless the dictionary changes. I would make it so that you add a setter method to the dictionary and always use that setter to change the dictionary. The setter method would regenerate the sorted keys. That way you only have to sort the keys if the dictionary changes. (But better to get rid of the dictionary entirely.)

I would suggest creating a Section object that contains a sectionTitle String and a sectionEntries Array. Then make the table view data an array of Section objects.

However, since you have a dictionary, I'll show you how to make that code work.

You need help with your cellForRowAtIndexPath method. You are almost there. You just need to fetch the appropriate entry in your data structure using the indexPath section and row. Something like this:

override func tableView(tableView: UITableView, 
cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{

let cell = tableView.dequeueReusableCellWithIdentifier("BufferCell",
forIndexPath: indexPath)
let section = indexPath.section
let row = indexPath.row

let categoryKey:String = categoryList[section]
let aCategoryEntry:[String] = categoryDict[categoryKey] as! [String]
let anObject = aCategoryEntry[row] //
let cell.textLabel.text = anObject
return cell
}

Populate different sections of tableview from single Dictionary in Swift

Group the elements that should be in the same section in array like this

var menuItems = [
[
["city":"Lahore","cityimage":"2","country":"Pakistan"],
["city":"istanbul","cityimage":"3","country":"Turkey"]
],
[
["city":"Moscow","cityimage":"4","country":"Russia"],
["city":"Riyadh","cityimage":"5","country":"KSA"]
]
]

you can access them with

cell.lblCityName.text!=menuItems[indexPath.section][indexPath.row]["city"]!

UITableView with Multiple Sections in Alphabetical Order using Realm and Swift, with multiple labels connected with same data

There are a number of issues with the code in the question and instead of trying to dissect them, let me provide an example that may help.

Suppose we have a bunch of fruits stored in Realm. We want to display those in alphabetical order in a tableview using sections

A
Apple
B
Banana

etc

This first piece of code is a structure to hold a section title and then an array of the fruits that go in that section. Also, a class fruitNameArray which will act as our tableView DataSource that holds all of the FruitStructs.

struct FruitStruct {
var sectionTitle = ""
var fruitNameArray = [String]()
}

var fruitDataSource = [FruitStruct]()

I am using an array to hold the initial data but in your case it could be a Realm results object populated from a Realm. This is called from viewDidLoad to organize our data into the dataSource array

func setupDataSourceData() {
let fruitArray = ["Apple", "Pear", "Banana", "Bing Cherry", "Grape", "Orange", "Plum", "Watermelon", "Cantelope"]

let allFirstChars = fruitArray.map { String($0.prefix(1)) } //get all first chars for section titles
let sectionTitles = Array(Set(allFirstChars)).sorted() //eliminate dups and sort

//iterate over the unique section titles and get the fruits that are in that section
// sort and then craft structs to hold the title and the associated fruits
sectionTitles.forEach { firstChar in
let results = fruitArray.filter { $0.prefix(1) == firstChar }
let sortedFruits = results.sorted()
let fruit = FruitStruct(sectionTitle: firstChar, fruitNameArray: sortedFruits)
fruitDataSource.append(fruit)
}

for fruitData in fruitDataSource {
print(fruitData.sectionTitle)
let fruits = fruitData.fruitNameArray
for fruitName in fruits {
print(" \(fruitName)")
}
}
}

Then, there are 4 functions a tableView needs to show the sections and then the rows in each section. You appear to have these 4 but an additional one sectionIndexTitles which may not be needed.

//
//handle sections
//
func numberOfSections(in tableView: UITableView) -> Int {
return self.fruitDataSource.count
}

func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let title = self.fruitDataSource[section].sectionTitle
return title
}

//
//handleTableView rows
//
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let rowsInSection = self.fruitDataSource[section].fruitNameArray.count
return rowsInSection
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellReuseIdentifier", for: indexPath)
let text = self.fruitDataSource[indexPath.section].fruitNameArray[indexPath.row]
cell.textLabel?.text = text
return cell
}

Note that I am not performing any filtering and any other stuff as the tableView is being populated. That section of code needs to be fast and lean to the UI is as responsive as possible.

I am just getting a string

let text = self.fruitDataSource[indexPath.section].fruitNameArray[indexPath.row]

but in your case, you may want to return an exersise object where you could then get the exerciseName and bodypartName to then assign those in the custom cellView

Best Practice Note: class properties should be lower cased - exerciseName, not uppercase, ExerciseName. Class and Struct names are typically capitalized.

How do I populate a tableView with all the data in a dictionary?

A tableView displays stuff in order. A dictionary isn't ordered. By looping over your dictionary with "for (key, value) in clubDict" you will only get the last entry in the dictionary to be displayed.

What you have to do is get the unordered dictionary into an ordered form like an array. For example like so:

let dict = ["key1" : "value1"]; // Your dictionary. Use the one you already have
var arr : [(String, String)] = [];
for (key, value) in dict {
arr.append((key, value));
}

Then you will have to adjust the "numberOfRowsInSection" method to now use arr.count

For each cell the cellForRowAtIndexPath method will be called.
There you extract the correct entry from the array by its index:

let (key, value) = arr[indexPath.row]; //read element for the desired cell
cell.textLabel?.text = key
cell.detailTextLabel?.text = value.address

You have to adjust the datatypes to your needs. I just took a dictionary of String:String

How do I populate two sections in a tableview with two different arrays using swift?

TableView Cells

You could use a multidimensional array. For example:

let data = [["0,0", "0,1", "0,2"], ["1,0", "1,1", "1,2"]]

For the number of sections use:

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return data.count
}

Then, to specify the number of rows in each section use:

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

Finally, you need to setup your cells:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellText = data[indexPath.section][indexPath.row]

// Now do whatever you were going to do with the title.
}

TableView Headers

You could again use an array, but with just one dimension this time:

let headerTitles = ["Some Data 1", "KickAss"]

Now to set the titles for the sections:

override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section < headerTitles.count {
return headerTitles[section]
}

return nil
}

The code above checks to see there's a title for that section and returns it, otherwise nil is returned. There won't be a title if the number of titles in headerTitles is smaller than the number of arrays in data.

The Result

Sample Image

UITableView With Multiple Sections

 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 2 ;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (section==0)
{
return [array1 count];
}
else{
return [array2 count];
}
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if(section == 0)
return @"Section 1";
else
return @"Section 2";
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{

static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}

if (indexPath.section==0) {
ObjectData *theCellData = [array1 objectAtIndex:indexPath.row];
NSString *cellValue =theCellData.category;
cell.textLabel.text = cellValue;
}
else {
ObjectData *theCellData = [array2 objectAtIndex:indexPath.row];
NSString *cellValue =theCellData.category;
cell.textLabel.text = cellValue;
}
return cell;
}

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

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
}

Sample Image

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
}

Swift: How to configure a tableview with multiple sections dynamically using JSON data

You can build your data structure like that:

var teams = [[String: [Datum]]]()
for team in teams {
let teamProjects = projects.filter {$0.relationships.team.id == team.id}
let teamData = [team: [teamProjects]]
teams.append(teamData)
}

and in numberOfRows method:

 let team = teamsWithProjects[section-1]
let teamProject = Array(team.values)[0]
return teamProject.count

and in cellForRow method:

  let team = teamsWithProjects[indexPath.section-1]
let teamProject = Array(team.values)[0]
projectCell.textLabel?.text = teamProject[indexPath.row].attributes.name

Hope this will help you!

How to populate sections of an indexed UITableView with different array for sectionIndexTitles

Group the people array by the state name and create array of tuple from the dictionary. Then use that array in tableview data source methods

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
struct Person {
var name: String
var state: String
}
var allPeople = [Person]()
var groupedPeople = [(state:String, people:[Person])]()
@IBOutlet weak var tableView: UITableView!

override func viewDidLoad() {
super.viewDidLoad()

allPeople = [Person(name: "a", state: "Alaska"), Person(name: "x", state: "Florida"),
Person(name: "c", state: "California")]
groupedPeople = Dictionary(grouping: allPeople, by: { $0.state }).sorted(by: { $0.key < $1.key })
.map({ (state:$0.key, people:$0.value)
})
}
func sectionIndexTitles(for tableView: UITableView) -> [String]? {
return Array(Set(groupedPeople.map({ String($0.state.first!) })))
}
func numberOfSections(in tableView: UITableView) -> Int {
return groupedPeople.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return groupedPeople[section].state
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return groupedPeople[section].people.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") ?? UITableViewCell(style: .subtitle, reuseIdentifier: "cell")
cell.textLabel?.text = groupedPeople[indexPath.section].people[indexPath.row].name
cell.detailTextLabel?.text = groupedPeople[indexPath.section].people[indexPath.row].state
return cell
}
}

Update

When you have more sections than sectionIndexTitles you should implement sectionForSectionIndexTitle method and return appropriate section index.

func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
if let index = groupedPeople.firstIndex(where: { $0.state.hasPrefix(title) }) {
return index
}
return 0
}


Related Topics



Leave a reply



Submit