Home > OS >  Accessing a UITableViewController delegate from a TableView header
Accessing a UITableViewController delegate from a TableView header

Time:09-17

I'm having an issue calling a segue from a TableView header that is associated with a programatically created TableView. As a stop-gap I have saved an object reference to the main UITableViewController using a Singleton but this is not the ideal solution.

My TableViewController has multiple sections with either 1 or 2 rows within each section depending on whether the top-level section row is selected. The second row is effectively used to expand on the content that is displayed on the selected row.

The second row contains a custom cell that contains a slider menu, and depending on the menu item selected, 1 of 5 subviews are displayed in the container view.

One of these subviews contains a programatically generated TableView with its own custom header and a custom cell. This cell contains a header and an embedded button. When the button is pressed in the header, I want to segue to another navigation controller, however, my problem is I cannot correctly initialise a delegate to access the main TableViewController.

It gets a bit more complex, as the custom cell creates its own TableView and handles its own functions for operations that are normally performed on the main controller using overrides e.g. didSelectRowAt, numberOfRowsInSelection, headerForRowAt, etc.

class pendingNotificationCustomCell: UITableViewCell, UITableViewDataSource, UITableViewDelegate {
    
    var dataSample:[String] = ["Row 1,"Row 2", "Row 3"]
    var PendingTableView: UITableView?
   
    required init(coder aDecoder: NSCoder) {
        
        fatalError("init(coder:) has not been implemented")
    }

    override func awakeFromNib() {

        print("##### awakeFromNib")
        super.awakeFromNib()
        // Initialization code
        setUpTable()
    }
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        
        super.init(style: style , reuseIdentifier: reuseIdentifier)
        setUpTable()
    }

    func setUpTable() {
        
        PendingTableView = UITableView(frame: CGRect.init(), style:UITableView.Style.plain)
        
        PendingTableView?.delegate = self
        PendingTableView?.dataSource = self
        
        PendingTableView?.register(MyCell.self, forCellReuseIdentifier: "pendingnotificationsCellID")
        
        PendingTableView?.register(PendingNotificationsHeader.self, forHeaderFooterViewReuseIdentifier: "pendingHeaderID")
        
        PendingTableView?.sectionHeaderHeight = 40
        
        PendingTableView?.allowsSelection = true
        
        PendingTableView?.allowsMultipleSelection = false
        
        self.addSubview(PendingTableView!)
    }

I can easily setup protocols and associated delegates to access functions on the controller that manages the main TableView custom cells. However, as the problematic custom cell does not inherit functions relating to performing segues I need to access the function on the main controller.

I've experimented quite a bit with this and haven't been able to come up with a viable solution other than the Singleton hack.

    let pendingCell = pendingNotificationCustomCell()
    
    pendingCell.delegate4 = mStats.mainController

When I try assigning delegate4 with an initialised outlet that references the main TableViewController it always has a value of 'nil' when it gets there. Even when I assign it with the Singleton value in the class the generates the second TableView. The commented out line fails whereas calling the method using the mStats Singleton works fine.

        //delegate4.callSegueFromCell2(myData: myData) 
        mStats.mainController?.callSegueFromCell2(myData: myData)

The delegate4 above, which is commented out, is set in the cell header classes as follows:

    protocol MyCustomCellDelegator3 {
    func callSegueFromCell2(myData dataobject: AnyObject)
}
class PendingNotificationsHeader: UITableViewHeaderFooterView {
    
    var delegate4:MyCustomCellDelegator3!
    
    var MainTableViewController: MainTableViewController?
    
    override init(reuseIdentifier: String?) {
        
        super.init(reuseIdentifier: reuseIdentifier)
        setupViews()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    let nameLabel: UILabel = {
        let label = UILabel()
        label.text = "Pending"
        label.font = UIFont.systemFont(ofSize: 17)
        label.textColor = .white
        return label
    }()
    
    let priceAlertButton: UIButton = {
        let button = UIButton(type: .system)
        button.setTitle("Add new", for: .normal)
        button.titleLabel?.font = UIFont (name: "Helvetica", size: 15)
        button.setTitleColor(.white, for: .normal)
        button.addTarget(self,action: #selector(createNewPriceAlertButtonPressed), for: .touchUpInside)
        return button
    }()
    
    @IBAction func createNewPriceAlertButtonPressed(_ sender: Any) {
        print ("##### New Price Alert Label Pressed")
        // Get the view
        
        var mydata = self

        //delegate4.callSegueFromCell2(myData: myData)
        mStats.mainController?.callSegueFromCell2(myData: myData)
    }

Appreciate any guidance.

CodePudding user response:

let pendingCell = pendingNotificationCustomCell()

looks odd.

You should set up your cells in tableView(cellForRow:at:) UITableViewDataSource method.

CodePudding user response:

The issue related to setting the delegate on the cell and not passing it on to the section header. I ended up solving the issue as follows:

I implemented a function and delegate in the MainViewController called API_Segue_01

final class MainTableViewController: UITableViewController, API_Segue_01 {


    func API_Segue_01(myData dataobject: AnyObject) {
        navigationItem.backBarButtonItem = UIBarButtonItem(title: "Back", style: .plain, target: nil, action: nil)
        self.performSegue(withIdentifier: "showCreateNotificationsViewController", sender:dataobject )
    }
    /// Rest of class code
    /// ...

In the custom cell I created a header prototype to reference the external function:

protocol API_Segue_01 {
    func API_Segue_01(myData dataobject: AnyObject)
}

I defined a var to hold the reference to the MainTableViewController in the custom cell and associated header

class pendingNotificationCustomCell: UITableViewCell, UITableViewDataSource, UITableViewDelegate {

    var funcCallTo: API_Segue_01!
    /// Rest of class code
    /// ...
    
class PendingNotificationsHeader: UITableViewHeaderFooterView {
    
    var funcCallTo:API_Segue_01!
    /// Rest of class code
    /// ...

When creating an instance of the custom cell I provided a reference to the MainTableViewController

    pendingCell.funcCallTo = mainTableViewController

As the action button is located in the cell header I passed the reference to the mainTableViewController within the viewForHeaderInSection as follows

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    
    print("##### viewForHeaderInSection")
    
    let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "pendingHeaderID") as! PendingNotificationsHeader
    
    header.funcCallTo = self.funcCallTo
    
    return header
}

I was then able to use this reference to call the API_Segue_01 function

@IBAction func createNewPriceAlertButtonPressed(_ sender: Any) {
    print ("##### New Price Alert Label Pressed")
    // Get the view
    
    var mydata = self /

    funcCallTo.API_Segue_01(myData: mydata)
}
  • Related