I have the code below, it's a simple address book fetching Name, Phone and Mail from a Firebase Database. all this worked perfect, until I added a Navigation Controller to get access to the Navigation Bar and search function.
now my Tableview won't show anything from the database, I don't know what it does, but its the Nav controller that is blocking something... please help!!
UPDATE!!: It's not loading func cellForRow, witch ofcause means that numberOfRows is returning 0... If I insert a brakepoint I can see that Handle gets one name from the Database so they are still connected, but something i interfering.
class TableVC: UITableViewController {
var Telefonbog: [String] = []
var Handle: FIRDatabaseHandle?
var Ref: FIRDatabaseReference?
var searchController: UISearchController!
var resultController = UITableViewController()
static var _Mail = ""
static var _Mobil = ""
static var _Navn = ""
override func viewDidLoad()
{
super.viewDidLoad()
self.searchController = UISearchController(searchResultsController: self.resultController)
self.tableView.tableHeaderView = self.searchController.searchBar
Ref = FIRDatabase.database().reference()
Handle = Ref?.child("medarbejder EL").observe(.childAdded, with: { (snapshot) in
if let item = snapshot.value as? String
{
self.Telefonbog.append(item)
self.reloadInputViews()
}
})
}
//opætning af telefonbog i tableview
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return Telefonbog.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let bog = UITableViewCell(style: .default, reuseIdentifier: "bog")
bog.textLabel?.text = Telefonbog[indexPath.row]
return bog
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let currentcell = Telefonbog[indexPath.row]
Ref = FIRDatabase.database().reference()
Ref?.child("medarbejder EL").child(currentcell).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let Mail = (value?["Mail"] as? String)!
let Mobil = (value?["Mobil"] as? String)!
TableVC._Mail = Mail
TableVC._Mobil = Mobil
TableVC._Navn = currentcell
self.performSegue(withIdentifier: "kontakt", sender: self)
})
}
}
It seem to be ui update problem. When you get the result, update the ui components asynchronously.
Would you try this? (Swift 2.3)
// Move to a background thread to do some long running work
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
Handle = Ref?.child("medarbejder EL").observe(.childAdded, with: { (snapshot) in
if let item = snapshot.value as? String
{ //Update ui on main-thread
dispatch_async(dispatch_get_main_queue()) {
self.Telefonbog.append(item)
self.reloadInputViews()
/*
* I think your tableView refresh call in "reloadInputViews()"
* If not add this line.
* self.tableView.reloadData()
*/
}
}
})
}
Related
I have table view controller. My problem; The cell in my table view changes every time I enter the view controller. For example; There are 2 data (address-1 and address-2). The first row is address-1, the second row is address-2, when I re-enter the page, the cells change. How can I fix this problem. Thanks
import UIKit
import Firebase
import MapKit
class DenemeTableViewController: UITableViewController {
var ref: DatabaseReference!
let user: User = Auth.auth().currentUser!
var adresListesi = [Adresler]()
var adres: Adresler!
#IBOutlet var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.table.delegate = self
self.table.dataSource = self
Adresdefteri()
}
override func viewWillAppear(_ animated: Bool) {
Adresdefteri()
table.reloadData()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return adresListesi.count
}
//prob.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! DenemeTableViewCell
let adresList = self.adresListesi[indexPath.row]
cell.textLabel?.text = adresList.adresname
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath:IndexPath) {
self.performSegue(withIdentifier: "DENEMEHARİTAGO", sender: indexPath.row)
}
func Adresdefteri() {
ref = Database.database().reference()
let adreslerim = ref
adreslerim?.child("locations").child(user.emailWithoutSpecialCharacters).child("Adresler").observe( .value) { [self] (snapshot) in
if let gelenVeributunu = snapshot.value as? [String:AnyObject]{
self.adresListesi.removeAll()
for gelenSatirVerisi in gelenVeributunu {
if let sozluk = gelenSatirVerisi.value as? NSDictionary {
let key = gelenSatirVerisi.key
let adresname = sozluk["adresname"] as? String ?? ""
let latitude = sozluk["latitude"] as? Double ?? 0.0
let longitude = sozluk["longitude"] as? Double ?? 0.0
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let adres = Adresler(adresname: adresname, latitude: latitude, longitude: longitude, adresid: key)
self.adresListesi.append(adres)
}
}
DispatchQueue.main.async {
self.table.reloadData()
}
}
}
tableview
Sort the data source array after creating it and reloading the table view, that way it stays consistent. Cheers!
self.arrayName = arrayName.sorted(by: { $0.valueToSortByInArray > $1.valueToSortByInArray })
You just have to sort the adresListesti to make sure the order is consistent.
An example would be:
adresListesi.sort { $0.adresname < $02.adresname }
Put that right before DispatchQueue.main.async and it should work.
I have been trying to display data from Firebase and display in the tableView. The data is fetched properly but is not displayed. Apart from the code mentioned below, I have already added the dataSource and Delegate for the table and the identifier for the cell.
This is the HomeTableViewController
import UIKit
import FirebaseDatabase
import Firebase
class HomeTableViewController: UITableViewController {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
print(artists.count)
return artists.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! HomeTableViewCell
print("Here")
let artist = artists[indexPath.row]
cell.artist = artist
return cell
}
let artistRef = Database.database().reference().child("Fund")
var artists = [ArtistList]()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
artistRef.observe(.value, with: { (snapshot) in
self.artists.removeAll()
for child in snapshot.children {
let childSnapshot = child as! DataSnapshot
let artist = ArtistList(snapshot: childSnapshot)
print(artist)
self.artists.insert(artist, at: 0)
}
self.tableView.reloadData()
})
}
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.isHidden = false
self.navigationItem.hidesBackButton = true
title = "Artcall"
self.tableView.estimatedRowHeight = 290.0
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.reloadData()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem
}
}
The main code for my HomeTableViewCell is
var artist: ArtistList!{
didSet
{
let dateString:String = String(format: "%#", artist.date as CVarArg)
labelDate.text = dateString
labelName.text = artist.name
labelLocation.text = artist.city
}
}
And the code for my model, ArtistList is here
class ArtistList
{
var name: String = ""
var city: String = ""
var date: Int64 = 0
let ref: DatabaseReference!
init(snapshot: DataSnapshot) {
ref = snapshot.ref
if let value = snapshot.value as? [String : Any] {
name = value["artist_name"] as! String
city = value["perf_location"] as! String
date = value["target_date"] as! Int64
}
}
}
UI updates need to occur on the main thread if they are being called within asynchronous functions, so try putting the tableview reload on the main queue like this:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
artistRef.observe(.value, with: { (snapshot) in
self.artists.removeAll()
for child in snapshot.children {
let childSnapshot = child as! DataSnapshot
let artist = ArtistList(snapshot: childSnapshot)
print(artist)
self.artists.insert(artist, at: 0)
}
//Print the artists array to verify it is definitely populated
print(self.artists)
//Reload tableview on the main queue
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
}
I have ran into a very interesting problem in my application. Upon viewing the users profile page the data in my tableview is being pulled and printed into the console, the problem though is that my information will not load into my tablecell.
If i were to leave the current tab and then go back to the profile page my data will then be loaded. I am using firebase in order to pull my information down.
I would like for the data to be there upon the first time viewing the page.
ProfileViewController
var cellId = "cell"
var userGames = [Game]()
var userCurrentGames = [Any]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.register(ProfileGameCell.self, forCellReuseIdentifier: cellId)
//I CALL MY FUNCTIONS UPON FIRST LOAD
getCurrentUser()
getUsersGames()
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//I CALL FUCTIONS EVERY TIME THE VIEW LOADS INCASE DATA CHANGES
getCurrentUser()
getUsersInformation()
getUsersGames()
}
func getUsersGames() {
let ref = Database.database().reference()
let current = getCurrentUser()
//I FIND ALL THE GAMES IN THE USERS REF AND APPEND THEIR KEY TO THE ARRAY
ref.child("users").child(current).child("user games").observe(.value, with: {(snapshot) in
if let snapshot = snapshot.children.allObjects as? [DataSnapshot] {
self.userCurrentGames = []
for snap in snapshot {
let gameKey = snap.key
print("SNAPSHOT DATAAA: \(gameKey)")
self.userCurrentGames.append(gameKey)
}
}
})
displayUsersGames()
}
func displayUsersGames() {
let ref = Database.database().reference()
self.userGames = []
//I FIND ALL THE GAMES AND APPEND THEM TO THE ACTUAL GAME ARRAY
for i in userCurrentGames {
ref.child("games").child("\(i)").observeSingleEvent(of: .value, with: { (gameSnap) in
if let gameDict = gameSnap.value as? Dictionary<String, AnyObject> {
let key = gameSnap.key
let game = Game(postKey: key, gameData: gameDict)
self.userGames.append(game)
self.tableView.reloadData()
}
})
}
}
Table View Functions
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let game = userGames[indexPath.row]
if let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as? ProfileGameCell {
cell.adminButton.tag = indexPath.row
cell.configureUserGameCell(game: game)
return cell
} else {
return ProfileGameCell()
}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userGames.count
}
The problem is very clear:
observe works asynchronously – the result is returned later – so userCurrentGames is empty when displayUsersGames() is called.
A solution is to move displayUsersGames() into the completion block
...
ref.child("users").child(current).child("user games").observe(.value, with: {(snapshot) in
if let snapshot = snapshot.children.allObjects as? [DataSnapshot] {
self.userCurrentGames = []
for snap in snapshot {
let gameKey = snap.key
print("SNAPSHOT DATAAA: \(gameKey)")
self.userCurrentGames.append(gameKey)
}
self.displayUsersGames()
}
})
Note:
Force downcast the cell
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! ProfileGameCell
The code must not crash. If it does it reveals a design error.
All the tableview functions are working except cell for row index path .
The problem maybe that foods array is empty so the number for rows is 0 so the cell for row at index path is not called
#IBOutlet weak var foooods: UITableView!
var databaseref = Database.database().reference()
var img : AnyObject?
var foods = [String?]()
override func viewDidLoad() {
super.viewDidLoad()
self.databaseref.child("basic food").observe(.childAdded, with: {( snap: DataSnapshot) in
let snapp = snap.value as! [String:AnyObject]
if let x = snapp["name"] as! String? {
self.foods.insert(x, at: 0)
//self.foods.append(x)
}
})
self.foooods.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.foods.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("difufuehf")
let cell : foodsTableViewCell = tableView.dequeueReusableCell(withIdentifier: "aupa", for:indexPath) as! foodsTableViewCell
print("fufvksdfvysdgfvjdsgfdsygfvds,jhvjsdvsdjvguydsfgdsylfgdsyfgsdlygfsiygf")
if let foo = foods[indexPath.row] {
print(foo)
cell.food.text = foo
}
return cell
}
This must be a duplicate but I can't find one.
Your issue is that you call reloadData in the wrong place which results in it being called far too soon. You need to call it inside the completion block, after you update your data model.
And you need to make sure it gets called on the main queue.
override func viewDidLoad() {
super.viewDidLoad()
self.databaseref.child("basic food").observe(.childAdded, with: {( snap: DataSnapshot) in
if let snapp = snap.value as? [String:Any], let x = snapp["name"] as? String {
self.foods.insert(x, at: 0)
//self.foods.append(x)
DispatchQueue.main.async {
self.foooods.reloadData()
}
}
})
}
Note that I also fixed the way the value is obtained. You really need to avoid force unwrapping and force casting.
My app crashing while search a text in a searchbar with error: thread1:signal SIGABRT probably the problem updateSearchResults() method?
or type of array? I'm beginner with swift any idea?
#IBOutlet weak var tableView: UITableView!
var data = [Any]()
var ref:FIRDatabaseReference!
// Filter Data from Firebase
var filteredData = [Any]()
// Declare searchBar
let searchController = UISearchController(searchResultsController: nil)
//is the device landscape or portrait
var isPortraid = true
#IBOutlet weak var bannerView: GADBannerView!
func fetchDataFromFirebase(){
EZLoadingActivity.show("caricamento...", disableUI: true)
ref = FIRDatabase.database().reference()
ref.observe(.value, with: { (snapshot) in
let dataDict = snapshot.value as! NSDictionary
self.data = dataDict["data"] as! [Any]
self.filteredData = self.data
print ("Sacco di merda:\(self.filteredData)")
self.tableView.reloadData()
EZLoadingActivity.hide()
})
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
fetchDataFromFirebase()
// Implement searchBar
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
tableView.tableHeaderView = searchController.searchBar
NotificationCenter.default.addObserver(self, selector: #selector(MainViewController.orientationChanged), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
}
//TableView Data Source and Delegate
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MainCell", for:indexPath) as! MainScreenTableViewCell
let rowData = self.filteredData[indexPath.row] as! NSDictionary
let imageName = rowData["imageName"] as! String
cell.backgroundImageView.image = UIImage(named: imageName)
let label = rowData["categoryName"] as! String
cell.mealCategoryLabel.text = label
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let categoryViewController = storyboard.instantiateViewController(withIdentifier: "CategoryViewController") as! CategoryViewController
let rowData = self.data[indexPath.row] as! NSDictionary
categoryViewController.categoryTitle = rowData["categoryName"] as! String
let categoryData = rowData["category"] as! [Any]
categoryViewController.data = categoryData
self.navigationController?.pushViewController(categoryViewController, animated: true)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if isPortraid {
return UIScreen.main.bounds.height/3
} else {
return UIScreen.main.bounds.height/1.2
}
}
//Method for update search
func updateSearchResults(for searchController: UISearchController) {
if searchController.searchBar.text! == ""{
filteredData = data
} else {
filteredData = data.filter{($0 as AnyObject).contains(searchController.searchBar.text!)}
}
self.tableView.reloadData()
}
if searchController.searchBar.text! == ""
This is almost certainly the offender. The text property on UI objects is typically nil when it's empty, so when you force unwrap it your app crashes. You should never force unwrap something unless you are absolutely certain it will never be nil at that point.
There's a couple different ways you can handle this, which basically amount to making sure text isn't nil before you do anything with it.
Personally I would rewrite the if statement to unwrap the optional for the non-empty case:
if let text = searchController.searchBar.text, text != "" {
filteredData = data.filter{($0 as AnyObject).contains(text)}
} else {
filteredData = data
}
You could also use nil-coalescing:
if (searchController.searchBar.text ?? "") == ""
but personally I prefer to write it to avoid force unwrapping even when you're sure it isn't nil, so I would recommend the first one.