Getting data from the selected cell (TableView) - swift

I have a TableView with several cells of data and there are 3 labels in each cell.
How can I save all 3 label.text into another variable with indexPath
let indexPath = self.tableView.indexPathForSelectedRow
Here is the full code
I've actually asked in another post that the variable "limit" becomes null after the .observe thing.
So I'm thinking if I can get the data directly from the cell.
import UIKit
import Firebase
import FirebaseDatabase
struct limitStruct{
var name : String!
var today : String!
var limit : String!
}
class CalcViewController: UITableViewController {
var limits = [limitStruct]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
navigationController?.navigationBar.barTintColor = UIColor.black
self.title = "Calculation"
navigationController!.navigationBar.titleTextAttributes =
[NSForegroundColorAttributeName: UIColor.white]
let databaseReference = FIRDatabase.database().reference()
databaseReference.child("Limit").queryOrderedByKey().observe(.childAdded, with: {
snapshot in
var snapshotValue = snapshot.value as? NSDictionary
let name = snapshotValue!["name"] as? String
snapshotValue = snapshot.value as? NSDictionary
let today = snapshotValue!["today"] as? String
snapshotValue = snapshot.value as? NSDictionary
let limit = snapshotValue!["limit"] as? String
snapshotValue = snapshot.value as? NSDictionary
self.limits.insert(limitStruct(name:name, today:today, limit: limit), at: self.limits.count)
self.tableView.reloadData()
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return limits.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Limit")
let label1 = cell?.viewWithTag(1) as! UILabel
label1.text = limits[indexPath.row].name
let label2 = cell?.viewWithTag(2) as! UILabel
label2.text = limits[indexPath.row].today
let label3 = cell?.viewWithTag(3) as! UILabel
label3.text = limits[indexPath.row].limit
return cell!
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetails"{
let svc = segue.destination as! CirSliderViewController;
if let indexPath = self.tableView.indexPathForSelectedRow{
// svc.RsegueData =
}
}
}
}

You really don't want to be using viewWithTag(). The best way to handle this is to subclass UITableViewCell, with a property for your data model object
class LimitCell: UITableViewCell {
var limit: Limit {
didSet {
// configureCell()
}
}
}
Then in your view controller:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Limit", forIndex: indexPath) as! LimitCell
cell.limit = limits[indexPath.row]
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let svc = segue.destination as? CirSliderViewController, cell = sender as? LimitCell {
svc.RsegueData = cell.limit
}
}

It seems that you are using a callback to get the data. Does the data come from a server or is stored locally?
1) If the data comes from a server, you code cannot guarantee that var limits already got the data when the func prepare is called.
2) If the data is stored locally, and ONLY limitis nil, you must check whether or not you correctly assign limits[indexPath.row].limit to limits to the cell.(Is it nil at this moment?) I think the problem is in func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) where limit is saved.
By the way, the more practical and efficient way to implement func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) is that:
Lets say the your custom cell is call LimitCell and has three UILabels: var label1, var label2, var label3.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Limit") as! LimitCell
cell.label1.text = limits[indexPath.row].name
cell.label2.text = limits[indexPath.row].today
cell.label3.text = limits[indexPath.row].limit
return cell
}

Related

Hide cell from UITableView

I'm trying to hide cells from a UITableView. My codes are below.
When I open the app I see empty rows in my TableViewas you can see here
How can I hide or remove(not delete) empty cells from UITableView?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FeedTableViewCell
let row = self.items[indexPath.row]
cell.lblTitle.text = row.title
cell.isHidden = !checkCurrentUser(email: row.email)
return cell
}
I added filtered array but then I take different error like this. My new codes are below. How can I solve this problem?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FeedTableViewCell
let row = self.items[indexPath.row]
self.items = self.items.filter{checkCurrentUser(email: $0.email)}
cell.lblTitle.text = row.title
//cell.isHidden = !checkCurrentUser(email: row.email)
return cell
}
Whole codes are below
import UIKit
import Firebase
class OyuncularVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
var items = [ItemModel]()
#IBOutlet weak var tblView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tblView.tableFooterView = UITableViewHeaderFooterView()
retrieveItems()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FeedTableViewCell
let row = self.items[indexPath.row]
self.items = self.items.filter{checkCurrentUser(email: $0.email)} //bunu ekledim siliceksem bunu silicem aga
cell.lblTitle.text = row.title
//cell.isHidden = !checkCurrentUser(email: row.email)
return cell
}
/* Retriev Items */
func retrieveItems() {
DataService.dataService.ITEM_REF.observe(.value, with: { (snapshot: DataSnapshot?) in
if let snapshots = snapshot?.children.allObjects as? [DataSnapshot] {
self.items.removeAll()
print(snapshots.count)
for snap in snapshots {
if let postDic = snap.value as? Dictionary<String, AnyObject> {
let itemModel = ItemModel(key: snap.key, dictionary: postDic)
print(itemModel)
self.items.insert(itemModel, at: 0)
}
}
self.tblView.reloadData()
}
})
}
func checkCurrentUser(email: String) -> Bool {
let currentUser = Auth.auth().currentUser
return email == currentUser?.email
}
}
}
If you want to display only the emails of the current user what don't you filter the items in the database (applying a predicate) which is the most efficient way.
Or filter the items in the for snap in snapshots loop.
However if you want to keep the entire data set declare a second array
var items = [ItemModel]()
var filteredItems = [ItemModel]()
and replace
for snap in snapshots {
if let postDic = snap.value as? Dictionary<String, AnyObject> {
let itemModel = ItemModel(key: snap.key, dictionary: postDic)
print(itemModel)
self.items.insert(itemModel, at: 0)
}
}
with the following it performs the check in the loop
let currentUser = Auth.auth().currentUser
self.filteredItems.removeAll()
for snap in snapshots {
if let postDic = snap.value as? Dictionary<String, AnyObject> {
let itemModel = ItemModel(key: snap.key, dictionary: postDic)
print(itemModel)
self.items.insert(itemModel, at: 0)
if itemModel.email == currentUser?.email {
self.filteredItems.insert(itemModel, at: 0)
}
}
}
And replace also the two data source methods with
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FeedTableViewCell
let row = self.filteredItems[indexPath.row]
cell.lblTitle.text = row.title
return cell
}
And delete the method checkCurrentUser

Displaying Firebase data in a tableview

I am trying to retrieve information stored in a Firebase Database, but my tableview is not displaying the information. I have tried using print functions to see if the data is being retrieved, and it shows that this is the case, but the tableview shows up as blank when I run the simulator.
I am using Xcode 11 and every tutorial that I have looked at is not working for some reason.
Here is my code:
import UIKit
import Firebase
import FirebaseDatabase
import SwiftKeychainWrapper
import FirebaseAuth
class FeedVC: UITableViewController {
var currentUserImageUrl: String!
var posts = [postStruct]()
var selectedPost: Post!
override func viewDidLoad() {
super.viewDidLoad()
getUsersData()
getPosts()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func getUsersData(){
guard let userID = Auth.auth().currentUser?.uid else { return }
Database.database().reference().child("users").child(userID).observeSingleEvent(of: .value) { (snapshot) in
if let postDict = snapshot.value as? [String : AnyObject] {
self.tableView.reloadData()
}
}
}
struct postStruct {
let firstName : String!
let lastName : String!
}
func getPosts() {
let databaseRef = Database.database().reference()
databaseRef.child("profiles").queryOrderedByKey().observeSingleEvent(of: .childAdded, with: {
snapshot in
let firstName = (snapshot.value as? NSDictionary)!["profileForename"] as? String
let lastName = (snapshot.value as? NSDictionary
)!["profileSurname"] as? String
print(firstName)
self.posts.append(postStruct(firstName: firstName, lastName: lastName))
print(self.posts)
DispatchQueue.main.async {
self.tableView.reloadData()
}
})
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell") as? PostCell else { return UITableViewCell() }
let nameLabel = cell.viewWithTag(1) as! UILabel
nameLabel.text = posts[indexPath.row].firstName
return cell
}
}
Any help would be much appreciated!
Update: Since PostCell is created in the storyboard within the table view it's registered and dequeued successfully. So the issue is being narrowed down to the label with tag 1. Try creating an #IBOutlet for the label and use that to set the text of UILabel.
Then in cellForRowAt:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell") as? PostCell else { return UITableViewCell() }
cell.firstNameLabel.text = posts[indexPath.row].firstName
return cell
}
Previous: You seem to have forgotten to register the PostCell.
override func viewDidLoad() {
super.viewDidLoad()
//...
tableView.register(PostCell.self, forCellReuseIdentifier: "PostCell")
}
Note: If you've created PostCell in Xib use nib registry method.
Update: If you want to register with Nib method use:
tableView.register(UINib(nibName: <#T##String#>, bundle: nil), forCellReuseIdentifier: "PostCell") // provide the xib file name at the placeholder

Firebase tableview not populating, Swift

I have data in my db and can search for an individual record, that's working fine. But when I try to simply populate a tableview with all of the db records its not receiving/displaying any data.
here is my code:
struct drinkStruct {
let pub: String!
let rating: String!
let price: String!
}
override func viewDidLoad() {
super.viewDidLoad()
loadDrinks()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func homeClicked(_ sender: Any) {
homeClicked()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let label1 = cell.viewWithTag(1) as! UILabel
label1.text = posts[indexPath.row].pub
let label2 = cell.viewWithTag(2) as! UILabel
label2.text = posts[indexPath.row].rating
let label3 = cell.viewWithTag(3) as! UILabel
label3.text = posts[indexPath.row].price
return cell
}
func loadDrinks(){
let databaseRef = Database.database().reference().child("Drinks")
ref = Database.database().reference()
databaseRef.queryOrderedByKey().observe(.childAdded, with: { (snapshot) in
if let valueDictionary = snapshot.value as? [AnyHashable:String]
{
let pub = valueDictionary["pub"]
let rating = valueDictionary["rating"]
let price = valueDictionary["price"]
self.posts.insert(drinkStruct(pub: pub, rating: rating, price: price), at: 0)
}
})
self.tableview.reloadData()
}
And here is my db structure:
Am I doing something blatantly obviously wrong? Or can anyone see what's causing no data to load?
There are no errors/unused variables etc etc.
Thanks in advance!
I think the following should do the job.
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
//getting a reference to the node //
databaseRef = Database.database().reference().child("Drinks")
//observing the data changes
databaseRef.observe(DataEventType.value, with: { (snapshot) in
if snapshot.childrenCount > 0 {
// clearing the list //
self.posts.removeAll()
// iterating through all the values //
for drinks in snapshot.children.allObjects as! [DataSnapshot] {
let drinkObject = drinks.value as! [String: AnyObject]
let drinkPub = drinkObject["pub"]
let drinkRating = drinkObject["rating"]
let drinkPrice = drinkObject["price"]
//creating a drinkStruct object with the model //
let drinkModel = drinkStruct(pub: drinkPub as! String?, rating: drinkRating as! String?, price: drinkPrice as! String?)
//appending it to list
self.posts.append(drinkModel)
}
// reloading data //
self.tableView.reloadData()
}
})
}
var posts = [drinkStruct]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! YourCustomTableViewCell
let drink: drinkStruct
drink = posts[indexPath.row]
cell.label1.text = drink.pub
cell.label2.text = drink.rating
cell.label3.text = drink.price
return cell
}
}
For the newbie that's here in my footsteps, I solved this by doing a lot of things.
You need to create the tableview & cell layout in the storyboard. Then you need a cell class that dictates/assigns what's happening in each cell(imageviews, labels etc) as well as a model class for whatever you're looking up, whatever the object may be.
This is the code I used for my function in which I populate the info in the cells with the data from Firebase:
func loadDrinks(){
Database.database().reference().child("Drinks").observe(.childAdded) { (snapshot: DataSnapshot) in
if let dict = snapshot.value as? [String: Any] {
let pub = dict["pub"] as! String
let rating = dict["rating"] as! String
let price = dict["price"] as! String
let drink = Drink(pub: pub.capitalized, rating: rating.capitalized, price: price.capitalized)
self.drinks.append(drink)
print(self.drinks)
self.tableview.reloadData()
}
}
}
This was a Newbie 101 question - my bad.

Passing selected cell's label text, return nil when first time (delay in data sent)

I'm passing the selected cell's label text to another view controller but It will return nil when every first time I select. After that I go back and select again, I will get the previous selected cell's text.Why?
var jobDateValueB:String!
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return jobTime.count //JSON Data from server
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "jobCell", for: indexPath)
let unixTimestamp = Double(jobTime[indexPath.row])
let unixTimestamp2 = Double(jobEndTime[indexPath.row])
let date1 = Date(timeIntervalSince1970: unixTimestamp)
let date2 = Date(timeIntervalSince1970: unixTimestamp2)
dateFormatter.dateFormat = "h:mm a"
let strDate = dateFormatter.string(from: date1)
let endDate = dateFormatter.string(from: date2)
cell.textLabel?.text = "\(strDate) - \(endDate)"
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showEdit"{
let destinationB = segue.destination as? EditTimeTableVC
destinationB?.passedDataB = jobDateValueB
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "showEdit", sender: self)
let indexPath = self.tableView.indexPathForSelectedRow;
let currentCell = self.tableView.cellForRow(at: indexPath!) as UITableViewCell!;
jobDateValueB = currentCell?.textLabel!.text!
}
EditTimeTableVC
var passedDataB: String!
override func viewDidLoad() {
super.viewDidLoad()
print(passedDataB)
}
In didSelectRowAt method you are calling self.performSegue first and then setting jobDateValueB. Try moving self.performSegue call to end of the function.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let indexPath = self.tableView.indexPathForSelectedRow;
let currentCell = self.tableView.cellForRow(at: indexPath!) as UITableViewCell!;
jobDateValueB = currentCell?.textLabel!.text!
self.performSegue(withIdentifier: "showEdit", sender: self)
}
This should solve your problem but it's not the recommended way. Instead of assigning the selected text value to a class variable you pass it as sender. like this.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let indexPath = self.tableView.indexPathForSelectedRow;
let currentCell = self.tableView.cellForRow(at: indexPath!) as UITableViewCell!;
if let value = currentCell?.textLabel?.text? {
self.performSegue(withIdentifier: "showEdit", sender: value)
}
}
And in your prepare method you can do.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showEdit"{
let destinationB = segue.destination as? EditTimeTableVC
if let selectedText = sender as? String {
destinationB?.passedDataB = selectedText
}
}
}

sending customCell label text to destination view

I am stuck on this problem. I am downloading a json file from Firebase and successfully displaying in a TableView. That TableView has a custom cell with two labels. I have set up a segue link from the custom cell to a Detail ViewController. That works fine, but now I want to take the text content of the cell labels and send to a destination ViewController.
I have had no trouble doing this in the past, but am having trouble implementing this from the label.text in a custom cell.
I am using label tags (label1 and label2, although I may change that to the label names sometime).
The question is, how do I get the text contents from the two labels from the row selected and pass those to the DetailViewController? All my attempts so far have failed. Is there something I should be doing in the:
valueToPass = currentCell?.textLabel?.text
Here is the code:
struct postStruct {
let property : String!
let propType : String!
}
class TableViewController: UITableViewController {
var posts = [postStruct]()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
let databaseRef = FIRDatabase.database().reference()
databaseRef.child("users").queryOrderedByKey().observe(.childAdded, with: {
snapshot in
let snapshotValue = snapshot.value as? NSDictionary
let property = snapshotValue!["property"] as? String
let propType = snapshotValue!["type"] as? String
self.posts.insert(postStruct(property: property, propType: propType), at: 0)
self.tableView.reloadData()
})
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
let label1 = cell?.viewWithTag(1) as! UILabel
label1.text = posts[indexPath.row].property
let label2 = cell?.viewWithTag(2) as! UILabel
label2.text = posts[indexPath.row].propType
// print(posts)
// cell.setcell(valueToPass.label1)
return cell!
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
var valueToPass:String!
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow;
let currentCell = tableView.cellForRow(at: indexPath!) as UITableViewCell!;
valueToPass = currentCell?.textLabel?.text
performSegue(withIdentifier: "detailSegue", sender: self)
}
Any help most welcome, in particular code examples. Many thanks in anticipation
Create a custom class for your tableCell, and attach outlets in the StoryBoard.
the cell creation in cellForRowAt becomes
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") As! MyCustomCell
cell.label1.text = posts[indexPath.row].property
cell.label2.text = posts[indexPath.row].propType
and then didSelectRowAtIndexPath becomes
let indexPath = tableView.indexPathForSelectedRow
let currentCell = tableView.cellForRow(at: indexPath!) as! MyCustomCell
self.valueToPass = currentCell.label1?.text
performSegue(withIdentifier: "detailSegue", sender: self)
The final thing you need if to prepare for the segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.identifier == "detailSegue" // you may have more than one segue, so you should check
{
let destinationController : DestinationViewControllerClass = segue.destination as! DestinationViewControllerClass
destinationController.valueToPass = self.valueToPass
}
}