Swift: numberOfRowsInSection and cellForRowAt not being invoked - ios

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

Related

Swift Table View Cell Changing Data

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.

Swift 4: UITableViewCell does will not display data upon initial load, but will load on second load

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.

searching and filter array firebase data swift3

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.

Navigation controller and Table view with firebase database

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()
*/
}
}
})
}

Value in JSON does not display in tableviewcell custom

Can't display data in TableViewCell.Data reports of events, but the when you open the array "sports" display the data in cels no.The display of the title occurs and the transfer is ended...
This is my json code...
Event.swift
import UIKit
struct Event {
let match : String
let forecast : String
let data : String
let image : UIImage
var sports : [Sport]
init (match : String, forecast : String, data: String, image : UIImage, sports : [Sport]) {
self.match = match
self.forecast = forecast
self.data = data
self.image = image
self.sports = sports
}
static func eventsFromBundle ()-> [Event] {
var events = [Event] ()
guard let url = Bundle.main.url(forResource: "events", withExtension: "json") else {
return events
}
do {
let data = try Data(contentsOf: url)
guard let rootObject = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String : Any] else {
return events
}
guard let eventObjects = rootObject["events"] as? [[String: AnyObject]] else {
return events
}
for eventObject in eventObjects {
if let match = eventObject["match"] as? String,
let forecast = eventObject["forecast"] as? String,
let data = eventObject["data"] as? String,
let imageName = eventObject["image"] as? String,
let image = UIImage(named: imageName),
let sportsObject = eventObject["sports"] as? [[String : String]]{
var sports = [Sport]()
for sportObject in sportsObject {
if let nameTitle = sportObject["name"] ,
let titleName = sportObject["image"],
let titleImage = UIImage(named: titleName + ".jpg"),
let prognozLabel = sportObject["prognoz"],
let obzor = sportObject["obzor"] {
sports.append(Sport(name: nameTitle, prognoz: prognozLabel, image: titleImage, obzor: obzor, isExpanded: false))
}
}
let event = Event(match: match, forecast: forecast, data: data, image: image, sports: sports)
events.append(event)
}
}
} catch {
return events
}
return events
}
}
import UIKit
class SportViewController: BaseViewController {
var events = Event.eventsFromBundle ()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
addSlideMenuButton()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 100
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
NotificationCenter.default.addObserver(forName: .UIContentSizeCategoryDidChange, object: .none, queue: OperationQueue.main) { [weak self] _ in
self?.tableView.reloadData()
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? SportDetailViewController,
let indexPath = tableView.indexPathForSelectedRow {
destination.selectedEvent = events[indexPath.row]
}
}
}
extension SportViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return events.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellMatch", for: indexPath) as! SportTableViewCell
let event = events[indexPath.row]
cell.matchLabel.text = event.match
cell.imageMatch.image = event.image
cell.forecastLabel.text = event.forecast
cell.dataLabel.text = event.data
cell.matchLabel.font = UIFont.preferredFont(forTextStyle: .subheadline)
cell.forecastLabel.font = UIFont.preferredFont(forTextStyle: .callout)
return cell
}
}
Her is the controller.SportDetailViewController.swift
import UIKit
class SportDetailViewController: UIViewController {
var selectedEvent : Event!
let obzorText = "Select for more info >"
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
title = selectedEvent.match
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 300
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
}
extension SportDetailViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return selectedEvent.sports.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell : SportDetailTableViewCell = tableView.dequeueReusableCell(withIdentifier: "cellMatch", for: indexPath) as! SportDetailTableViewCell
let sport = selectedEvent.sports[indexPath.row]
cell.nameTitle.text = sport.name
cell.titleImage.image = sport.image
cell.prognozLabel.text = sport.prognoz
cell.selectionStyle = .none
cell.nameTitle.backgroundColor = UIColor.darkGray
cell.backgroundColor = UIColor.red
cell.obzorText.text = sport.isExpanded ? sport.obzor : obzorText
cell.obzorText.textAlignment = sport.isExpanded ? .left : .center
return cell
}
}
extension SportDetailViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let cell = tableView.cellForRow(at: indexPath) as? SportDetailTableViewCell else { return }
var sport = selectedEvent.sports[indexPath.row]
sport.isExpanded = !sport.isExpanded
selectedEvent.sports[indexPath.row] = sport
cell.obzorText.text = sport.isExpanded ? sport.obzor : obzorText
cell.obzorText.textAlignment = sport.isExpanded ? .left : .center
tableView.beginUpdates()
tableView.endUpdates()
tableView.scrollToRow(at: indexPath, at: .top, animated: true)
}
}
all these methods have tried: tableview.datasource = self , tableview.delegate = self и reloadData().....in viewDidLoad.
Delete this init from your struct: (because struct gets free initializer)
init (match : String, forecast : String, data: String, image : UIImage, sports : [Sport]) {
self.match = match
self.forecast = forecast
self.data = data
self.image = image
self.sports = sports
}
Now, your var events won't be populated as you are calling method in class scope. So change this:
class SportViewController: BaseViewController {
var events = Event.eventsFromBundle ()
...
...
}
to
class SportViewController: BaseViewController {
var events = [Event]()
...
...
override func viewDidLoad() {
super.viewDidLoad()
addSlideMenuButton()
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 100
events = Event().eventsFromBundle()
}
...
...
}
This should solve your problem.

Resources