0

I have a tableView. I register the Cell as follow:

tableView.register(TVCellElementProperties.self, forCellReuseIdentifier: cellId1)

Inside TVCellElementProperties, I created manually a segment controller as follow:

    let unitTypeSegmentedControl: UISegmentedControl = {
    let types = ["Blue", "White"]
    let sc = UISegmentedControl(items: types)
    sc.selectedSegmentIndex = 0
    sc.translatesAutoresizingMaskIntoConstraints = false
    sc.tintColor = UIColor.darkBlue
    sc.addTarget(self, action: #selector(handleUnitChange), for: .valueChanged)

    return sc
}()

@objc func handleUnitChange() {
    NotificationCenter.default.post(name: .unitPicked, object: self)
}

So, I think when I change the value inside the SegmentController, it should redirect me to the function handleUnitChange()

inside the tableView, I inserted this line into the ViewDidLoad:

NotificationCenter.default.addObserver(self, selector: #selector(unitPicked), name: .unitPicked, object: nil)

When I run the application, the function ** handleUnitChange** inside tableviewCell is not called. What I did wrong? how do I know what I clicked ?

EDIT: I am calling a setupView Function which responsible for insert the Segment Controller inside the Cell from init inside the UITableViewCell as follow:

override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: style, reuseIdentifier: reuseIdentifier)
    self.setupViews()
    print("test init")
}

So, when i run it, setupView is called just once, thus ** handleUnitChange** is called too just once. I mean to say, when the application is up and running, and when I click on the segment controller inside TableView, the function handleUnitChange is not called anymore.

I tried to call the function from the Cell inside CellForRow, but same as above. the function ** handleUnitChange** is not called overtime I clicked inside the Segment Controller.

if let cell = tableView.dequeueReusableCell(withIdentifier: cellId1, for: indexPath) as? TVCellElementProperties {

    //cell.backgroundColor = UIColor.rgb(red: 12, green: 122, blue: 12)
    //cell.contentView.isUserInteractionEnabled = false
    cell.setupViews()
    //print("\(cell.handleUnitChange(sender: u))")
    cell.handleUnitChange(sender: cell.unitTypeSegmentedControl)
    return cell
}
Jay Patel
  • 2,642
  • 2
  • 18
  • 40
Xin Lok
  • 496
  • 1
  • 6
  • 21
  • 1
    @objc func handleUnitChange(sender: UISegmentedControl) { print(sender.selectedSegmentIndex) } – El Tomato Feb 20 '18 at 14:15
  • 1
    You don't have to put anything in ViewDidLoad. You may have to trigger the action with the closure inside the tableViewCell, though. – El Tomato Feb 20 '18 at 14:16
  • Can you post the code that you do in your cell? – Agent Smith Feb 20 '18 at 14:17
  • check if any of the solutions given here works or not? https://stackoverflow.com/questions/18848689/button-in-uitableviewcell-not-responding-under-ios-7 – Vincent Joy Feb 20 '18 at 14:27
  • @El Tomato: i tried it, but still it didn't read the handleUnitChange function inside tableViewCell. – Xin Lok Feb 20 '18 at 20:11
  • @Agent Smith: Inside the Cell, I am calling a setup function from tableViewCell to just to add the SegmentController with its constrains to it. – Xin Lok Feb 20 '18 at 20:14
  • As I suggested before, use the closure. – El Tomato Feb 21 '18 at 03:03
  • Do you still need help? – El Tomato Feb 21 '18 at 10:52
  • @El Tomato: I EDIT the question to make it more clear, the function handleUnitChange is called just for one time and it is when I run the application. I am missing something which I am not able to find it out by myself. – Xin Lok Feb 21 '18 at 12:38
  • @El Tomato: What I wrote inside the CellForRow is correct? is this a proper way to get the information from TableViewCell? – Xin Lok Feb 21 '18 at 12:44
  • 1
    No. Sorry to say, but cell.handleUnitChange(sender: cell.unitTypeSegmentedControl) doesn't make sense to me. – El Tomato Feb 21 '18 at 12:56
  • @ElTomato So, how to get the value from tableViewCell? – Xin Lok Feb 21 '18 at 13:09

1 Answers1

1

Step 1

We first work on UITableViewCell.

class ProfileTableViewCell: UITableViewCell {
    // MARK: - IBOutlet
    @IBOutlet var firstLabel: UILabel!
    @IBOutlet var lastLabel: UILabel!
    @IBOutlet var ageLabel: UILabel!
    @IBOutlet var pictureImageView: UIImageView!
    @IBOutlet weak var segmentControl: UISegmentedControl! // (a)

    // MARK: - awakeFromNib
    override func awakeFromNib() {
        super.awakeFromNib()
        firstLabel.backgroundColor = UIColor.clear
        lastLabel.backgroundColor = UIColor.clear
        ageLabel.backgroundColor = UIColor.clear
        segmentControl.addTarget(self, action: #selector(valueChanged), for: .valueChanged) // (b)
    }

    var onSegmentChanged: ((Int, Int) -> Void)? // (c)

    // (d)
    @objc func valueChanged(sender: UISegmentedControl) {
        onSegmentChanged!(sender.tag, sender.selectedSegmentIndex)
    }
}

First, add an IBOutlet connection (a) through Interface Builder. Set a selector target (b) through awakFromNib. In order to receive user's action through UITableView, you must send a signal with the closure (c). Finally, add a target receiver (c).

Step 2

We now work with UIViewController.

class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        guard let people = fetchedResultsController.fetchedObjects else { return 0 }
        return people.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ProfileTableViewCell
        let person = fetchedResultsController.object(at: indexPath)
        cell.segmentControl.tag = indexPath.row // (e)
        // action //
        (f)
        cell.onSegmentChanged = { tag, selectedSegment in
            // (g) self.segmentTapped(tag: tag, index: selectedSegment)
            // print(tag, selectedSegment)
        }
        return cell
    }

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            // fetching data //
            let person = fetchedResultsController.object(at: indexPath)

            // deleting data //
            person.managedObjectContext?.delete(person)

            // saving the change //
            let context = persistentContainer.viewContext
            do {
                try context.save()
                print("saved...")
            } catch {
                print("failed saving")
            }
        }
    }

    (h)
    func segmentTapped(tag: Int, index: Int) {
        print(tag, index)
    }
}

When you send user's action through UITableViewCell, you must receive it through UIViewController with UITableView's cellForRowAt delegate method. In order to see which segmentControl the user has selected, you set a tag (e). The closure (f) is set before returning the cell. You can print the result inside the closure. If you want to take the result out of the delegate method, see (g) and (h).

Warning

If you take a good look at line (e), indexPath.row is used as a tag. This approach works only if you know that none of the table rows is removed. Otherwise, your table data must have a dictionary value that gives each row a unique number.

Test

See how it works. When the user uses the segment control on the second row, you will receive a call through cellForRowAt. The result is 2 and 1, 2 as in the row, 1 as in the selectedSegmentIndex value. The user doesn't have to select any of the table rows.

enter image description here

El Tomato
  • 6,479
  • 6
  • 46
  • 75