Robert Garcia
Robert Garcia

Reputation: 43

Dealing with dynamic cells in a UITableView?

New Error Message

Error Message.

Now, I'm getting this error message.

Updated Code with error message shown in this image link error message:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = runReportTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = valuesArray[indexPath.row]
cell.SetCheckMark(cell.checkMark) //Call 'SetCheckMark' function here
cell.tapButton = {
    if cell.checkMark.isSelected == false {
        
        let data:[String:String] = [self.Facilities[indexPath.row]: "Disinfected"]
        self.InfoArray.append(data)
    }
    else {
        self.InfoArray.remove(at: indexPath.row)
    }
}

print("Responsibilities in cells: \(valuesArray)")
print("\(data)")
return cell
}

I have this code working all the way up to "print("You tapped cell #(indexPath.row)")." This code produces this firebase document with some fields: this document should have fields of the room/area text and values of "Disinfected" or "Not Disinfected" depending on whether the user selected that cell or left it unselected

So, all I need this code to do now is update my Cloud Firestore document with the text (valuesArray[indexPath.row]) of the cells that were selected by the users. I had this working perfectly before using a static number of buttons and labels on my viewController and had my desired result in my firebase Firestore document like this: screenshot of when I had static information that I was updating in my firebase database after the user made all selections and tapped the "send report" button

However, now, instead of displaying a constant "Room 1"-"Room 21" keys in the firebase document with the selected value of "Disinfected" or "Not Disinfected" I am wondering how I can have my code automatically write to that firebase document "Disinfected" if the cell is selected/highlighted or "Not Disinfected" if the user does not select the cell and, then display which cell had that value of disinfected/notdisinfected using the cell's label text which I am getting from the data in the "Firestore Document Data Screenshot" down below.

I think its because I'm not 100% sure how to check if the dynamic cells I created are selected or not and then assign either "Disinfected" or "Not Disinfected" to that selected cell's text in my Cloud Firestore document.

As aforementioned, this code prints the correct number of the tapped cell and the correct label text of the tapped cell. It also successfully creates a new Cloud Firestore document without the values ("Disinfected" or "Not Disinfected") of the selected buttons and their matching cell label text.

Here's a screenshot of the simulator Simulator Screenshot and a screenshot of the Cloud Firestore document data that I'm using for my cells' label text Firestore Document Data Screenshot .

import UIKit
import Firebase
import FirebaseFirestore
import SCLAlertView


class RunReportViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {


@IBOutlet weak var runReportTableView: UITableView!

    

var namesDocumentRef:DocumentReference!
var userName = ""
var userEmail = ""
var data:[String] = []
var valuesArray:[String] = []
var selectedResponsibility:[String] = []
//    var keysArray:[String] = []


override func viewDidLoad() {
    super.viewDidLoad()
    
    startObservingDB()
    
    runReportTableView.delegate = self
    runReportTableView.dataSource = self
    // Do any additional setup after loading the view.
}



// Gets user's specific room(s)/area(s)/classroom(s) responsibilities from Cloud Firestore Database to be used for checking "Disinfected" or "Not Disinfected" in order to put the text as a cell label
func startObservingDB() {
    var responsibilitiesDocumentRef:DocumentReference!
    let db = Firestore.firestore()
    let userID = Auth.auth().currentUser!.uid
    responsibilitiesDocumentRef = db.collection("UserResponsibilities").document("McGrath").collection("Custodians").document("\(userID)")
    responsibilitiesDocumentRef.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>
//                self.keysArray = Array(data.keys)
            
            self.runReportTableView.reloadData()
            print("Current data: \(data)")
            print("Current data has the responsibilities: \(self.valuesArray)")
            print("Current data totals \(self.valuesArray.count) items.")
        }
    }
}



@IBAction func sendReportTapped(_ sender: Any) {
    getSelectionValues()
}

func getSelectionValues() {

    let db = Firestore.firestore()
    let userID = Auth.auth().currentUser!.uid
    
    db.collection("Users").document("\(userID)").collection("UserInfo").getDocuments { (snapshot, err) in
        if let err = err {
            print("Error getting documents: \(err)")
        } else {
            for document in snapshot!.documents {
                let docID = document.documentID
                self.userName = document.get("Name") as! String
                self.userEmail = document.get("Email") as! String
                
        
                print("Current document is: \(docID)")
                print("Current user's name: \(self.userName)")
            }
    

            db.collection("Run Reports").document("Custodians").collection("Custodians").document("\(String(describing: userID))").collection("Run Reports").document("\(self.getCurrentShortDate())").setData ([
            "Name": "\(String(describing: self.userName))",
            "Email": "\(String(describing: self.userEmail))",
            "Admin": Bool(false),
            "Last Updated": FieldValue.serverTimestamp(),
            ])
        }
        
        
        // getting values of selection code:
            func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
                print("The cell you tapped has the text: \(self.valuesArray[indexPath.row])")
//                    let selectedResponsibility = "\(self.valuesArray[indexPath.row])"
                
                print("You tapped cell #\(indexPath.row)")
                
                

This is where the issue is at. This code is just me experimenting, although the code where I update the firebase document works--"Set the status of Responsibility 1" works. Its just not working for the information I have above it about the dynamic cell which is all experimental and wrong since doesnt work:

//                let currentUsersCellCount = self.valuesArray.count
                     let cell = self.runReportTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
                let dynamicCell = self.runReportTableView.cellForRow(at: indexPath)
                
                if dynamicCell?.isSelected == true {
                        let status = "Disinfected"
                    let DocumentRef = db.collection("Run Reports").document("Custodians").collection("Custodians").document("\(String(describing: userID))").collection("Run Reports").document("\(self.getCurrentShortDate())")

                        // Set the status of Responsibility 1
                        DocumentRef.updateData(["\(self.valuesArray[indexPath.row])" : "\(status)"])
        }

                    else if dynamicCell?.isSelected == false {
                        let status = "Not Disinfected"
                    let DocumentRef = db.collection("Run Reports").document("Custodians").collection("Custodians").document("\(String(describing: userID))").collection("Run Reports").document("\(self.getCurrentShortDate())")

                        // Set the status of Responsibility 1
                        DocumentRef.updateData(["\(self.valuesArray[indexPath.row])" : "\(status)"])
        }
        
 

This all works perfectly fine:

    // Setup action for when "Send Report" and alert buttons are tapped
    let appearance = SCLAlertView.SCLAppearance(
                // Hide default button???
                showCloseButton: false
            )
            
    // Create alert with appearance
    let alert = SCLAlertView(appearance: appearance)
    alert.addButton("Done", action: {
        
        // Show SendReportViewController after successfully sent report and alert button is tapped
                        let storyboard = UIStoryboard(name: "Main", bundle: nil)
                        let vc = storyboard.instantiateViewController(withIdentifier: "SendReportViewController")
                        vc.modalPresentationStyle = .overFullScreen
                        self.present(vc, animated: true)
                         // create button on alert
                        print("'Done' button was tapped.")
    })
            
    alert.showSuccess("Report Sent!", subTitle: "Your Run Report has been sent to your supervisor.", closeButtonTitle: "Done", timeout: nil, colorStyle: SCLAlertViewStyle.success.defaultColorInt, colorTextButton: 0xFFFFFF, circleIconImage: nil, animationStyle: .topToBottom)
    
    }
}
        
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    print("The cell you tapped has the text: \(valuesArray[indexPath.row])")
//    let selectedResponsibility = "\(valuesArray[indexPath.row])"


print("You tapped cell #\(indexPath.row)")
}


func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return valuesArray.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = runReportTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = valuesArray[indexPath.row]


print("Responsibilities in cells: \(valuesArray)")
print("\(data)")

return cell
}

   // using date to create new firestore document with date as the title
func getCurrentShortDate() -> String {
    let todaysDate = NSDate()
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "(MM-dd-yy)"
    let DateInFormat = dateFormatter.string(from: todaysDate as Date)
    return DateInFormat
}

}

Upvotes: 0

Views: 462

Answers (1)

Coder
Coder

Reputation: 553

If you want to store the information about the cell you have selected in fireStore document as Text of the cell : Disinfected or Not Disinfected, i.e ConferenceRoom: Disinfected, then this is what I would suggest.

Firstly, inside the tableViewCell file, add following properties

var tapButton: (() -> Void)? = nil //This will allow you to get the text of the row you have selected

Then, I suppose you have a button inside your cell which in your case is on left side and it can be tapped or untapped. Let call that button name as checkMark so it will be declared inside your tableviewCell file like so.

@IBOutlet weak var checkMark: UIButton!

Now, add a function SetCheckMark so user can see wether a cell is checked or unchecked. Also, you would need two images which would be assigned to button in checked or unchecked state.

@IBAction func SetCheckMark(_ sender: UIButton) {
    //If button is selected, then
    if checkMark.isSelected == true {
               checkMark.setImage(UIImage(named: "Check"), for: .normal)
               checkMark.isSelected = false


           }
           else {
               checkMark.setImage(UIImage(named: "UnCheck"), for: .normal)
                checkMark.isSelected = true

           }
   
    tapButton?() //This is the property that we declared above.
}

Now, you are almost done. Come inside your Main file. Firstly, add a dictionary at the start of File to save information.

var InfoArray:[[String:String]] = [["":""]]

Now, inside your tableView cellForRowAt indexPath: IndexPath function, add these lines.

 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
 let cell = runReportTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)

    cell.label.text = valuesArray[indexPath.row]
    cell.SetCheckMark(cell.checkMark) //Call `SetCheckmark` function here
    cell.tapButton = {
        if cell.checkMark.isSelected == false {
            
            let data:[String:String] = [self.Facilities[indexPath.row]: "Disinfected"]
            self.InfoArray.append(data)
        }
        else {
            self.InfoArray.remove(at: indexPath.row)
        }
           }
    
    
    return cell
}

After the user is done selecting the options, it will tap the sendReport button. Then, inside that button you can post the data to firestore document like this.

db.collection("CollectionName").document("DocumentName").setData(["Facilities" : self.InfoArray])

Your tableViewCell file should look something like this.

class YourTableViewCell: UITableViewCell {

@IBOutlet weak var checkMark: UIButton!

var tapButton: (() -> Void)? = nil


@IBAction func SetCheckMark(_ sender: UIButton) {
    if checkMark.isSelected == true {
               checkMark.setImage(UIImage(named: "Checkbox"), for: .normal)
               checkMark.isSelected = false
           }
           else {
               checkMark.setImage(UIImage(named: "UnCheckbox"), for: .normal)
                checkMark.isSelected = true

                }
    
       tapButton?()
    }
 }

It should work. If you get any problem, do let me know instantly.

Upvotes: 1

Related Questions