Save UISwitch state in UserDefaults - ios

I would like to save the state of a UISwitch label with UserDefaults. My code looks like this:
func viewDidAppear() {
mySwitch.setOn(userDefaults.standard.bool(forKey: "mySwitchValue"), animated: true)
}
func viewWillDesappear() {
UserDefaults.standard.set(mySwitch.isOn, forKey: "mySwitchValue")
}
But in the app, when I leave the switch view, and I return in, the UISwitch isn't as I turned it.

Probably, what rmaddy pointed out earlier is the issue. In that case go for spell thingy.
Otherwise, it is possible that setting the value of your switch's state when view is disappearing is not a judicious choice. As when application goes into background other processes are acted upon alongside, and probably setting default is not effected before application closes.
I would generally set such values when I am calling such functions, i.e., in the switch action. As soon as a user changes the switch state, save it in defaults, that way when you retrieve it when viewDidAppear, it will work.
import UIKit
class ViewController: UIViewController {
let userDefaults = UserDefaults.standard
#IBOutlet weak var mySwitch: UISwitch!
#IBAction func switchAction(_ sender: UISwitch) {
userDefaults.set(sender.isOn, forKey: "mySwitchValue")
}
override func viewDidAppear(_ animated: Bool) {
mySwitch.isOn = userDefaults.bool(forKey: "mySwitchValue")
}
}
Demo below:

This is not an answer to your original query, but an answer to another query in the comment. Question: How to set the default state of UISwitch as on, if application is launched for the first time?
Though ideally, it should be asked as another question, given it is incremental, the code is below:
import UIKit
class ViewController: UIViewController {
let userDefaults = UserDefaults.standard
var firstTimeAppLaunch: Bool {
get {
// Will return false when the key is not set.
return userDefaults.bool(forKey: "firstTimeAppLaunch")
}
set {}
}
#IBOutlet weak var mySwitch: UISwitch!
#IBAction func switchAction(_ sender: UISwitch) {
userDefaults.set(sender.isOn, forKey: "mySwitchValue")
}
override func viewDidLoad() {
super.viewDidLoad()
if !firstTimeAppLaunch {
// This will only be trigger first time the application is launched.
userDefaults.set(true, forKey: "firstTimeAppLaunch")
userDefaults.set(true, forKey: "mySwitchValue")
}
// Do any additional setup after loading the view, typically from a nib.
}
override func viewDidAppear(_ animated: Bool) {
mySwitch.isOn = userDefaults.bool(forKey: "mySwitchValue")
}
}
Note that you could do this within AppDelegate's function:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Could add the above code within this as well. Upto you.
return true
}

Related

swift - how to save switch, even I change TableViews

import UIkit
// ViewController file
class ViewController: UITableViewController, UIViewController{
#IBAction func aaaSwitch(_ sender: UISwitch) {
if (sender.isOn == true)
{
ClickCounterA+=2
UserDefaults.standard.set(aaaSwitch, forKey: "aaaSwitch")
UserDefaults.standard.bool(forKey: "aaaSwitch")
defaults.set(aaaSwitch, forKey: "aaaSwitch")
}
else
{
defaults.set(aaaSwitch, forKey: "aaaSwitch")
}
}
I change the switch(aaaSwitch) in tableview and move to another tableview. After that, I return to the first tableview, the switch is the statement before I change. In other words, I can't save the switch statement.
The problem is you're passing aaaSwitch, the method, into the UserDefaults set calls. You want to be passing in a value, in this case a boolean value.
UserDefaults.standard.set(sender.isOn, forKey: "aaaSwitch")
If you really wanted to archive your entire UISwitch, you'd need to first convert it to an NSData (and you'd likely have to come up with an algorithm for doing so). And I can see no point in ever wanting to do that. Surely you just want to persist its on/off state, so use the above code.
class ViewController: UITableViewController, UIViewController{
#IBOutlet weak var aaaswitch: UISwitch!
override func viewWillAppear(_ animated: Bool) {
aaaswitch.isOn = UserDefaults.standard.bool(forKey: "aaaSwitch")
}
#IBAction func aaaSwitch(_ sender: UISwitch) {
if (sender.isOn == true)
{
....
}
else
{
....
}
UserDefaults.standard.set(sender.isOn, forKey: "aaaSwitch")
}
}

Saving and retrieving a bool with UserDefaults

I'm trying to save a bool value to UserDefaults from a UISwitch, and retrieve it in another view. However, I've tried following multiple tutorials and stack answers and none seem to work.
This is how I'm saving it:
class SettingsViewController: UITableViewController {
#IBOutlet weak var soundSwitchOutlet: UISwitch!
#IBAction func soundSwitch(_ sender: UISwitch) {
UserDefaults.standard.set(soundSwitchOutlet.isOn, forKey: "sound")
}
and this is how I'm trying to retrieve it in another view:
if let savedValue = UserDefaults.standard.bool(forKey: "sound") {
boolValue = savedValue
}
//this is inside viewDidLoad and "boolValue" was declared outside viewDidLoad//
For a reason this code is giving me errors and none of the things I've tried have worked. How can I save a bool to UserDefaults and retrieve it in another view?
Edit: I think I fixed the first part. However, the way I'm retrieving the boolean seems to be totally wrong. Also: No other stackExchange answer responds to what I'm asking, at least not in swift.
As Leo mentioned in the comments bool(forKey returns a non-optional Bool. If the key does not exist false is returned.
So it's simply
boolValue = UserDefaults.standard.bool(forKey: "sound")
Calling synchronize() as suggested in other answers is not needed. The framework updates the user defaults database periodically.
Do it like this.
In your first view controller.
create an IBoutlet connection to your UISwitch
And then the action for your UISwitch. so in the end, your first view controller should look like this.
import UIKit
class FirstViewController: UIViewController {
#IBOutlet weak var myswitch: UISwitch! // Outlet connection to your UISwitch (just control+ drag it to your controller)
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func myswitchAction(_ sender: Any) { // Action for your UISwitch
var myswitctBool : Bool = false // create a local variable that holds your bool value. assume that in the beginning your switch is offed and the boolean value is `false`
if myswitch.isOn == true { // when user turn it on then set the value to `true`
myswitctBool = true
}
else { // else set the value to false
myswitctBool = false
}
// finally set the value to user default like this
UserDefaults.standard.set(myswitctBool, forKey: "mySwitch")
//UserDefaults.standard.synchronize() - this is not necessary with iOS 8 and later.
}
}
End of the first view controller
Now in your second view controller
you can get the value of userdefault, which you set in first view controller. I put it in the viewdidload method to show you how it works.
import UIKit
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let myswitchBoolValuefromFirstVc : Bool = UserDefaults.standard.bool(forKey: "mySwitch")// this is how you retrieve the bool value
// to see the value, just print those with conditions. you can use those for your things.
if myswitchBoolValuefromFirstVc == true {
print("true")
}
else {
print("false")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Hope this will help to you. good luck
Use this line of code:
#IBAction func soundSwitch(_ sender: UISwitch) {
UserDefaults.standard.set(soundSwitchOutlet.isOn, forKey: "sound")
}
insteadof :
#IBAction func soundSwitch(_ sender: UISwitch) {
UserDefaults.standard.set(soundSwitchOutlet, forKey: "sound")
}
Try this:
#IBAction func soundSwitchs(_ sender: Any)
{
UserDefaults.standard.set(soundSwitchOutlet.isOn, forKey: "sound")
UserDefaults.standard.synchronize()
}
//this is inside viewDidLoad and "boolValue" was declared outside viewDidLoad//
boolValue = UserDefaults.standard.bool(forKey: "sound")

Disable Button from another view controller with a switch with swift

Ok so I am trying to make it when the user flips the cheat mode switch it will disable the answerButton. I did try to set the button to disable near the bottom however it does not appear to do anything. I added the print("") to see if it would print in the console however nothing was printed. I a unsure what is wrong here.
I have pasted my code below.
import Foundation
import UIKit
class SettingsController: UITableViewController {
#IBOutlet weak var cheatSwitch: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
cheatSwitch.isOn = UserDefaults.standard.bool(forKey: "switchState")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func saveSettings(_ sender: UISwitch) {
UserDefaults.standard.set(cheatSwitch.isOn, forKey: "switchState")
UserDefaults.standard.synchronize()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let mainView : ViewController = segue.destination as! ViewController
if cheatSwitch.isOn == true {
print("turn off button")
mainView.btnAnswer.isEnabled = false;
}
else {
print("turn on button")
mainView.btnAnswer.isEnabled = true;
}
}
}
You are saving the value UISwitch's state in UserDefaults so you can use its value in ViewController's method viewDidApper.
override func viewDidAppear(_ animated: Bool) {
self.btnAnswer.isEnabled = !UserDefaults.standard.bool(forKey: "switchState")
}
Note: There is no need to add prepareForSegue in your SettingsController because it will never called when you press back button of NavigationController so consider to remove it.

Loading a switch state from NSUserDefault on load

I am working on a settings view for a basic app. Basic, in there is just one switch in the settings view for the user. The switch setting is saved with NSUserDefault. I use a delegate to send the switch signal from the settings view to the main view. The delegation works properly.
The UI is basic. On the main view, a label will read On in green (if the switch is on) and Off in red (if the switch is off.) There is a setting button in the top right that will segue (settingsSegue) to the settings UITableViewController, where the UISwitch is located.
The problem is loading up the NSUserDefault once the app loads. In viewDidLoad, I check to see if there's a value saved for the switch key. If there is, load it up. If not, set it to false (in the storyboard, the switch is set to false as default.) The Switch Status loads up as Off every time. Even if the default value is On. This shouldn't be happening.
ViewController.swift:
import UIKit
var nsDefaults = NSUserDefaults.standardUserDefaults()
class ViewController: UIViewController, SettingsViewControllerDelegate {
var onFromMain = Bool()
#IBOutlet weak var switchStateLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
if let mySavedKey = nsDefaults.objectForKey("savedSwitchSettingDefault") {
// A value exists. Load it up.
nsDefaults.objectForKey("savedSwitchSettingDefault")
print("The switch is set! \(mySavedKey)")
checkSwitchState()
}
else {
// Nothing stored in NSUserDefaults yet. Set switch to False.
nsDefaults.setBool(false, forKey: "savedSwitchSettingDefault")
checkSwitchState()
}
}
func myVCDidFinish(controller: SettingsViewController, switchState: Bool) {
onFromMain = switchState.boolValue
checkSwitchState()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "settingsSegue" {
let nav = segue.destinationViewController as! UINavigationController
let secondVC = nav.topViewController as! SettingsViewController
secondVC.delegate = self
}
}
func checkSwitchState() {
if onFromMain {
switchStateLabel.text = "On"
switchStateLabel.textColor = UIColor.greenColor()
}
else {
switchStateLabel.text = "Off"
switchStateLabel.textColor = UIColor.redColor()
}
}
}
SettingsViewController.swift:
import UIKit
protocol SettingsViewControllerDelegate {
func myVCDidFinish(controller: SettingsViewController, switchState: Bool)
}
class SettingsViewController: UITableViewController {
var delegate: SettingsViewControllerDelegate? = nil
#IBOutlet weak var switchOutlet: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
switchOutlet.on = nsDefaults.boolForKey("savedSwitchSettingDefault")
}
#IBAction func closeSettingsPageBarButtonItemPressed(sender: UIBarButtonItem) {
if (delegate != nil) {
delegate!.myVCDidFinish(self, switchState: switchOutlet.on)
self.dismissViewControllerAnimated(true, completion: nil)
}
}
#IBAction func switchPressed(sender: UISwitch) {
// Tap the switch to change the setting.
nsDefaults.setBool(switchOutlet.on, forKey: "savedSwitchSettingDefault")
}
}
I believe my problem lies somewhere in loading up the default key for "savedSwitchSettingDefault". Is this correct? Or does the issue lie elsewhere in the code?
You can tidy things up quite a bit by relying on the fact that the default you want is false and that boolForKey gives you false when the key isn't present.
Also, by accessing the setting in viewWillAppear you can avoid the need for the delegate callback.
ViewController.swift
import UIKit
class ViewController: UIViewController {
let nsDefaults = NSUserDefaults.standardUserDefaults()
var onFromMain = false
#IBOutlet weak var switchStateLabel: UILabel!
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.onFromMain = self.nsDefaults.boolForKey("savedSwitchSettingDefault")
self.checkSwitchState()
}
func checkSwitchState() {
if self.onFromMain {
switchStateLabel.text = "On"
switchStateLabel.textColor = UIColor.greenColor()
}
else {
switchStateLabel.text = "Off"
switchStateLabel.textColor = UIColor.redColor()
}
}
}
SettingsViewController.swift:
import UIKit
class SettingsViewController: UITableViewController {
let nsDefaults = NSUserDefaults.standardUserDefaults()
#IBOutlet weak var switchOutlet: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.switchOutlet.on = self.nsDefaults.boolForKey("savedSwitchSettingDefault")
}
#IBAction func closeSettingsPageBarButtonItemPressed(sender: UIBarButtonItem) {
self.dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func switchPressed(sender: UISwitch) {
// Tap the switch to change the setting.
self.nsDefaults.setBool(self.switchOutlet.on, forKey: "savedSwitchSettingDefault")
}
}
When retrieving the bool value from User Defaults, boolForKey will return false if the value doesn't exist. So in this case there's no need for unwrapping. From the documentation:
If a boolean value is associated with defaultName in the user defaults, that value is returned. Otherwise, false is returned.
If the value is getting set (you are sure of it), and the behavior of the app is not working correctly your problem might lie elsewhere.
I would recommend using another approach, declare your "onFromMain" as an optional boolean, then unwrap it when you need it.
var onFromMain: Bool?
...
func checkSwitchState() {
//- This will unwrap your optional or set false if its nil
let switchSate = onFromMain ?? false
//- Then you can set the values based on the value (or the default false)
switchStateLabel.text = switchState ? "On" : "Off"
switchStateLabel.textColor = switchState ? UIColor.greenColor() : UIColor.redColor()
}
Then attach the debugger with a breakpoint and see if the value is being unwrapped or if its defaulting to false.
Also, you are setting your delegate only when the segue is called, depends of the scenario, and if i understand you correctly, you migt not get the value until you have actually navigated to the settings view. So when opening the app (without navigating to the settings view) the onFromMain will never get populated.
Alternatively you can fetch the value on the view did load method to get it straight away when you load the app.

How can I force a global variable to "update"?

I think this is a simple problem, so you might go straight to the question.
I have a global variable, switchStatusIndex, (does not belong to any class.):
import Foundation
//Default
var userIndex:[Int] = [0,1,2]
var userIndexForItem:[Int] = [Int]()
//Index to hold the switch status for all datatypes
var switchStatusIndex:[String] = [
NSUserDefaults.standardUserDefaults().boolForKey("switchA").description,
NSUserDefaults.standardUserDefaults().boolForKey("switchB").description,
NSUserDefaults.standardUserDefaults().boolForKey("switchC").description]
In ViewControllerOne I have a function:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
setupUserIndexForItem ()
}
func setupUserIndexForItem () {
userIndexForItem = [Int]() //clears userIndexForItem
for i in 0...userIndex.count-1{
if switchStatusIndex[i] == "true"{
userIndexForItem.append(userIndex[i])
}
}
}
Finally I have a ViewControllerTwo with UISwitches ex
#IBOutlet var switchA: UISwitch!
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
override func layoutSubviews() {
restoreSwitchesStates()
}
#IBAction func setStateSwitchA(sender: UISwitch) {
var userdefault: NSUserDefaults = NSUserDefaults.standardUserDefaults()
if switchA.on == true {
userdefault.setBool(switchA!.on, forKey: "switchA")
}else if switchA.on == false {
userdefault.setBool(switchA!.on, forKey: "switchA")
}
userdefault.synchronize()
}
func restoreSwitchesStates() {
switchA!.on = NSUserDefaults.standardUserDefaults().boolForKey("switchA")
}
When I flip the switch to .on, it updates NSUserdefault to "true".
However, when I go back to ViewControllerOne, setupUserIndexForItem does not reflect this change. It is as switchStatusIndex does not update.
Question: How can I force a global variable to "update" ?
If I understand correctly your code, you're expecting the contents of the switchStatusIndex array to be updated every time you access that array from within the viewWillAppear method. But that's not going to happen because switchStatusIndex is set only once and never changed again. Perhaps what you intended was to define switchStatusIndex as a computed property or a property that is initialised by a closure, so that every time you access it, something happens (the array contents get updated with the values in NSUserDefaults).
You can do this by many way.you can take switchStatusArray as global by putting in Appdelegate or you can use nsnotifiation or you can make delegate.
If use notification then first make method to intialize that array and use as a selector in notification method.
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: "intializeSwitcharrayMethod:",
name:"intializeSwitcharray" object: nil)
#objc func intializeSwitcharrayMethod(notification: NSNotification){
//do stuff
Self.intializeSwitcharraywithUserdefaults()
}
func intializeSwitcharraywithUserdefaults()
{
// your code of array intialization
}
You can call intializeSwitcharraywithUserdefaults() from viewdidload also.

Resources