25

Lets consider this example:

import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { 
    @IBOutlet weak var tableView: UITableView!

    var names = ["Vegetables": ["Tomato", "Potato", "Lettuce"], "Fruits": ["Apple", "Banana"]]

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

        let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier:"test")

    return cell
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
    return ???
    }
    func numberOfSectionsInTableView(tableView: UITableView) -> Int{      
    return names.count
    }

    func sectionIndexTitlesForTableView(tableView: UITableView) -> [AnyObject]!{

    return ???
    }

    func tableView(tableView: UITableView,
        titleForHeaderInSection section: Int) -> String?{        
    return ????
    }
}

let's assume that we need that the keys (fruits and vegetables) of the dictionary are the number of sections, plus they will be the titles of the sections. The items of the keys (eg apples and banana) will be the rows of each section. How can I implement this in my code? I know it might be easy but I couldn't figure it out my self.

Rami Ammoun
  • 855
  • 2
  • 10
  • 25

7 Answers7

61

You can use struct for that and here is example:

import UIKit

class TableViewController: UITableViewController {

    var names = ["Vegetables": ["Tomato", "Potato", "Lettuce"], "Fruits": ["Apple", "Banana"]]

    struct Objects {

        var sectionName : String!
        var sectionObjects : [String]!
    }

    var objectArray = [Objects]()

    override func viewDidLoad() {
        super.viewDidLoad()

        for (key, value) in names {
            println("\(key) -> \(value)")
            objectArray.append(Objects(sectionName: key, sectionObjects: value))
        }
    }

    // MARK: - Table view data source

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

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


    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell

        // Configure the cell...
        cell.textLabel?.text = objectArray[indexPath.section].sectionObjects[indexPath.row]
        return cell
    }

    override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {

        return objectArray[section].sectionName
    }
}
Dharmesh Kheni
  • 71,228
  • 33
  • 160
  • 165
28

Swift 2

you dictionary example

var dic:Dictionary<String,String> = ["key":"value","key1":"value2"]

Your table

 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell

    var key   = Array(self.dic.keys)[indexPath.row]
    var value = Array(self.dic.values)[indexPath.row]
    cell.text = key + value 
}
Diego
  • 2,395
  • 2
  • 21
  • 27
4

If you want it sorted use the global sorted function to sort the dictionary.

import UIKit

class TableViewController: UITableViewController {

    var names = ["Vegetables": ["Tomato", "Potato", "Lettuce"], "Fruits": ["Apple", "Banana"]]

    var namesSorted = [String, Array<String>]()

    override func viewDidLoad() {
        super.viewDidLoad()

        // Sort names
        namesSorted = sorted(names) { $0.0 < $1.0} // namesSorted = ["Fruits": ["Apple", "Banana"], "Vegetables": ["Tomato", "Potato", "Lettuce"]]

    }

    // MARK: - Table view data source

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

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


    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell

        // Configure the cell...
        cell.textLabel?.text = namesSorted[indexPath.section].1[indexPath.row]
        return cell
    }

    override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {

        return namesSorted[section].0
    }
}
Santhosh
  • 691
  • 4
  • 12
2

All collection types must be Array

var names = [["Tomato", "Potato", "Lettuce"], ["Apple", "Banana"]]
var sectionNames = ["Vegetables", "Fruits"]

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

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

func sectionIndexTitlesForTableView(tableView: UITableView) -> [AnyObject]!{

  return sectionNames
}

func tableView(tableView: UITableView,
  titleForHeaderInSection section: Int) -> String?{
    return sectionNames[section]
}
vadian
  • 274,689
  • 30
  • 353
  • 361
  • @Lamar I don't understand. Group how? And what do you mean with *same name*? There are two sections with different names. – vadian Jul 13 '16 at 19:53
  • what if I had multiple sections of the same name but they all had different data; for example `var names = [["Tomato", "Potato", "Lettuce"], ["Apple", "Banana"],["orange","pinapple"]] var sectionNames = ["Vegetables", "Fruits","Fruits"]` how would you group all the fruits into one section without the UI displaying two different sections – Lamour Jul 13 '16 at 19:59
  • @Lamar Eventually the table view data source expects one array per section, so you have two options: Consolidate the affected arrays or write code to pretend to be one array. The most efficient way is the first one. – vadian Jul 13 '16 at 20:11
2

From Apple Documentation :

var keys: LazyForwardCollection<MapCollectionView<Dictionary<Key, Value>, Key>> { get }

Description: A collection containing just the keys of self. Keys appear in the same order as they occur as the .0 member of key-value pairs in self. Each key in the result has a unique value.

names.keys.array returns an Array of the keys.

SO:

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return names.keys.array[section].count
}

func sectionIndexTitlesForTableView(tableView: UITableView) -> [AnyObject]!{
return names.keys.array
}

func tableView(tableView: UITableView,
    titleForHeaderInSection section: Int) -> String?{        
return names.keys.array[section]
}

This will work on Any Dictionary with any amount of data(even if it is unknown to the programmer

Raja Vikram
  • 1,970
  • 15
  • 17
  • 1
    Dictionaries are unordered. It is a bad idea to depend on `names.keys.array` always returning in the same order. It would be better to do this once (and perhaps sort it) and keep the array in a property. – vacawama Jun 30 '15 at 11:05
  • Yeah but In an UITableView where you are just displaying Data. Sorting doesn't matter. – Raja Vikram Jun 30 '15 at 11:08
  • Swift doesn't guarantee that `names.keys.array` will return the array in the same order on two consecutive calls. You could get `[Fruits, Vegetables]` the first time you call it and `[Vegetables, Fruits]` the second time you call it and this would be perfectly legal. You could end up showing a table with `Fruits` as the title of your first section and `Fruits` as the title of your second section if they aren't based upon the same single call to `names.keys.array`. – vacawama Jun 30 '15 at 11:12
  • From Apple Documentation : `var keys: LazyForwardCollection, Key>> { get }` `Description`: A collection containing just the keys of self. Keys appear in the same order as they occur as the .0 member of key-value pairs in self. Each key in the result has a unique value. – Raja Vikram Jun 30 '15 at 11:17
  • Interesting. Then it's probably OK for a static dictionary, but for one in which keys are being inserted and deleted you'd be better off managing the order of your keys in a separate array. – vacawama Jun 30 '15 at 11:30
0

An easier way to solve this problem is to copy your dictionary into a temporary variable. Use removeFirst to extract the values from the array inside the dictionary.

var itemList=["Grocery":["soap","flour","carrots"],"Vehicles":["oil change","gas","tire rotation"],"Household":["Cable","Tv","cellphone"]]
var itemListTmp :[String:[String]] = [:]

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text=itemListTmp[keysItem[indexPath.section]]?.removeFirst()
       //cell.textLabel?.text=itemList[indexPath.section].items[indexPath.row]
        return cell
    }

Another way of solving this problem is to extract keys and values in separate arrays:

var task=[String](itemList.keys)
var tobeDone=[[String]](itemList.values)
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    return task[section]
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)

    cell.textLabel?.text=tobeDone[indexPath.section][indexPath.row]

    return cell
}
O.O.Balance
  • 2,930
  • 5
  • 23
  • 35
pam
  • 1
  • 1
-1

Similar to https://stackoverflow.com/a/31136537/11098567 answer I would use classes instead of structs, so that you can manipulate or add to your values after it has been placed into the array.

@objc func addToInitialClassInstance() {

   let classInstance = Class(property1: String, property2: [CLass2.init(property1: String, property2: String)])
    let isAvailable = initialClassInstance.contains { (classInArray) -> Bool in
        if classInArray.property == classInstance.property {
            classInArray.property2.append(classInstance.property2[0])
            return true
        }
        return false
    }
    if !isAvailable {
        initialClassInstance.append(classInstance)
    }
    tableView.reloadData()
}