Is there a way to struct a Firestore fields(same name) with arrays and show each array element from each field in TableView - ios

I'm developing a IOS app and I want to show the array Strings from all document fields in table view.
this is my struct for the array.
struct Test{
var car: Array<String>
var dictionary: [String: Any] {
return [
"car":car
]
}
}
extension Test{
init?(dictionary: [String : Any]) {
guard let car = dictionary["car"] as? Array<String>
else { return nil }
self.init(car:car)
}
}
This is my code for fetching the data.
func loadData(){
let db = Firestore.firestore()
db.collection("test").getDocuments(){
querySnapshot, error in
if let error = error {
print("\(error.localizedDescription)")
}else{
self.testArray = querySnapshot!.documents.compactMap({Test(dictionary: $0.data())})
print(self.testArray)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
And this my tableView code.
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return testArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let item = testArray[indexPath.row].car[indexPath.row]
cell.textLabel!.text = ("\(item)")
return cell
Everything seems fine but when run the app, the tableview shows the [0] from the 1st document in the first line, the [1] from the 2nd document in the second line etc. I want to show the whole array from first document then the whole array from 2nd document etc.

You need multiple sections
override func numberOfSections(in tableView: UITableView) -> Int {
return testArray.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return testArray[section].car.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel!.text = testArray[indexPath.section].car[indexPath.row]
return cell
}

Related

How do I parse JSON data into a table view?

all Im a noob at swift lets just set the record,
Im trying to parse JSON data from an external php file and display the data in a tableview, the data is from a php file hosted elsewhere, its values are
{
'id' : '1',
'username' : 'test',
'password' : 'test',
'email' : 'test#test.com'
}
My code is as follows
class ForuthTableViewController: UITableViewController {
#IBOutlet var userTable: UITableView!
#IBOutlet weak var refreshButton: UIBarButtonItem!
var values:NSArray = []
override func viewDidLoad() {
super.viewDidLoad()
getUsers()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func refreshPressed(_ sender: UIBarButtonItem) {
getUsers()
}
func getUsers(){
let url = NSURL(string: "http://xxx.xxxxxx.xx/get.php")
let userdata = NSData(contentsOf: url! as URL)
do {
values = try JSONSerialization.jsonObject(with: userdata as! Data, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSArray
print("Parse success")
} catch {
print("Parse error")
return
}
print(values)
//userTable.reloadData()
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return values.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 0
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let cell = tableView.dequeueReusableCellWithIdentifier("userCell", forIndexPath: indexPath) as! UITableViewCell
let maindata = values[indexPath.row]
cell.username.text = maindata["username"] as? String
cell.password.text = maindata["password"] as? String
cell.email.text = maindata["email"] as? String
return cell
}
}
I followed a tutorial on youtube of creating a login / register app but it was from an old version of xcode and when I copy the code across theres errors everywhere so not sure where to start,
Tried looking for a simple JSON tutorial but there are so many different ways to approach JSON I'm not sure which is the right one.
Any ideas would be appreciated, or perhaps a simple fix.
got an error "Errors thrown from here not handled" : Catch the error.
do {
values = try JSONSerialization.jsonObject(with: userdata as! Data, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSArray
print("Parse success")
} catch {
print("Parse error")
print("Error: \(error)")
}
error "IndexPath not resolved" :
You are using method numberOfRowsInSection instead of cellForRowAtIndexPath.
Also override both methods.
Edit 1:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
should be:
override func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return values.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "userCell", for: indexPath) as! UITableViewCell
let maindata = values[indexPath.row]
cell.username.text = maindata["username"] as? String
cell.password.text = maindata["password"] as? String
cell.email.text = maindata["email"] as? String
return cell
}

Grouping Data in TableView from Core Data in Swift 4

I am having trouble finding out how to group my sections by a property in my Core Data database. This is what my DB looks like here. I am trying to group my tableView by the dueDate property. I have loaded up my Attributes in an array and that is how they are displayed. I plan on customizing the headers as well, so I would like to use the standard tableView methods. Here is the code from my ViewController.
import UIKit
import CoreData
class MainTableViewController: UITableViewController {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var taskArray = [Task]()
override func viewDidAppear(_ animated: Bool) {
loadData()
}
// MARK: - Table view functions
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return taskArray.count
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "Date"
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 65.00
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "taskCell", for: indexPath) as! TaskCell
cell.nameLabel.text = taskArray[indexPath.row].name ?? "Add Items"
if taskArray[indexPath.row].dueTime == nil {
cell.timeLabel.text = ""
} else {
let timeFormatter = DateFormatter()
timeFormatter.timeStyle = .short
cell.timeLabel.text = timeFormatter.string(from: taskArray[indexPath.row].dueTime!)
}
return cell
}
// MARK: Add New Task
#IBAction func addButtonPressed(_ sender: Any) {
performSegue(withIdentifier: "newTaskSegue", sender: self)
}
// MARK: Save & Load Data
func saveData() {
do {
try context.save()
} catch {
print("Error saving context \(error)")
}
tableView.reloadData()
}
func loadData() {
let request : NSFetchRequest<Task> = Task.fetchRequest()
let sort = NSSortDescriptor(key: "dueDate", ascending: false)
let sort2 = NSSortDescriptor(key: "dueTime", ascending: false)
request.sortDescriptors = [sort, sort2]
do {
taskArray = try context.fetch(request)
} catch {
print("Error loading data \(error)")
}
tableView.reloadData()
}
}
Any help would be much appreciated. Thanks!
You can easily group your data using NSFetchedResultsController. One parameter in the instantiation of NSFetchedResultsController specifically allows you to group your results into sections by passing the keyPath of an attribute that constitutes the predicate for section grouping.
Apple's documentation explains this pretty clearly, with example code:
override func numberOfSections(in tableView: UITableView) -> Int {
if let frc = <#Fetched results controller#> {
return frc.sections!.count
}
return 0
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
guard let sections = self.<#Fetched results controller#>?.sections else {
fatalError("No sections in fetchedResultsController")
}
let sectionInfo = sections[section]
return sectionInfo.numberOfObjects
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = <#Get the cell#>
guard let object = self.<#Fetched results controller#>?.object(at: indexPath) else {
fatalError("Attempt to configure cell without a managed object")
}
// Configure the cell with data from the managed object.
return cell
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
guard let sectionInfo = <#Fetched results controller#>?.sections?[section] else {
return nil
}
return sectionInfo.name
}
override func sectionIndexTitles(for tableView: UITableView) -> [String]? {
return <#Fetched results controller#>?.sectionIndexTitles
}
override func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
guard let result = <#Fetched results controller#>?.section(forSectionIndexTitle: title, at: index) else {
fatalError("Unable to locate section for \(title) at index: \(index)")
}
return result
}
It is generally a Good Idea(tm) to use an NSFetchedResultsController when dealing with CoreData and UITableView or UICollectionView as you get handy notifications (through a NSFetchedResultsControllerDelegate) when your data changes that allow you to insert or remove cells from your displayed view.

ios table data not displaying

I am new to iOS programming
I am facing problem to view table data on my iOS device but it can be seen on console
Below is my code:
#IBOutlet weak var tableView: UITableView!
var model = [String]()
override func viewDidLoad() {
super.viewDidLoad()
webService.delegate = self
tableView.delegate = self
tableView.dataSource = self
let str = ""
webService.fireRequest(functionName: "ClassName.php", requestString: str, view: view)
}
func numberOfSections(in tableView: UITableView) -> Int {
return model.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "classWiseMgmtTableViewCell", for: indexPath)
cell.textLabel?.text = model[indexPath.row]
cell.layer.cornerRadius = 8
cell.layer.masksToBounds = true
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let vc = storyboard?.instantiateViewController(withIdentifier: "divisionWiseTableViewCell") as! DivisionWiseViewController
vc.term = self.model[indexPath.section]
navigationController?.pushViewController(vc, animated: true)
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 10.0
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// func webserviceDidFinishWith(response: [String : Any]) {
// }
func webserviceDidFinishWith(response: [String : Any], functionName: String) {
print("Response\(response)")
let array = response["Cls_Name"] as? [[String:String]]
if array != nil
{
self.model.removeAll()
for obj in array!
{
self.model.append(obj["Cls_Name"]!)
}
self.tableView.reloadData()
}
}
}
You can use like:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return model.count;
}
I guess the issue is with the rows in the tableView
and also i am not sure if you are having this much number of sections.? If there is only one section pass like this
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
also check this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier : String = "classWiseMgmtTableViewCell";
var cell : SportCell! =
tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as! SportCell //Pass your tableViewCell FileName here
}
Hope this Helps.
Edit to set Data as per your response
you can extract data like:
var arrResponse = repsonse as! [String:Any] //Instead of response pass your WebService Response here
let arrClassName = arrResponse["clsname"] as! NSArray
print(arrClassName)
Edit Demo Link Integrated with WS Call
Link to google drive is: Demo Project
Some Notes: I have used Alamofire for Get Method, and also in case the project does not run or fail to detect the Alamofire, just install the pod again using pod install and it will work.
Hope it helps!
Update inside cellForRowAt function with below code
cell.textLabel?.text = model[indexPath.section]
One thing missing and one thing wrong.
Missing (put this in viewDidLoad):
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "classWiseMgmtTableViewCell")
Wrong (Replace indexPath.row by indexPath.section in cellForRowAt):
cell.textLabel?.text = model[indexPath.section]

Adding two Custom cells in tableView

I have a tableView on mainStoryboard with two custom cells.
I would like to set two more cells at different row.
However When I implemented the code the added cells replaces original cells. (Custom cell of "Basic grammar3" and "Basic grammar5" are disappearing.)
I was trying to find the answer but could not find out.
I have image and code added below.
import UIKit
class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tblStoryList: UITableView!
var array = PLIST.shared.mainArray
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.array.count + 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 || indexPath.row == 3 || indexPath.row == 5 {
let cell = tableView.dequeueReusableCell(withIdentifier: "HeaderCell", for: indexPath) as! HeaderCell
cell.headerTitle.text = indexPath.row == 0 ? "First Stage" : indexPath.row == 3 ? "Second Stage" : "Third Stage"
return cell
}
let cell = tableView.dequeueReusableCell(withIdentifier: "StoryTableviewCell", for: indexPath) as! StoryTableviewCell
//making plist file
let dict = self.array[indexPath.row - 1]
let title = dict["title"] as! String
let imageName = dict["image"] as! String
let temp = dict["phrases"] as! [String:Any]
let arr = temp["array"] as! [[String:Any]]
let detail = "progress \(arr.count)/\(arr.count)"
//property to plist file を぀ăȘぐ
cell.imgIcon.image = UIImage.init(named: imageName)
cell.lblTitle.text = title
cell.lblSubtitle.text = detail
cell.selectionStyle = UITableViewCellSelectionStyle.none
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == 0 {
return
}
tableView.deselectRow(at: indexPath as IndexPath, animated:true)
if indexPath.row == 3 {
return
}
tableView.deselectRow(at: indexPath as IndexPath, animated:true)
if indexPath.row == 5 {
return
}
tableView.deselectRow(at: indexPath as IndexPath, animated:true)
let messagesVc = self.storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
messagesVc.object = self.array[indexPath.row - 1]
self.navigationController?.show(messagesVc, sender: self)
}
You could use sections for your table view. Now, you are returning 1 in your numberOfSections function. And it is creating only one section. If you want to use headers, you can use sections for your need. And also you can fill your table view cells with multidimendional arrays. For example:
For adjusting your section headers:
let lessonTitles = ["First Stage", "Second Stage"]
Titles for sections:
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section < lessonTitles.count {
return lessonTitles [section]
}
return nil
}
For adjusting your sections and rows:
let lessons = [["Basic Grammar 1", "Basic Grammar 2"], ["Basic Grammar 3", "Basic Grammar 4"]]
Number of sections function should be:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return lessons.count
}
Number of rows in section should be:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return lessons[section].count
}
And creating your cells is like this:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellText = data[indexPath.section][indexPath.row]
...
}
Try like this...
func numberOfSections(in tableView: UITableView) -> Int
{
return numberOfStages
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return numberOfRowsInCurrentStage
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
return customizedCell
}
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat
{
return requiredHeight
}
func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView?
{
return stageCountView
}
You can use viewForHeaderInSection if you want to show stage count on top.
edit: The comment by raki is the much better solution (use headers). I leave this here in case you want something closer to your existing implementation.
You have to change your numbering scheme in order to insert these additional rows (and not replace existing rows). So you might want to adjust the row for the "normal" elements like this:
func adjustRow(_ row: Int) -> Int {
if row < 3 {
return row
} else if row < 5 {
return row+1
} else {
return row+2
}
}

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.
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
}
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
}
}
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 numberOfSectionsInTableView(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
}
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
}
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: [TTContact]]() //TTContact 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 self.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: [TTContact]]() //A temporary array
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
}
return UITableViewCell()
}
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 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
}
}

Resources