简体   繁体   中英

Alphabetical sections in table table view in swift

I have a list of names sorted alphabetically, and now I want display these names in a table view. I'm struggling with grouping these names for each letter.

My code looks like this:

let sections:Array<AnyObject> = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]
var usernames = [String]()

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
    
    let cellID = "cell"
    
    let cell: UITableViewCell = self.tv.dequeueReusableCellWithIdentifier(cellID) as UITableViewCell
    
    cell.textLabel?.text = usernames[indexPath.row]
    
return cell
}

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


func sectionIndexTitlesForTableView(tableView: UITableView) -> [AnyObject]!{
    
    return self.sections
}

func tableView(tableView: UITableView,
    sectionForSectionIndexTitle title: String,
    atIndex index: Int) -> Int{
        
        return index
}

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

and it all works pretty good except for the grouping which makes my table view end up like this:

在此处输入图片说明

So I know you should be able to use the filtered function in an Array, but I did not understand how to implement it.

Any suggestions on how to proceed would be appreciated.

In Swift 4 Dictionary(grouping:by:) was introduced to group a sequence to a dictionary by an arbitrary predicate.

This example maps the grouped dictionary to a custom struct Section

struct Section {
    let letter : String
    let names : [String]
}

...

let usernames = ["John", "Nancy", "James", "Jenna", "Sue", "Eric", "Sam"]

var sections = [Section]()

override func viewDidLoad() {
    super.viewDidLoad()

    // group the array to ["N": ["Nancy"], "S": ["Sue", "Sam"], "J": ["John", "James", "Jenna"], "E": ["Eric"]]
    let groupedDictionary = Dictionary(grouping: usernames, by: {String($0.prefix(1))})
    // get the keys and sort them
    let keys = groupedDictionary.keys.sorted()
    // map the sorted keys to a struct
    sections = keys.map{ Section(letter: $0, names: groupedDictionary[$0]!.sorted()) }
    self.tableView.reloadData()
}


func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cellID = "cell"
    let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath)
    let section = sections[indexPath.section]
    let username = section.names[indexPath.row]
    cell.textLabel?.text = username
    return cell
}

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

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

func sectionIndexTitles(for tableView: UITableView) -> [String]? {
    return sections.map{$0.letter}
}

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

This is how I recently implemented sorted list in a tableView in Swift programmatically,

import UIKit

class BreedController: UITableViewController{

    var breeds = ["A": ["Affenpoo", "Affenpug", "Affenshire", "Affenwich", "Afghan Collie", "Afghan Hound"], "B": ["Bagle Hound", "Boxer"]]

    struct Objects {
        var sectionName : String!
        var sectionObjects : [String]!
    }

    var objectArray = [Objects]()

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self
        tableView.dataSource = self
        tableView.registerClass(UITableViewCell.classForCoder(), forCellReuseIdentifier: "Cell")
        // SORTING [SINCE A DICTIONARY IS AN UNSORTED LIST]
        var sortedBreeds = sorted(breeds) { $0.0 < $1.0 }
        for (key, value) in sortedBreeds {
            println("\(key) -> \(value)")
            objectArray.append(Objects(sectionName: key, sectionObjects: value))
        }
    }

    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
        // SETTING UP YOUR 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
    }


}

Download CountryList Json file and put in side your project

https://gist.github.com/keeguon/2310008

   var json = NSArray()
   var arr_name = NSArray()
   var arrIndexSection : NSArray = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]


override func viewDidLoad() {

   let path = Bundle.main.path(forResource: "countries", ofType: "json")
    let data = NSData(contentsOfFile: path! )
    json = (try! JSONSerialization.jsonObject(with: data as! Data, options: JSONSerialization.ReadingOptions.mutableContainers)) as! NSArray
    arr_name = json.value(forKey: "name") as! NSArray;

    tableview.reloadData()
    super.viewDidLoad()
}
// Side List in tableview
public func numberOfSections(in tableView: UITableView) -> Int {
    return 26
}
public func sectionIndexTitles(for tableView: UITableView) -> [String]? {
    return self.arrIndexSection as? [String] //Side Section title
}
public func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int
{
    return index
}


public func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    return arrIndexSection.object(at: section) as? String
}


// number of rows in table view
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
    let predicate = NSPredicate(format: "SELF beginswith[c] %@", arrIndexSection.object(at: section) as! CVarArg)
    let arrContacts = (arr_name as NSArray).filtered(using: predicate)
    return arrContacts.count;

}

public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
    let cell : TableViewCell=self.tableview.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
    let predicate = NSPredicate(format: "SELF beginswith[c] %@", arrIndexSection.object(at: indexPath.section) as! CVarArg)
    let arrContacts = (arr_name as NSArray).filtered(using: predicate) as NSArray
    cell.textLabel?.text = arrContacts.object(at: indexPath.row) as? String
    return cell
}

You can put your arrays with names into dictionary with letter keys.

For example

var names = ["a": ["and", "array"], "b": ["bit", "boring"]]; // dictionary with arrays setted for letter keys

then you need to access values in your dictionary in the next way

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
    return names[usernames[section]].count; // maybe here is needed to convert result of names[...] to NSArray before you can access count property
}

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

    let cellID = "cell"

    let cell: UITableViewCell = self.tv.dequeueReusableCellWithIdentifier(cellID) as UITableViewCell

    cell.textLabel?.text = names[usernames[indexPath.section]][indexPath.row]; // here you access elements in arrray which is stored in names dictionary for usernames[indexPath.section] key

return cell
}
var frndsName = ["Vanitha","Ilakkiya","Parveen","Divya","Seetha","Madhavi","Ashwini","Sangeetha","Swathi","Don","Priyanka","Tamilselvi","Premlatha","Prashanthi","Rekha","Ajitha","Praveena","Indhusree","Nisha","Priya","Lavanya","Sandhiya","Gejalakshmi","Pavithra","Abinaya"]
    let sections = ["*","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","#"]
    var dividedArray:NSMutableArray = []

    override func viewDidLoad() {
        super.viewDidLoad()
        for i in sections{
            let dummyArray:NSMutableArray = []
            for j in frndsName{
                if  i.first!  == j.first! {
                  dummyArray.add(j)
                }
            }
           dividedArray.add(dummyArray)
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    func numberOfSections(in tableView: UITableView) -> Int {
     return 26
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return (dividedArray[section] as! NSMutableArray).count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
        let dummyArray = dividedArray[indexPath.section] as! NSMutableArray
        cell?.textLabel?.text = dummyArray[indexPath.row] as? String

        return cell!
          }
    func sectionIndexTitles(for tableView: UITableView) -> [String]? {
        return self.sections
    }
    func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
        return index
    }
    internal func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return self.sections[section] as String
    }
}

In case, if your data array is not predefined, here's a way to achieve the same thing.

Let's say our class is ViewController .

class ViewController: UIViewController {

     var contactDictionary = [String: [Contact]]() //Contact is a model, it has firstName and lastName properties
     var keys = [String]()
     var alphabets = (97...122).map { "\(Character(UnicodeScalar.init($0)))" }.map { $0.uppercased() } //Populating alphabets
     ... // other properties


    override func viewDidLoad() {
        super.viewDidLoad()
        //set delegate and register cell for your tableView
        self.setContacts()
    }

    private func setContacts() {
         //Loop through your array, take the firstName, and the first character of that string.
         //Check the uppercase value of that character, if it's an alphabet or not, otherwise, we'd place "#" for the names starting with a number in the header.

         var temp = [String: [Contact]]() //A temporary dictionary
         for contact in self.contacts {
             if let firstName = contact.firstName, !firstName.isEmpty { //In my case, the firstName is an optional string
                let firstChar = "\(firstName.first!)".uppercased()
                if alphabets.contains(firstChar) {
                    var array = temp[firstChar] ?? []
                    array.append(contact)
                    temp[firstChar] = array
                } else {
                    var array = temp["#"] ?? []
                    array.append(contact)
                    temp["#"] = array
                }
            }
        }
        self.keys = Array(temp.keys).sorted() //Populating and sorting all the keys alphabetically.
        for key in self.keys { self.contactDictionary[key] = temp[key] }
        //reload table
    }
}

extension: ViewController: UITableViewDelegate, UITableViewDataSource {

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

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.contactDictionary[keys[section]]?.count ?? 0
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let key = self.keys[indexPath.section]
        let cell: //dequeue your cell here.
        if let row = self.contactDictionary[key]?[indexPath.row] {
            cell.display(with: row) //Bind your cell's outlets with the properties
            return cell
        }

        //handle error
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let key = self.keys[indexPath.section]
        if let row = self.contactDictionary[key]?[indexPath.row] {
            //handle selection.
        }
    }

    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { //You can use viewForHeaderInSection either.
        return self.keys[section]
    }

}
var nameList = ["Abc","Bbc","Cbc",......]//whatever it is
var arrNameList = [[nameList]]()
var str = String()

override func viewDidLoad() {

  str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

  for i : Int in 0 ..< (str.count) {
      var arr = [nameList]()
        for j : Int in 0 ..< nameList.count {
          let name = nameList[j]
          let index = str.index(str.startIndex, offsetBy: i)
          let char = str[index]
          if name.first == char {
              arr.append(name)
          }
      }
      arrNameList.append(arr)
   }
}

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

func tableView(_ tableView: UITableView, titleForHeaderInSection 
section: Int) -> String? {
  let index = str.index(str.startIndex, offsetBy: section)
  let char = str[index]
  return char
}

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

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

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

func tableView(_ tableView: UITableView, cellForRowAt indexPath: 
IndexPath) -> UITableViewCell {

  let cell = tableView.dequeueReusableCell(withIdentifier: "cell", 
  for: indexPath) as! NameListTableViewCell

  let name = arrNameList[indexPath.section][indexPath.row]
  cell.nameLbl.text = name

  cell.selectionStyle = .none
  return cell
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM