如何在Swift中从字典中对TableView的项目进行分组?

25

让我们考虑这个例子:

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 ????
    }
}

假设我们需要字典的键(水果和蔬菜)是节的数量,它们将成为各个节的标题。 键的项目(例如苹果和香蕉)将是每个节的行。 我该如何在我的代码中实现这一点? 我知道这可能很容易,但我自己想不出来。


这与Xcode无关。 - The Paramagnetic Croissant
7个回答

61
你可以使用结构体来实现这个,下面是一个例子:

你可以使用结构体来实现这个,下面是一个例子:

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
1
我做到了!你的答案提供了比我预期的更多! :) - Rami Ammoun
祝你的项目好运.. :) - Dharmesh Kheni
还有一个问题,如果可能的话,你能告诉我如何按字母顺序对章节进行排序吗? - Rami Ammoun
是的,这很棒。 - Zonker.in.Geneva
显示剩余2条评论

28

Swift 2

你的字典示例

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

您的表格

 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 
}

4
如果你想要排序,可以使用全局的sorted函数来对字典进行排序。
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
    }
}

2

所有的集合类型都必须是数组

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]
}

@Lamar 我不明白。你是指如何分组?而且你所说的“相同名称”是什么意思?这里有两个名字不同的部分。 - vadian
如果我有多个相同名称的部分,但它们都具有不同的数据,该怎么办?例如 var names = [["Tomato", "Potato", "Lettuce"], ["Apple", "Banana"],["orange","pinapple"]] var sectionNames = ["Vegetables", "Fruits","Fruits"] 如何将所有水果分组到一个部分中,而不显示两个不同的部分在用户界面上? - Lamour
@Lamar 最终,表视图数据源希望每个部分有一个数组,因此您有两个选择:合并受影响的数组或编写代码来模拟一个数组。最有效的方法是第一个选项。 - vadian

2

来自苹果文档:

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

描述:该集合仅包含self的键。 键按照它们在self中作为key-value对的.0成员出现的相同顺序显示。 结果中的每个键都具有唯一值。

names.keys.array 返回一个Array,其中包含键。

因此:

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]
}

这将适用于任何词典,无论数据量大小如何(即使程序员不知道它的大小)。

1
字典是无序的。依赖于 names.keys.array 总是以相同顺序返回是一个不好的想法。最好只做一次(并可能进行排序),并将数组保存在属性中。 - vacawama
是的,但在只显示数据的UITableView中,排序并不重要。 - Raja Vikram
Swift不能保证names.keys.array在两次连续调用中返回的数组顺序相同。第一次调用时,您可能会得到[Fruits, Vegetables],第二次调用时,您可能会得到[Vegetables, Fruits],这是完全合法的。如果它们不基于同一个names.keys.array的单个调用,您可能最终会显示一个标题为Fruits的第一部分和一个标题为Fruits的第二部分的表格。 - vacawama
从苹果文档:var keys: LazyForwardCollection<MapCollectionView<Dictionary<Key, Value>, Key>> { get }描述: 包含self的键的集合。 键以与self中的键值对的.0成员相同的顺序出现。结果中的每个键都具有唯一的值。 - Raja Vikram
有趣。那么对于静态字典来说,这可能是可以的,但是对于插入和删除键的字典来说,最好在单独的数组中管理键的顺序。 - vacawama

0
解决这个问题的更简单方法是将您的字典复制到一个临时变量中。使用removeFirst从字典内部的数组中提取值。
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
    }

另一种解决这个问题的方法是将键和值分别提取到不同的数组中:
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
}

-1
https://dev59.com/g10Z5IYBdhLWcg3wvCSd#31136537的答案类似,我会使用类而不是结构体,这样您可以在将其放入数组后操纵或添加其值。

@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()
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接