I have a tableView which I am trying to populate from Firebase, but I have a problem with reloadData(),it does not refresh the table.
This is my code:
func showAnswers(pos: Int, name: String){
let n = name
answersReference.observe(DataEventType.value, with: {(snapshot) in
if(!snapshot.exists()){
}else{
let answers = snapshot.children
for answer in answers{
let answersFromDB = AnswerFromDBObject()
answersFromDB.setQuestion(question: (answer as! DataSnapshot).key)
let ansData = (answer as! DataSnapshot).children
for a in ansData{
answersFromDB.setAnswer(answer: (a as! DataSnapshot).key)
}
print("firebase answer: \(answersFromDB.getAnswer())")
self.answerFromDBObject += [answersFromDB]
}
print("array count: \(self.answerFromDBObject.count)")
self.userInfoAnwers.text = NSLocalizedString("users_questions", comment: "").replacingOccurrences(of: "(usernamehere)", with: n)
self.answersTable.reloadData()
}
})
}
And my tableView delegate methods:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return answerFromDBObject.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "answersCell") as! SeeAnswersCell
let answer = answerFromDBObject[indexPath.row]
cell.question.text = answer.getQuestion()
cell.answer.text = answer.getAnswer()
return cell
}
These are print methods on console:
firebase answer: Red
firebase answer: Fine
firebase answer: London
array count: 3
Try this after the final })
DispatchQueue.main.async {
self.answersTable.reloadData()
}
You are doing this to be able to reload it in the main thread
Check tableView delegate & data source connected
If you are using Xib cell, check if it's registered
Related
I am new to Swift and I'm making my first application, so the question will be asked will be in a simpler way, not exactly with programming terms.
I have a table with names, which are also written in the Firebase. I need the ones I will select, and I press the save button, to write them (selected rows with names) in the database as a new child.
And my code is:
let uid = Auth.auth().currentUser?.uid
var ref: DatabaseReference!
var guestList = [GuestModel]()
var indexArray: [Int] = []
func addTable(){
ref = Database.database().reference().child("userInfo").child(uid!).child("tables")
let key = ref.childByAutoId().key
let table = ["id": key, "tableName": entertableNameTextField.text! as String, "tableCapacity": tableCapacityTextField.text! as String, "tableNo": enterTableNumber.text! as String, "guestsOnTable" : "\(indexArray)" as String]
ref.child(key!).setValue(table)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return guestList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "guestListCell", for: indexPath) as! GuestListToTableTableViewCell
let guest: GuestModel
guest = guestList[indexPath.row]
cell.fullNameLabel.text = guest.guestName! + " " + guest.guestFamilyName!
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
indexArray.append(indexPath.row)
}
you can take a key(in Firbase) and set is default false while click on it set it true.
and your logic will be.
in didselect method
you need to update that key to true.
tblView.reloadData()
in cellforRow method
check selected true change your cell design as well you want.
I would like to retrieve data from my simple Firestore database
I have this database:
then I have a model class where I have a method responsible for retrieving a data which looks like this:
func getDataFromDatabase() -> [String] {
var notes: [String] = []
collectionRef = Firestore.firestore().collection("Notes")
collectionRef.addSnapshotListener { querySnapshot, error in
guard let documents = querySnapshot?.documents else {
print("Error fetching documents: \(error!)")
return
}
notes = documents.map { $0["text"]! } as! [String] // text is a field saved in document
print("inside notes: \(notes)")
}
print("outside notes: \(notes)")
return notes
}
and as a UI representation I have tableViewController. Let's take one of the methods, for example
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("tableview numberOfRowsInSection called")
return model.getDataFromDatabase().count
}
Then numberOfRows is 0 and the output in the console is:
and I am ending up with no cells in tableView. I added a breakpoint and it doesn't jump inside the listener.
And even though I have 3 of them, they are kinda "late"? They are loaded afterwards. And then the tableView doesn't show anything but console says (later) that there are 3 cells.
If needed, there is also my method for showing the cells names:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("Cells")
let cell = tableView.dequeueReusableCell(withIdentifier: "firstCell", for: indexPath)
cell.textLabel!.text = String(model.getDataFromDatabase()[indexPath.row].prefix(30))
return cell
}
but this method is not even loaded (no print in the console) and this method is written below the method with numberOfRowsInSection.
I have also 2 errors (I don't know why each line is written twice) and these are:
but I don't think it has something to do with the problem.
Thank you for your help!
As #Galo Torres Sevilla mentioned, addSnapshotListener method is async and you need to add completion handler to your getDataFromDatabase() function.
Make following changes in your code:
Declare Global variable for notes.
var list_notes = [String]()
Add completion handler to getDataFromDatabase() method.
func getDataFromDatabase(callback: #escaping([String]) -> Void) {
var notes: [String] = []
collectionRef = Firestore.firestore().collection("Notes")
collectionRef.addSnapshotListener { querySnapshot, error in
guard let documents = querySnapshot?.documents else {
print("Error fetching documents: \(error!)")
return
}
notes = documents.map { $0["text"]! } as! [String] // text is a field saved in document
print("inside notes: \(notes)")
callback(notes)
}
}
Lastly, call function on appropriate location where you want to fetch notes and assign retrieved notes to your global variable and reload TableView like below:
self.getDataFromDatabase { (list_notes) in
self.list_notes = list_notes
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
Changes in TableView:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("tableview numberOfRowsInSection called")
return self.list_notes.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("Cells")
let cell = tableView.dequeueReusableCell(withIdentifier: "firstCell", for: indexPath)
cell.textLabel!.text = String(self.list_notes[indexPath.row].prefix(30))
return cell
}
All you need to do is refresh the table cell every time you retrieve the data. Put this code after you set your data inside the array.
self.tableView.reloadData()
I am trying to load some data from CloudKit to populate a tableview with custom cells and I am having difficulty getting the data to appear.
When I define the number of rows as the count of the CKRecord array the tableview shows up, but with nothing loaded into them. They are just spaced out correctly for the images to be in there. Also, when I set breakpoints at let record = matches[indexPath.row] it won't trigger.
However, if I change the return matches.count to an actual number, the project crashes at let record = matches[indexPath.row]
with the error that the index is out of range. I want to keep the number of rows as the count for the record array, but that is the only change that will actually execute the override function that calls the tableview cell.
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return matches.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Card", for: indexPath) as! MatchTableViewCell
let record = matches[indexPath.row]
if let img = record.value(forKey: "Picture") as? CKAsset {
cell.profileimg.image = UIImage(contentsOfFile: img.fileURL.path)
}
return cell
}
Any advice is appreciated
Update- here is where I load the model
func loadModel() {
let totalMatch = (defaults.object(forKey: "passedmatch") as! [String] )
let predicateMatch = NSPredicate(format: "not (UserID IN %#)", totalMatch )
ProfilesbyLocation = defaults.object(forKey: "Location") as! String
let query = CKQuery(recordType: ProfilesbyLocation , predicate: predicateMatch )
publicData.perform(query, inZoneWith: nil,completionHandler: ({results, error in
print("loading")
if (error != nil) {
let nsError = error! as NSError
print(nsError.localizedDescription)
print ("error")
} else {
if results!.count > 0 {
DispatchQueue.main.async() {
self.tableView.reloadData()
self.refresh.endRefreshing()
print("refreshed")
}
}
}
}
)
)}
It sounds like you are not calling
tableview.reloadData()
after your asynchronous data comes in. Leave numberOfRowsInSection as it is and make sure when your network request comes back you are reloading. That should do it.
If you received your data from background thread(or queue), please make sure when you update your UI elements under the main thread(or queue). Try this:
DispatchQueue.main.async {
tableView.reloadData()
}
This solved my problem.
First, disconnect the Delegate/DataSource of the TableView from the Interface Builder refer this image. When you have finally prepared your data that is matches array then add this.
`DispatchQueue.main.async {
tableView.dataSource = self
tableView.delegate = self
tableView.reloadData
}`
Hope this helps.
It looks like your matches is incorrect. Before you call func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int)
, you should check your matches.
If you are still not sure about that. You can init your matches with string literals and have a try.
I have an app that has a tableview embedded in a ViewController and whenever I navigate to another ViewController and navigate back to the table view, the cells repeat when I scroll. Does anyone have any advice on how to prevent this? The current code for the tableview is :
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return marathonRaces.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let singleCell: marathonTableViewCell = tableView.dequeueReusableCellWithIdentifier("marathonCell") as! marathonTableViewCell
singleCell.marathonName.text = marathonRaces[indexPath.row]
singleCell.entry.text = "\(entryNumber[indexPath.row])"
singleCell.entries.text = "\(entires[indexPath.row])"
singleCell.length.text = "\(length[indexPath.row])"
return singleCell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let indexPath = self.marathonsTableView.indexPathForSelectedRow!
let currentCell = marathonsTableView.cellForRowAtIndexPath(indexPath) as! marathonTableViewCell
let marathonEvents = currentCell.marathonName.text
self.performSegueWithIdentifier("marathonDetail", sender: self)
}
I am using swift, xcode7, and parse as my backend
the only relevant code within viewDidAppear would be :
var query = PFQuery(className: “Marathons")
query.orderByAscending("end")
query.findObjectsInBackgroundWithBlock { (marathons, error:NSError?) -> Void in
if(error == nil ){
//success
for marathon in marathons! {
self.marathonRaces
.append(marathon[“marathonName"] as! String)
self.entry.append(marathon[“entryNumber"] as! Int)
self.entries.append(marathon[“entries"] as! Int)
self.length.append(marathon[“length"] as! Int)
}
self.marathonsTableView.reloadData()
}else {
print(error)
}
}
The problem is in your viewDidAppear method. Every time the controller appears you fetch data from background, append them to your arrays and reload the tableview. Move the code for fetching data to viewDidLoad for example and "repeating" should be gone.
Have you checked to see if the datasource marathonRaces is gaining more entries?
You may be adding more entries on each back navigation, if so either do not add them or remove all entries prior to adding them.
I'm working on my first Swift app, and stuck on a bug forever. When adding a new entry to the coredata, everything goes fine the first time. However, with additional items, the previously added item is duplicated in the table.
The data is not duplicated, only the cell. When the app is reloaded, the cells are displayed correctly.
Here's the code that populates the cells:
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if let fetchedSections: AnyObject = fetchedResultController.sections as AnyObject? {
return fetchedSections.count
} else {
return 0
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let fetchedSections: AnyObject = fetchedResultController.sections as AnyObject? {
return fetchedSections[section].numberOfObjects
} else {
return 0
}
}
func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
let lookup = sortedArray[indexPath.row] as! NSManagedObjectID
let spot = spotsDict[lookup] as! Spots
let cell = tableView.dequeueReusableCellWithIdentifier("SpotCell", forIndexPath:indexPath) as! SpotCell
println(sortedArray)
println(spot)
cell.spotLabel.text = spot.title
cell.spotPhoto.image = self.imagesDict[lookup]
cell.distanceLabel.text = self.distanceStringDict[lookup] as NSString! as String
cell.spotPhoto.layer.cornerRadius = 4
cell.spotPhoto.clipsToBounds = true
return cell
}
Please replace your code
let lookup = sortedArray[indexPath.row] as! NSManagedObjectID
with below code in cellForRowAtIndexPath method.
let lookup = sortedArray[indexPath.section] as! NSManagedObjectID
The root of the problem was in my sorting method.. simplified the code a lot, and sorted with sortDescriptors instead.