GCr
GCr

Reputation: 25

Passing data from one Custom TableView Cell to another using delegates and protocols

I'm trying to wrap my head around delegates and protocols in swift, in particular how to pass data between custom tableview cells.

I've set up a simple project to test this out with three classes, a TableViewController and two custom cells using .xib for the layouts: DateLabelCell and DatePickerCell. The desired result being that when the datepicker is changed it updates the datelabel cell with the new value.

At the moment the DatePicker Cell is communicating with the TableViewController, but I cant seem to get the TableViewController to then communicate with the Datelabel Cell. I think it has something to do with the Datelabel Cell not referencing the tableviewcontroller correctly?

Any help or insights greatly appreciated.

Cheers,

TableViewController

    import UIKit

protocol DateLabelDelegate: AnyObject {
    func setDateLabel(_ text: String)
}


class TableViewController: UITableViewController {

    weak var dateLabelDelegate: DateLabelDelegate?

    override func viewDidLoad() {
        super.viewDidLoad()

        tableViewSetup()

    }

     func tableViewSetup() {

        // Format tableView
        tableView.rowHeight = UITableView.automaticDimension
        tableView.estimatedRowHeight = 200
        tableView.tableFooterView = UIView(frame: CGRect.zero) // Removes Empty Cells
        tableView.separatorStyle = UITableViewCell.SeparatorStyle.none

        // Register Cells
        tableView.register(UINib(nibName: "DatePickerCell", bundle: nil), forCellReuseIdentifier: "DatePickerCell")
        tableView.register(UINib(nibName: "DateLabelCell", bundle: nil), forCellReuseIdentifier: "DateLabelCell")

        }


    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        return 2
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        if indexPath.row == 0 {
             let cell = tableView.dequeueReusableCell(withIdentifier: "DateLabelCell", for: indexPath) as! DateLabelCell

             return cell
        } else {
              let cell = tableView.dequeueReusableCell(withIdentifier: "DatePickerCell", for: indexPath) as! DatePickerCell
                cell.dateDelegate = self
              return cell
        }
    }
}

extension TableViewController: DatePickerDelegate {
    func setDate(_ text: String) {
        print("WORKING: TableViewController: DatePickerDelegate")
        dateLabelDelegate?.setDateLabel(text)
    }


}

DatePickerCell

import UIKit

protocol DatePickerDelegate: AnyObject {
    func setDate(_ text: String)
}

class DatePickerCell: UITableViewCell {

    weak var dateDelegate: DatePickerDelegate?

    @IBOutlet var datePicker: UIDatePicker!
    let formatter = DateFormatter()

        override func awakeFromNib() {
            super.awakeFromNib()
             formatter.dateFormat = "dd.MM.yyyy"
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }

    override func prepareForReuse() {
    }

    @IBAction func datePickerDidChange(_ sender: Any) {
        let result = formatter.string(from: datePicker.date)
        print("RESULT -> \(result)")
        dateDelegate?.setDate(result)
    }

}

DateLabelCell

import UIKit

class DateLabelCell: UITableViewCell {

    @IBOutlet var dateLabel: UILabel!

    let date = Date()
    let formatter = DateFormatter()
    let sendingTableVC = TableViewController()



    override func awakeFromNib() {
        super.awakeFromNib()

        sendingTableVC.dateLabelDelegate = self

        formatter.dateFormat = "dd.MM.yyyy"

        let result = formatter.string(from: date)
        dateLabel.text = "Date: \(result)"

    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}

extension DateLabelCell: DateLabelDelegate {
    func setDateLabel(_ text: String) {
        print("DATE LABEL UPDATED")
        dateLabel.text = text
    }

}

Upvotes: 0

Views: 1280

Answers (2)

Starsky
Starsky

Reputation: 2038

You could also use callbacks, which are very powerful and somewhat easier to understand:

class TableViewController: UITableViewController {

    //MARK: Properties
    var date = "today"

    .....

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        if indexPath.row == 0 {
             let cell = tableView.dequeueReusableCell(withIdentifier: "DateLabelCell", for: indexPath) as! DateLabelCell
             cell.setDate(self.date)
             return cell
        } else {
             let cell = tableView.dequeueReusableCell(withIdentifier: "DatePickerCell", for: indexPath) as! DatePickerCell
             cell.dateSelectedCallback = { newDate in //here we get the new date
                 self.date = newDate
                 self.tableView.reloadData()
             }
             return cell
        }
    }
}

DateLabelCell

class DateLabelCell: UITableViewCell {

    //MARK: IBOutlets
    @IBOutlet var dateLabel: UILabel!

    //MARK: Cell setup
    func setDate(_ text: String) {
        dateLabel.text = text
    }
}

DatePickerCell

Here we create an optional callback variable. Why optional? In case it is not being implemented, it won't crash. Callbacks are like simple completion handlers, and you can pass any data through them.

class DatePickerCell: UITableViewCell {

    //MARK: IBOutlets
    @IBOutlet var datePicker: UIDatePicker!

    //MARK: Properties
    var dateSelectedCallback: ((_ date: String) -> ())?
    let formatter = DateFormatter()

    //MARK: Lifecycles
    override func awakeFromNib() {
        super.awakeFromNib()
        formatter.dateFormat = "dd.MM.yyyy"
    }

    //MARK: IBActions
    @IBAction func datePickerDidChange(_ sender: Any) {
        let result = formatter.string(from: datePicker.date)
        print("RESULT -> \(result)")
        dateSelectedCallback?(result) //we pass the date string here
    }
}

Upvotes: 0

Subramanian Mariappan
Subramanian Mariappan

Reputation: 3886

The proper way of updating a tableview/collectionviewcell is to reload the particular cell. And you should always maintain a data represent your table view cells since your cells are not persistent and re-usable. You can use that data to populate the properties of your cell when it is created.

In your case, date is the data needs to be persisted. In this scenario, you could simply have that as a field in your controller.

import UIKit

class DateLabelCell: UITableViewCell {

    @IBOutlet var dateLabel: UILabel!

    override func awakeFromNib() {
        super.awakeFromNib()
    }

    func setDate(_ text: String) {
        dateLabel.text = text
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }
}
class TableViewController: UITableViewController {
    var date: String = "Date"

    .......

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        if indexPath.row == 0 {
             let cell = tableView.dequeueReusableCell(withIdentifier: "DateLabelCell", for: indexPath) as! DateLabelCell
             cell.setDate(self.date)
             return cell
        } else {
              let cell = tableView.dequeueReusableCell(withIdentifier: "DatePickerCell", for: indexPath) as! DatePickerCell
                cell.dateDelegate = self
              return cell
        }
    }

}
extension TableViewController: DatePickerDelegate {
    func setDate(_ text: String) {
        self.date = text
        tableView.reloadRows(at: [IndexPath(item: 0, section: 0)], with: .automatic)
    }
}

Upvotes: 1

Related Questions