How to save the state of a Switch

How do I save the state of the switch. I have a switch in a custom cell. I have a class for the custom cell. I am trying to save it from a view controller.

class Settings_Custom_Cell: UITableViewCell {

    @IBOutlet weak var settingLabel: UILabel!

    @IBOutlet weak var quickAddSwitch: UISwitch!

}



class SettingsCell {

    var settingLabel: String

    var settingValue: UISwitch

    

    init(settingLabel: String, settingValue: UISwitch) {

        self.settingLabel = settingLabel

        self.settingValue = settingValue

    }

}
import UIKit



class Settings: UIViewController, UITableViewDelegate, UITableViewDataSource {

    

    var array: [String] = ["Quick Add"]

    

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

        return array.count

    }

    

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

        let cell = table.dequeueReusableCell(withIdentifier: "cell", for: indexPath as IndexPath)

        cell.textLabel?.text = ""

        cell.textLabel?.text = array[indexPath.row]

        cell.textLabel?.numberOfLines = 0

        cell.textLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping

        cell.textLabel?.font = UIFont.systemFont(ofSize: 20)

        

        return cell

    }

    

    @IBOutlet weak var table: UITableView!

    



    override func viewDidLoad() {

        super.viewDidLoad()

    }

    

}
  • You declare Settings_Custom_Cell (a very odd name as Swift code), but you are not using it in Settings. Have you really set up the storyboard for Settings to use the custom class Settings_Custom_Cell for the reuse identifier cell?

  • Yes, I setup storyboard. When I run the app, the switch displays fine but when I leave the view controller and return to the view controller the state of the switch is back to off.

  • OK, then can you explain why you are not using Settings_Custom_Cell in your code?

Accepted Reply

I figured out how to save the state of the switch when the state of the switch is changed. Thanks for your help on getting me there.

class SettingsCustomCell: UITableViewCell {
//...
@IBAction func switchValueChanged(_ sender: UISwitch) {
        state?.settingValue = sender.isOn
        let defaults = UserDefaults.standard
        defaults.set(quickAddSwitch.isOn, forKey: "quickAdd")
    }
}
class Settings: UIViewController, UITableViewDelegate, UITableViewDataSource {
//...
override func viewDidLoad() {
        super.viewDidLoad()
        cellStates = [
            SettingsState(settingLabel: "Quick Add", settingValue: (UserDefaults.standard.bool(forKey: "quickAdd")))
        ]
    }
}

Replies

OK so I rewrote some code,

class Settings: UIViewController, UITableViewDelegate, UITableViewDataSource {

    

    var data = [Settings_Custom_Cell]()

    

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

        return data.count

    }

    

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

        data = [SettingsCell(settingLabel: "Quick Add", settingValue: <#UISwitch#> )]

        let cell = table.dequeueReusableCell(withIdentifier: "cell") as! Settings_Custom_Cell

        let array = data[indexPath.row]

        cell.textLabel?.text = array.settingLabel.text

        cell.settingValue.isOn = array.settingValue.isOn

        cell.textLabel?.numberOfLines = 0

        cell.textLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping

        cell.textLabel?.font = UIFont.systemFont(ofSize: 20)

        return cell

    }

    @IBOutlet weak var table: UITableView!

    override func viewDidLoad() {

        super.viewDidLoad()

    }

    

}

I am not sure if I am on the right track but what do I put in the 'settingValue' value in

data = [SettingsCell(settingLabel: "Quick Add", settingValue: <#UISwitch#> )]

This does not compile, of course, because of

data = [SettingsCell(settingLabel: "Quick Add", settingValue: <#UISwitch#> )]

But there is, if I understand your intent, a flaw in this code:

  • you want to display datain the tableView ?
  • if so, the dataSource (data ?) should not be defined in cellForRowAt, but most likely in viewDidLoad
  • but data, with its IBOutlets, is not a correct dataSource.

So please explain in detail what you want to do:

  • what to display in each cell
  • what is the list of cells
  • Yes, I want to display data in the tableview. I have a label and a switch in the custom cell. I want to display it in the settings so users can turn the switch on or off. I want the switch to default to off. When the user turns the switch on, I want to save the state of the switch and then when the user returns to the app, I want the saved state of the switch to load.

    Thanks

Add a Comment

OK so I rewrote some code,

Thanks, that makes sense a little more than the original code.

But still, a few things:

  • Having UI elements as data would not be a good practice
  • You use textLabel although you define settingLabel in your custom cell

You may need to reflect the state of each switch to some sort of data source.

Please try something like this.

Prepare a custom cell which can reflect the changes of switch:

class SettingsCustomCell: UITableViewCell {
    
    @IBOutlet weak var settingLabel: UILabel!
    
    @IBOutlet weak var quickAddSwitch: UISwitch!
    
    var state: SettingsState? {
        didSet {
            if let state = state {
                settingLabel.text = state.settingLabel
                quickAddSwitch.isOn = state.settingValue
            }
        }
    }
    
    override func awakeFromNib() {
        quickAddSwitch.addTarget(self, action: #selector(switchValueChanged), for: .valueChanged)
    }
    
    @objc func switchValueChanged(sender: UISwitch) {
        state?.settingValue = sender.isOn
    }
}

class SettingsState {
    var settingLabel: String
    var settingValue: Bool
    
    init(settingLabel: String, settingValue: Bool) {
        self.settingLabel = settingLabel
        self.settingValue = settingValue
    }
}

And use it like this:

class Settings: UIViewController, UITableViewDelegate, UITableViewDataSource {

    var cellStates: [SettingsState] = [
        SettingsState(settingLabel: "Quick Add", settingValue: false),
    ]

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = table.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! SettingsCustomCell
        cell.state = cellStates[indexPath.row]
 
        return cell
    }

    @IBOutlet weak var table: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        table.dataSource = self
        // Load `cellStates` if you need it...
        // (I could not find when you want to save it.)
    }

}
  • That works to load it but I want to save the state when there is a change to the switch state and want to load the saved switch state when the app loads.

  • I want to save the state when there is a change to the switch state You should better have clarified it in the opening post.

Add a Comment

I want to save the state when there is a change to the switch state

Please modify the code above as follows:

class Settings: UIViewController, UITableViewDelegate, UITableViewDataSource {

    //...

    override func viewDidLoad() {
        super.viewDidLoad()

        table.dataSource = self
        // Load `cellStates` if you need it...
        NotificationCenter.default.addObserver(self, selector: #selector(switchDidChange), name: .switchDidChange, object: nil)
    }
    
    @objc func switchDidChange(_ notification: Notification) {
        // Save `cellStates`...
    }

}
class SettingsCustomCell: UITableViewCell {
    
    //...

    @objc func switchValueChanged(sender: UISwitch) {
        state?.settingValue = sender.isOn
        NotificationCenter.default.post(name: .switchDidChange, object: self)
    }
}

extension Notification.Name {
    static let switchDidChange = Notification.Name("switchDidChange")
}

I figured out how to save the state of the switch when the state of the switch is changed. Thanks for your help on getting me there.

class SettingsCustomCell: UITableViewCell {
//...
@IBAction func switchValueChanged(_ sender: UISwitch) {
        state?.settingValue = sender.isOn
        let defaults = UserDefaults.standard
        defaults.set(quickAddSwitch.isOn, forKey: "quickAdd")
    }
}
class Settings: UIViewController, UITableViewDelegate, UITableViewDataSource {
//...
override func viewDidLoad() {
        super.viewDidLoad()
        cellStates = [
            SettingsState(settingLabel: "Quick Add", settingValue: (UserDefaults.standard.bool(forKey: "quickAdd")))
        ]
    }
}