Populate UITableView from dictionary - ios

I'm trying to make a table from the dictionary I have, but got an error. Since I just learning Swift I don't understand how to fix it. Could you help me please? Thank you for advance.
import UIKit
class TableViewController: UITableViewController {
var DictionaryList = dictionary() {
didSet {
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
GIDSignIn.sharedInstance()?.presentingViewController = self
GIDSignIn.sharedInstance()?.restorePreviousSignIn()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return DictionaryList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let entry = DictionaryList[indexPath.row]
var key = Array<String>(self.dictionary.keys)[indexPath.row]
var value = Array<String>(self.dictionary.values)[indexPath.row]
cell.textLabel?.text = key
cell.detailTextLabel?.text = value
return cell
}
var dictionary: Dictionary = [
"line1:" : "description",
"line2:" : "description",
"line3:" : "description",
]
}

You can try like this:
class TableViewController: UITableViewController {
var DictionaryList: Dictionary<String, String> = [:] {
didSet {
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.DictionaryList = [
"line1:":"description",
"line2:":"description",
"line3:":"description",
]
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return DictionaryList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
var key = Array<String>(self.DictionaryList.keys)[indexPath.row]
var value = Array<String>(self.DictionaryList.values)[indexPath.row]
cell?.textLabel?.text = key
cell?.detailTextLabel?.text = value
return cell!
}
}

This is never going to work. Dictionaries are unordered - there is no guarantee by a dictionary that keys is going to preserve the ordering each time it is called. There isn't even a guarantee that values is going to return the values in the same order that it returns the keys.
You will need to re-design your model to turn the dictionary into an ordered collection of some kind.

Related

UITableView creating an iPhone settings screen with Swift

I am trying to create UITableview with cells, identical to the iPhone settings screenshot.
It is part of my homework so i have to do it all in UITableview.
this is what I did with my code, but everything is red and full with errors. I tried to do it following the samples from lessons but it kinda looks all wrong.
Please, help me understand how this thing works and what is wrong.
import UIKit
struct Lines{
var image: [UIImage] = []
var title: [String] = []
}
class Titles {
static func titles() -> [Lines]{
return [
Lines(image: UIImage[ systemName: "airplane" ,"wifi.square.fill", "bitcoinsign.circle.fill", "iphone.homebutton.radiowaves.left.and.right", "personalhotpot" ], title: ["Авиарежим" , "Wi-fi", "Bluetooth", "Сотовая связь", "Режим модема"]),
Lines(image: UIImage[ systemName: "bell.badge.fill" ,"speaker.wave.3.fill", "moon.fill", "iphone.homebutton.radiowaves.left.and.right", "clock.fill" ], title: ["Уведомления", "Звуки,тактильные сигналы", "Не беспокоить", "Экранное время"]),
Lines(image: UIImage[ systemName: "gear" ,"switch.2", "display" ] , title: ["Общие", " Control Centre", "Экран и яркость"])
]
}
}
class SecondTableViewController: UITableViewController {
var lines = Titles.titles()
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension SecondTableViewController: UITableViewDataSource, UITableViewDelegate{
func numberOfSections(in tableView: UITableView) -> Int {
return titles.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return titles[section].title.count
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "SectionCell") as! TableViewCell
let title = titles[section]
cell.image = Lines.image
cell.titleLabel.text = Lines.title
return cell
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SecondTableViewCell") as! TableViewCell
let name = titles[indexPath.section].title[indexPath.row]
cell.image = Lines.image
cell.titleLabel.text = Lines.title
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
}
Thank you!
You use the wrong property name :
func numberOfSections(in tableView: UITableView) -> Int {
// titles is not defined
return titles.count
}
You must use :
func numberOfSections(in tableView: UITableView) -> Int {
return lines.count
}
Same for the other methods. In cellForRow :
let name = titles[indexPath.section].title[indexPath.row]
Let image = titles[indexPath.section].image[indexPath.row]
Should be replaced by :
let name = lines[indexPath.section].title[indexPath.row]
For viewFirHeader you use a cell which is usually not what is done. You have no title for each lines array. You may have to think again what you want to use. The way you organise your data may also be rethink as you have 2 different array for images and titles.

Swift, tableview doesn't get populated

I'm new to Swift and i've been working on an app for a couple of day now. Now I'm trying to fill up my tableview, but for some reason it stays empty. I've checked if my data comes through (by doing a print of my drinks in the updateUi function), and it seems so be there but my view just doesn't get filled. Anyone has a clue what could be wrong?
Thanks a lot!
import UIKit
class SearchResultsTableViewController: UITableViewController {
let cocktailController = CocktailController()
var drinks = [Drink]()
var drink: String!
override func viewDidLoad() {
super.viewDidLoad()
title = drink.capitalized
cocktailController.fetchDrinks(forDrink: drink)
{ (drinks) in
if let drinks = drinks {
self.updateUI(with: drinks)
}
}
}
func updateUI(with drinks: [Drink]) {
DispatchQueue.main.async {
self.drinks = drinks
self.tableView.reloadData()
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return drinks.count
}
override func tableView(_ tableView: UITableView, cellForRowAt
indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier:
"DrinkCellIdentifier", for: indexPath)
configure(cell, forItemAt: indexPath)
return cell
}
func configure(_ cell: UITableViewCell, forItemAt indexPath:
IndexPath) {
let drinkItem = drinks[indexPath.row]
cell.textLabel?.text = drinkItem.strDrink
}
}
Instead of number of sections, you should use number of rows in section to return the tableview row count.
func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
return drinks.count
}

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

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
}

Table view is returning only 1 section instead 2. How solve that?

I'm fetching data from a Realm database and I want to populate a tableview with 2 sections: Autobots and Decepticons. But section property from TableView is returning just the position 0 from the array. Please help me, Thank you.
import UIKit
import RealmSwift
class ViewController: UIViewController, UITableViewDataSource {
let sections = ["Autobots","Decepticons"]
var autobotsNames : [String] = []
var decepticonsNames : [String] = []
var allTransformersNames = [[String]]()
override func viewDidLoad() {
super.viewDidLoad()
print(Realm.Configuration.defaultConfiguration.fileURL!)
let transformers = Transformers()
let transformersData = transformers.TransformersData()
for data in transformersData {
if data.group == "A" {
autobotsNames.append(data.name)
}
else {
decepticonsNames.append(data.name)
}
}
allTransformersNames.append(autobotsNames)
allTransformersNames.append(decepticonsNames)
print(autobotsNames)
print(decepticonsNames)
print(allTransformersNames)
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section]
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.allTransformersNames[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Transformers", for: indexPath)
cell.textLabel?.text = self.allTransformersNames[indexPath.section][indexPath.row]
return cell
}
}
Print Results
TableView showing 1 section

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

Resources