I'm trying to create a tableView within a viewController. I know it is annoying, but the table looks much better that way. I am also trying to incorporate data from Firebase to put into the table. Unfortunately when I run the code, it only shows a blank table. The console was able to print the desired data, but it just won't show on the actual table. Please let me know what I'm doing wrong. Many thanks!
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var user = Auth.auth().currentUser
var users = [Users]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.reloadData()
fetchUser()
}
func fetchUser() {
Database.database().reference(fromURL: "https://yala-2018.firebaseio.com/").child("users").observe(.childAdded, with: { (DataSnapshot) in
if let dictionary = DataSnapshot.value as? [String: AnyObject] {
let user = Users()
// user.setValuesForKeys(dictionary)
user.name = dictionary["name"] as! String
user.age = dictionary["age"] as! String
user.sex = dictionary["sex"] as! String
self.users.append(user)
print(user.name, user.age)
DispatchQueue.main.async(execute: {
self.tableView.reloadData()
})
}
})
}
func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return users.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "guide", for: indexPath)
let user = users[indexPath.row]
cell.textLabel?.text = user.name
cell.detailTextLabel?.text = "Age: \(user.age) Sex: \(user.sex)"
// Configure the cell...
return cell
}
func tableView(tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
}
Change this to 1 as by 0 you mean no sections which will display empty tableView even if there is a data , or remove it as by default it's 1
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
Related
I know the issue is regarding scope; I just dont know if theres an easy fix I can do without changing my code much. but open to anything
import UIKit
import Firebase
import FirebaseAuth
import FirebaseFirestore
class AdminViewController: UIViewController, UITableViewDelegate,
UITableViewDataSource {
#IBOutlet var custodianRunReportsTableView: UITableView!
var dbRef: DatabaseReference!
var data = [String]()
override func viewDidLoad() {
super.viewDidLoad()
startObservingDB()
custodianRunReportsTableView.delegate = self
custodianRunReportsTableView.dataSource = self
// Do any additional setup after loading the view.
}
// Gets users' names from Cloud Firestore Database
func startObservingDB() {
let db = Firestore.firestore()
let namesDocumentRef = db.collection("Users").document("Names")
namesDocumentRef.addSnapshotListener { DocumentSnapshot, error in
guard let document = DocumentSnapshot else {
print("Error fetching document: \(error!)")
return
}
guard let data = document.data() else {
print("Document data was empty.")
return
}
let values = data.values
let rowNumber = data.count
print("Current data: \(data)")
print("Current data has the values: \(values)")
print("Current data totals \(data.count) items.")
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped me!")
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rowNumber
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = custodianRunReportsTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = values[indexPath.row]
print("Names in cell: \(values)")
print("\(data)")
return cell
}
}
UPDATED CODE:
This is updated after an answer to the original post. The code no longer has the unresolved identified error; however, the table view does not display any cell text and is empty.
class AdminViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var custodianRunReportsTableView: UITableView!
var valuesArray:[String] = []
var data:[String] = []
var namesDocumentRef:DocumentReference!
override func viewDidLoad() {
super.viewDidLoad()
startObservingDB()
custodianRunReportsTableView.delegate = self
custodianRunReportsTableView.dataSource = self
}
// Gets users' names from Cloud Firestore Database
func startObservingDB() {
var namesDocumentRef:DocumentReference!
let db = Firestore.firestore()
namesDocumentRef = db.collection("Users").document("Names")
namesDocumentRef.addSnapshotListener { DocumentSnapshot, error in
if error != nil{
return
}
else {
guard let snapshot = DocumentSnapshot, snapshot.exists else {return}
guard let data = snapshot.data() else { return }
self.valuesArray = Array(data.values) as! Array<String>
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped me!")
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return valuesArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = custodianRunReportsTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = valuesArray[indexPath.row]
return cell
}
}
The variables data,values and rowCount are inside closure so you can't just write values[indexPath.row] because you can't return from inside closure. Usually completionHandlers are used for this purpose, but in this scenario, you should put the values inside an array and then use inside the tableview. Let me show you how to do.
At the start of your viewController, declare a string array.
var valuesArray:[String] = []
Then, inside modify your startObservingDB() function
func startObservingDB() {
var docRef:DocumentReference!
let db = Firestore.firestore()
docRef = db.collection("Users").document("Names")
docRef.addSnapshotListener { (docSnapshot, error) in
if error != nil {
return
}
else {
guard let snapshot = docSnapshot, snapshot.exists else {return}
guard let data = snapshot.data() else { return }
self.valuesArray = Array(data.values) as! Array<String>
self.tableView.reloadData()
}
}
}
In viewDidLoad call this function
startObservingDB()
Then in tableView methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return ValuesArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = custodianRunReportsTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = valuesArray[indexPath.row]
return cell
}
In your class AdminViewController you can reference properties defined outside of methods with self (in your example you have dbRef that you can reference like self.dbRef in methods).
So I suggest you make properties for data, value and rowNumber and change them in startObservingDB method rather than declare them. This way you will be able to reference them in tableView methods.
I have a tableView connected to the array. The array isn't empty but the tableView shows nothing other than lines. Can you help me, please?
var invoicesDictionary = [String:[String]]()
var invoicesSectionTitles = [String]()
var invoices: [String] = ["JV1", "R1", "JV2"]
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
for invoice in invoices {
let invoiceKey = String(invoice.prefix(1))
if var invoiceValues = self.invoicesDictionary[invoiceKey] {
invoiceValues.append(invoice)
self.invoicesDictionary[invoiceKey] = invoiceValues
} else {
self.invoicesDictionary[invoiceKey] = [invoice]
}
}
invoicesSectionTitles = [String](invoicesDictionary.keys)
invoicesSectionTitles = invoicesSectionTitles.sorted(by: { $0 < $1 })
tableView.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return invoicesSectionTitles.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let invoiceKey = invoicesSectionTitles[section]
if let invoiceValues = invoicesDictionary[invoiceKey] {
return invoiceValues.count
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let invoiceKey = invoicesSectionTitles[indexPath.section]
if let invoiceValues = invoicesDictionary[invoiceKey] {
cell.textLabel?.text = invoiceValues[indexPath.row]
}
return cell
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return invoicesSectionTitles[section]
}
func sectionIndexTitles(for tableView: UITableView) -> [String]? {
return invoicesSectionTitles
}
I printed out the array which is without a doubt not empty. I tried this code on an another project and it just worked.
You have to put this in viewDidLoad :
tableView.delegate = self
tableView.dataSource = self
You have to tell the View controller that it's the delegate or assigned VC for the table View functionality by passing the following lines inside viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
I created a UIViewController containing a UITableView.
I want it to display files I created at another position.
The corresponding class looks like this:
import UIKit
class BackupViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var rows:[String] = []
override func viewDidLoad() {
super.viewDidLoad()
self.table.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
self.table.dataSource = self
self.table.delegate = self
getFiles()
// Do any additional setup after loading the view.
}
func getFiles() {
let filemgr = FileManager.default
let path = filemgr.urls(for: FileManager.SearchPathDirectory.documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).last
do {
rows = try filemgr.contentsOfDirectory(atPath: (path?.path)!)
// process files
} catch {
print("Error while enumerating files")
}
table.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return rows.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:UITableViewCell = self.table.dequeueReusableCell(withIdentifier: "cell") as! UITableViewCell
cell.textLabel?.text = rows[indexPath.row]
return cell
}
#IBOutlet weak var table: UITableView!
}
It reads the files correctly and when
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int)
is called (which happens 3 times for some reason), the array has (at the moment) 2 elements. However, the function
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
is never called and of course I get an empty table.
You need to reload your tableView after fetching files in getFile() function.
func getFiles() {
let filemgr = FileManager.default
let path = filemgr.urls(for: FileManager.SearchPathDirectory.documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).last
do {
rows = try filemgr.contentsOfDirectory(atPath: (path?.path)!)
self.table.reloadData()
// process files
} catch {
print("Error while enumerating files")
}
/*for file in files {
rows.append(file.absoluteString)
}*/
showFiles = true
}
You need to call reloadData() once row will have some value in it.
I'm trying to delete the data from firebase with no luck so far. This is the code I'm using, can anyone give me a hand with it please.
class TableViewController: UITableViewController {
var ref: FIRDatabaseReference?
var grocery = [Grocery]()
override func viewDidLoad() {
super.viewDidLoad()
loadData()
}
func loadData() {
let uid = FIRAuth.auth()?.currentUser?.uid
FIRDatabase.database().reference().child("Users").child(uid!).child("Grocery").observe(.childAdded) { (snspshot: FIRDataSnapshot) in
if let dict = snspshot.value as? [String: Any] {
let Items = dict["Item"] as! String
let Quintities = dict["Quintities"] as! String
let Done = dict["Done"] as! Bool
let themBe = Grocery(Items: Items, Quintitiess: Quintities, Dones: Done)
self.grocery.append(themBe)
print(themBe)
self.tableView.reloadData()
}
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return grocery.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TasksTableViewCell") as! TasksTableViewCell
cell.titleLabel?.text = grocery[indexPath.row].Item
cell.numLabel?.text = grocery[indexPath.row].Quintities
return cell
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
self.grocery.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath], with: .automatic)
}
}
----------
import Foundation
class Grocery {
var Item: String
var Quintities: String
var Done: Bool
init(Items: String, Quintitiess: String, Dones: Bool) {
Item = Items
Quintities = Quintitiess
Done = Dones
}
}
You are only deleting data for your UITableView. The logic that you need is to delete from your UITableView and Fireabase Database. As the firebase docs says you can either call removeValue, or setValue to nil or updateChildValues.
To make the deletion easier, I'd save the key of the object where the data is saved (snapshot.keys), so when you want to delete you can just get that key and perform actions.
How would I fill the TableView Cell using Firebase? I'm trying to fill it using an array which would be filled by Firebase, but since the data is filled asynchronously, I don't know how to make the array filled with data outside of the getData() function. As of now the data is filled inside the completion handler, but outside of that it is nil.
import UIKit
import Firebase
class ProxiesListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var ProxiesTableView: UITableView!
var proxyname:[String] = []
var proxyprice:[Int] = []
var proxycountry:[String] = []
var databaseRef : FIRDatabaseReference!
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return proxyname.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ProxiesTableViewCell
cell.countryFlag.image = UIImage(named: proxycountry[indexPath.row])
cell.NameLabel.text = proxyname[indexPath.row]
cell.priceLabel.text = "$" + String(proxyprice[indexPath.row])
return(cell)
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//add code
}
override func viewDidLoad() {
super.viewDidLoad()
ProxiesTableView.delegate = self
ProxiesTableView.dataSource = self
getData()
}
func getData() {
databaseRef = FIRDatabase.database().reference()
/*
proxyname = ["50 Proxies (24 Hours)", "100 Proxies", "10 Proxies", "60 Proxies (48 Hours)"]
proxyprice = [600, 140, 60, 10]
proxycountry = ["CA", "UK", "UK", "US"]
*/
self.databaseRef.child("ProxiesItemsFeed").observe(.childAdded, with: { (snapshot) in
if let proxynames = snapshot.key as String! {
self.proxyname.append(proxynames)
self.databaseRef.child("ProxiesItemsFeed").child(proxynames).observe(.childAdded, with: { (snapshot) in
if let country = snapshot.key as String! {
self.proxycountry.append(country)
}
if let price = snapshot.value as! Int! {
self.proxyprice.append(price)
}
})
}
})
}
}
You should add a tableView.reloadData() inside your completion handler (i.e., the one in your getData() method).
Method documentation:
Call this method to reload all the data that is used to construct the table, including cells, section headers and footers, index arrays, and so on.