Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UISwitch in accessory view of a tableviewcell, passing a parameter with the selector to have selector function see the indexpath.row?

I have a UISwitch in a tableviewcontroller, and when the switch is toggled I want it to change the value of a boolean variable in an array I created inside the view controller, that the cell is related to. Kind of like the Stock Alarm App on IOS, where each cell has a UISwitch, and toggling the switch will turn off each individual alarm. So with the UISwitch, with its selector code, this is inside the cellForRowAtIndexPath method

//switch
    let lightSwitch = UISwitch(frame: CGRectZero) as UISwitch
    lightSwitch.on = false
    lightSwitch.addTarget(self, action: #selector(switchTriggered), forControlEvents: .ValueChanged)
    //lightSwitch.addTarget(self, action: "switchTriggered", forControlEvents: .ValueChanged )

    cell.accessoryView = lightSwitch

I want it to do this

func switchTriggered(a: Int) {
    changeValueOfArray = array[indexPath.row]
}

I don't have the code written for that part yet, but my question is, How can i let the switchTriggered function see the indexPath.row value, without passing it as an argument to the function because I can't because its a selector?

like image 873
Jolaroux Avatar asked Dec 25 '22 01:12

Jolaroux


2 Answers

let lightSwitch = UISwitch(frame: CGRectZero) as UISwitch
    lightSwitch.on = false
    lightSwitch.addTarget(self, action: #selector(switchTriggered), forControlEvents: .ValueChanged)
    lightSwitch.tag = indexpath.row
    cell.accessoryView = lightSwitch

Let save your boolean value in Array

    func switchTriggered(sender: UISwitch) {
      sender.on ? array[sender.tag]=1 : array[sender.tag]=0
     }
}
like image 68
Bhagabata Avatar answered May 15 '23 15:05

Bhagabata


The basic idea is that you can capture the cell for which the switch was flipped and then use tableView.indexPath(for:) to translate that UITableViewCell reference into a NSIndexPath, and you can use its row to identify which row in your model structure needs to be updated.

The constituent elements of this consist of:

  1. Create a model object that captures the information to be shown in the table view. For example, let's imagine that every cell contains a name of a Room and a boolean reflecting whether the light is on:

    struct Room {
        var name: String
        var lightsOn: Bool
    }
    
  2. Then the table view controller would have an array of those:

    var rooms: [Room]!
    
  3. I'd define a UITableViewCell subclass with outlets for the label and the switch. I'd also hook up the "value changed" for the light switch to a method in that cell. I'd also set up a protocol for the cell to inform its table view controller that the light switch was flipped:

    protocol RoomLightDelegate: class {
        func didFlipSwitch(for cell: UITableViewCell, value: Bool)
    }
    
    class RoomCell: UITableViewCell {
    
        weak var delegate: RoomLightDelegate?
    
        @IBOutlet weak var roomNameLabel: UILabel!
        @IBOutlet weak var lightSwitch: UISwitch!
    
        @IBAction func didChangeValue(_ sender: UISwitch) {
            delegate?.didFlipSwitch(for: self, value: sender.isOn)
        }
    
    }
    

    I'd obviously set the base class for the cell prototype to be this UITableViewCell subclass and hook up the @IBOutlet references as well as the @IBAction for the changing of the value for the switch.

  4. I'd then have the UITableViewDataSource methods populate the cell on the basis of the Room properties:

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return rooms.count
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "SwitchCell", for: indexPath) as! RoomCell
    
        let room = rooms[indexPath.row]
    
        cell.roomNameLabel.text = room.name
        cell.lightSwitch.setOn(room.lightsOn, animated: false)
        cell.delegate = self
    
        return cell
    }
    
  5. Note, the above cellForRowAtIndexPath also specifies itself as the delegate for the cell, so we'd want to implement the RoomLightDelegate protocol to update our model when the light switch is flipped:

    extension ViewController: RoomLightDelegate {
    
        func didFlipSwitch(for cell: UITableViewCell, value: Bool) {
            if let indexPath = tableView.indexPath(for: cell) {
                rooms[indexPath.row].lightsOn = value
            }
        }
    
    }
    

Now, I don't want you to worry about the details of the above. Instead, try to capture some of the basic ideas:

Bottom line, to your immediate question, once you know which cell was was updated, you can inquire with the UITableView to determine what NSIndexPath that UITableViewCell reference corresponds to, using tableView.indexPath(for:).

like image 21
Rob Avatar answered May 15 '23 13:05

Rob