1
votes

I have been working on an app where I need to put a UICollectionView inside UITableViewCell. Each UICollectionViewCell has two buttons which I need to handle in my TableViewController class. So I added targets to those buttons to call a method in my TableViewCell class as I am setting each CollectionViewCell from there. Then, I have a custom delegate method that is called from that method and I have implemented that method in my TableViewController class. Everything works as expected unless I press the home button on my device and then return to the app. The method in the TableViewCell class is still being called but not the one in my TableViewController class.

This is my protocol and handler

protocol CollectionViewCellButtonPressHandlerDelegate {
    func handle(button: UIButton?, data: Any?)
}

class CollectionViewCellButtonPressHandler {
    var delegate: CollectionViewCellButtonPressHandlerDelegate?

    func handle(button: UIButton?, data: Any?) {
        if self.delegate != nil {
            delegate?.handle(button: button, data: data)
        }
    }
}

Apart from setting the TableViewCell, this is the code that is in my TableViewController's cellForRowAtIndexPath method

cell.handler = CollectionViewCellButtonPressHandler()
cell.handler.delegate = self
cell.goalCollectionView.reloadData()

This is my implementation of the delegate method in my TableViewController class.

func handle(button: UIButton?, data: Any?) {
        if let msg = data as? String {
            print(msg)
        }
}

This is how I have set the target to the button in cellForRowAtIndexPath method of my TableViewCell class

cell.playButton.addTarget(self, action: #selector(self.play(sender:)), for: .touchUpInside)

This is the implementation of the play button action in my TableViewCell class

func play(sender: UIButton) {
        for i in 0..<goals.count {
            let indexPath = IndexPath(row: i, section: 0)
            if let cell = self.goalCollectionView.cellForItem(at: indexPath) as? GoalCollectionViewCell {
                if sender == cell.playButton {
                    print("Play: ", self.goals[indexPath.row].subskill!)
                    self.handler.handle(button: sender, data: self.goals[indexPath.row])
                }
            }
        }
    }

The print statement above the delegate method is executed even after returning from background but the next statement is not. I have also checked for the handler and its delegate to be nil but they were not. Please help.

1
which one has conformed the protocol (CollectionViewCellButtonPressHandlerDelegate)? UITableViewCOntroller or UITableViewCell?Mina
TableViewController because that is where I need to handle the button press.Inder Deep Singh
does the handle function in CollectionViewCellButtonPressHandler is a random function which has the same name and structure as your delegate function func handle(button: UIButton?, data: Any?) ?Mina
No. The CollectionViewCellButtonPressHandler class is the one which is used to call the handle method with the appropriate arguments in the delegate class, which in this case, happens to be my TableViewController class. You can see the class in action in cellForRowAtIndexPath method of my TableViewController class.Inder Deep Singh
well, i have another solution for your case, which you can find as an answer. i hope it helps.Mina

1 Answers

1
votes

Swift 3: for passing an action of a button which is in a (collectionView or tableView)cell to the controller, as you know, you have to use delegate. if this button is in the cell which is in another (collectionView or tableView)cell you need one more delegate. and you have to pass data in this hierarchically. You can use this sample code, to pass the button action and data from collectionViewCell to tableViewCell and then to ViewController.

i checked this, it always works, even if the app moves to background and then foreground.

import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, TableViewCellPassPressActionToControllerDelegate {

    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        tableView.delegate = self
        tableView.dataSource = self
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell
        cell.delegate = self
        return cell
    }

    func passAction(button: UIButton?, data: Any?) {
        //you have the click of the button in collectionviewcell here
    }

}

class TableViewCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource, CollectionViewCellButtonPressHandlerDelegate {

    @IBOutlet weak var collectionView: UICollectionView!

    var delegate: TableViewCellPassPressActionToControllerDelegate?

    override func awakeFromNib() {
        collectionView.delegate = self
        collectionView.dataSource = self
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 1
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell
        cell.delegate = self
        return cell
    }

    func handle(button: UIButton?, data: Any?) {
        delegate?.passAction(button: button, data: data)
    }
}

class CollectionViewCell: UICollectionViewCell {
    var delegate: CollectionViewCellButtonPressHandlerDelegate?

    @IBAction func press(_ sender: UIButton) {
        delegate?.handle(button: sender, data: nil/*any data*/)
    }
}

protocol CollectionViewCellButtonPressHandlerDelegate {
    func handle(button: UIButton?, data: Any?)
}

protocol TableViewCellPassPressActionToControllerDelegate {
    func passAction(button: UIButton?, data: Any?)
}