UIButton in TableViewCell segue not working

ISSUE HAS BEEN FIXED. SEE POST: https://forums.developer.apple.com/message/374921#374921


hello everyone, i'm new at coding and if possible i need help with my app. i'm trying to perform a segue from a button inside a custom cell to another VC. The button inside the cell is querying data from Firebase and grabbing the results from a query and using them to do a bigger query, then it needs to send the data in another VC. Every label within the cell has a value, i dont understand why the SEGUE is not working as excpected. Any help is greatly appreciated. Thank you!


**Cell Protocol**


protocol MyCellDelegate: class {
    func sendDataFromCell(_ sender: Any)
}


**CustomCell: ptlistCell**

import UIKit
import Firebase
import FirebaseFirestore

class ptlistCell: UITableViewCell {
    
    @IBOutlet private weak var ptnameLbl: UILabel!
    @IBOutlet private weak var assignedmdLbl: UILabel!
    @IBOutlet private weak var officemdLbl: UILabel!
    @IBOutlet private weak var roomnumberLbl: UILabel!
    @IBOutlet private weak var seenoseeLbl: UILabel!
    @IBOutlet private weak var assigneddateLbl: UILabel!
    
    @IBOutlet weak var ptdataBtn: UIButton!
   
    var ptdatainfoRef: CollectionReference!
    var query: Query?
    var patdata = [PTData]()
    var delegate: MyCellDelegate?
    
    override func awakeFromNib() {
        super.awakeFromNib()
        
        ptdatainfoRef = Firestore.firestore().collection(PTLIST_REF)
        
    }
    
    func configureCell(ptlist: PTList) {
        
        ptnameLbl.text = ptlist.ptname
        assignedmdLbl.text = ptlist.assignedmd
        officemdLbl.text = ptlist.officemd
        roomnumberLbl.text = ptlist.room
        seenoseeLbl.text = ptlist.seenosee
        assigneddateLbl.text = ptlist.assigneddate 
        
    }
    
    @IBAction func ptdataTapped(_ sender: Any) {
        
        let query = ptdatainfoRef.whereField("ptname", isEqualTo: (ptnameLbl.text!))
            .whereField("assignedmd", isEqualTo: (assignedmdLbl.text!))
            .whereField("officemd", isEqualTo: (officemdLbl.text!))
            .whereField("room", isEqualTo: (roomnumberLbl.text!))
            .whereField("seenosee", isEqualTo: (seenoseeLbl.text!))
            .whereField("assigneddate", isEqualTo: (assigneddateLbl.text!))
        
        query.getDocuments { (snapshot, error) in
            if let err = error {
                debugPrint("error getting data: \(err)")
            } else {
                guard let snap = snapshot else { return }
                for document in snap.documents {
                    let data = document.data()
                    let ptname = data[PTNAME] as? String ?? ""
                    let dob = data[DOB] as? String ?? ""
                    let fin = data[FIN] as? String ?? ""
                    let officemd = data[OFFICEMD] as? String ?? ""
                    let assignedmd = data[ASSIGNEDMD] as? String ?? ""
                    let assigneddate = data[ASSIGNEDDATE] as? String ?? ""
                    let room = data[ROOM] as? String ?? ""
                    let diagnoses = data[DIAGNOSES] as? String ?? ""
                    let reasonforadmitorconsult = data[REASONFORADMITORCONSULT] as? String ?? ""
                    let goalofhospitalization = data[GOALOFHOSPITALIZATION] as? String ?? ""
                    let seenosee = data[SEENOSEE] as? String ?? ""
                    let notestocbo = data[NOTESTOCBO] as? String ?? ""
                    let comments = data[COMMENTS] as? String ?? ""
                    let hosp = data[HOSP] as? String ?? ""
                    let team = data[TEAM] as? String ?? ""
                    
                    let newPtdata = PTData (ptname: ptname, dob: dob, fin: fin, officemd: officemd, assignedmd: assignedmd, assigneddate: assigneddate, room: room, diagnoses: diagnoses, reasonforadmitorconsult: reasonforadmitorconsult, goalofhospitalization: goalofhospitalization, seenosee: seenosee, notestocbo: notestocbo, comments: comments, hosp: hosp, team: team)
                    
                    self.patdata.append(newPtdata)
                    print(document.data())
                    
                };print(self.delegate)
                if(self.delegate != nil) {
                    self.delegate?.sendDataFromCell(sender)
                }
                
            }
        }
    }
    
}


**firstVC w customCell (ptlistcell)**


import UIKit
import Firebase

class resultsdataVC: UIViewController, UITableViewDataSource, UITableViewDelegate, MyCellDelegate {
    
    @IBOutlet weak var tableView: UITableView!
    
    var ptlist: PTList!
    var ptlists = [PTList]()
    var tableVc: patdataVC!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self
        tableView.dataSource = self
        
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return ptlists.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        if let cell = tableView.dequeueReusableCell(withIdentifier: "ptlistCell", for: indexPath) as? ptlistCell {
            cell.configureCell(ptlist: ptlists[indexPath.row])
            return cell
        }
        
        return UITableViewCell()
    }
    func sendDataFromCell(_ sender: Any) {
        performSegue(withIdentifier: "gotoptdata", sender: (Any).self)
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "gotoptdata" {
            tableVc = segue.destination as? patdataVC
            tableVc.tableViewCellDelegate = self as? MyCellDelegate
        }
    }
}



**secondVC w customCell (ptdataCell)**

import UIKit
import Firebase
import FirebaseFirestore

class patdataVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    var ptdata: PTData!
    var patdata = [PTData]()
    var tableViewCellDelegate: MyCellDelegate?
    
    @IBOutlet weak var tableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.delegate = self
        tableView.dataSource = self
        
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return patdata.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cellIdentifier = "ptlistCell"
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "ptdataCell", for: indexPath) as? ptlistCell else {
            fatalError("The dequeued cell is not an instance of TableViewCell.")
        }
        cell.delegate = tableViewCellDelegate
        return cell
    }
}




**CustomCell: ptdataCell**

import UIKit
import Firebase

class ptdataCell: UITableViewCell {
    
    @IBOutlet weak var ptnameLbl2: UILabel!
    @IBOutlet weak var dobLbl2: UILabel!
    @IBOutlet weak var finLbl2: UILabel!
    @IBOutlet weak var officemdLbl2: UILabel!
    @IBOutlet weak var assignedmdLbl2: UILabel!
    @IBOutlet weak var assigneddateLbl2: UILabel!
    @IBOutlet weak var roomLbl2: UILabel!
    @IBOutlet weak var diagnosesLbl2: UILabel!
    @IBOutlet weak var reasonforadmitorconsultLbl2: UILabel!
    @IBOutlet weak var goalofhospitalizationLbl2: UILabel!
    @IBOutlet weak var seenoseeLbl2: UILabel!
    @IBOutlet weak var notestocboLbl2: UILabel!
    @IBOutlet weak var commentsLbl2: UILabel!
    
    
    override func awakeFromNib() {
        super.awakeFromNib()
    }
    
    func configureCell(patdata: PTData) {
        
        ptnameLbl2.text = patdata.ptname
        dobLbl2.text = patdata.dob
        finLbl2.text = patdata.fin
        officemdLbl2.text = patdata.officemd
        assignedmdLbl2.text = patdata.assignedmd
        assigneddateLbl2.text = patdata.assigneddate
        roomLbl2.text = patdata.room
        diagnosesLbl2.text = patdata.diagnoses
        reasonforadmitorconsultLbl2.text = patdata.reasonforadmitorconsult
        goalofhospitalizationLbl2.text = patdata.goalofhospitalization
        seenoseeLbl2.text = patdata.seenosee
        notestocboLbl2.text = patdata.notestocbo
        commentsLbl2.text = patdata.comments
        
    }
    
}

Replies

Your code is hard to read, please follow two things to improve readability.

- Use Capitalized identifier for type names, which is a very common convention of Swift

- Use the code insertion feature of this site, shown with this icon `< >`(Thanks for updating!)


I have not checked your whole code, but this may be the most suspicious line in your code. (In class PatdataVC.)

        cell.delegate = self as? CellDelegate


Your PatdataVC does not conform `CellDelegate`, so `self as? CellDelegate` always generates `nil`.

Replace the line with:

        cell.delegate = self

This causes a compile-time error, but you need to fix it not by adding `as?`.

Your PatdataVC needs to conform to `CellDelegate`:

class PatdataVC: UIViewController, UITableViewDelegate, UITableViewDataSource,
CellDelegate {


(If capitalized `PatdataVC` has something bad for your code, please replace it.)

good afternoon, thank you so much for your reply. I went ahead and fixed the post with the code insertion. Also, i updated the PatdataVC code to reflect your suggestions, unfortunately the segue still not working. After pressing the button inside the cell, i get the query from Firebase in the console, but segue doesnt fire up. There are no errors, it's like xcode is not reading those lines. This is so weird. Again, i appreciate your help.


BTW. i did use caps with the class names, should i change them?

Could you add, before line 76,

print(self.delegate)

Hi Claude31, thank you for your help. Per your suggestion: print(self.delegate) = nil.

i was reading about the NIL delegate, and for an UIButton in a CustomCell the delegate needs to be weak and the VC is the one that executes the delegate (i hope i'm right). So i went ahead and changed the following:


**CustomCell: ptlistCell**

**patDataVC**



the code was updated above.


but, i'm still getting the same results. NIL delegate and no SEGUE.

Thank you

Hello, i have updated my code but still not able to segue to the other VC. Any help is greatly appreciated.

Why do you insert class in the protocol definition ?

protocol MyCellDelegate: class {

func sendDataFromCell(_ sender: Any)

}


Your code is awful to read, sorry. And I fear you have messed in delegates declarations.


You have 2 properties with delegate:

weak var delegate: MyCellDelegate?

weak var delegate: patdataVC?

It is so difficult to understand who you are speaking of.

Please change the namses, such as:

weak var cellDelegate: MyCellDelegate?

weak var patDataDelegate: PatdataVC? // Why this ? // Uppercase here


and post the modified classes.



It seems your class PatdataVC // note it should start with upperCase, for readability

does not conform to MyCellDelegate (or to PatdataVC) ?


So,

cell.delegate = self

is not correct


Should be:

class PatdataVC: UIViewController, UITableViewDelegate, UITableViewDataSource, MyCellDelegate {

hi,


i've struggled with this code since seeing it yesterday, hoping i'd see more before commenting. but now that we finally see the MainVC that was missing before, it's pretty clear what the immediate problem is: cellForRowAtIndexPath in the MainVC dequeues a ptlistCell, calls configure, and never sets its delegate.


but i have questions going forward:


(1) why is a button added in a cell to create a segue to another view controller? it's not a bad thing, perhaps, but would not a simple disclosureIndicator do the trick?


(2) why let a cell in one view controller do all the heavy database lifting and then call back to its view controller to push out a segue? wouldn't it be easier for the cell that is tapped to simply let the MainVC respond with one of the delegate methods didSelectRowAt or accessoryButtonTappendForRowWith and let the view controller do the heavy lifting of a database lookup and then push out the segue?


(3) your destinationVC dequeues tableview cells using the identifier "ptdataCell," but types it as a ptlistCell. not sure what that's about yet, but perhaps we'll find out if you get the segue working.


i'd also echo some of OOPer's and Claude31's suggestions regarding readability. e.g., the type PTList has no definition shown (we can infer what it is from its usage) and it's not really a "list," but rather a struct.


hope that helps push this discussion along,

DMG.

Hi Claude31, i made corrections to the code and has been updated.

Hopefully is better. I appreciate the comments, im new at coding and this helps me a lot. As per the name of the classes, is there a problem if i dont use the first letter in caps? im just asking before i start changing things. Thank you!

Hi DMG, thank you for your comments. Per your questions, here are my answers:


1) Please see the screenshots of the app (link below) I hope this helps understand why i added a button inside the cell. if there is simpler option, please let me know. https://drive.google.com/open?id=1phCHEhDrnvuNVphTyndkI_0zSFTEip6l


2) i did not think of that. I'm going to check.


3) i have to check this.


As per the readability, sorry, we use terms such as PT which refers to a patient. I did not know it was going to create any confusion.


Again, thank you for your comments. I just started learning coding, i hope to get better w time.