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.
Related
I am learning VIPER. I have successfully processed the data from View->Presnter->Interactor and returning the data from Interactor->Presenter->View. The data is successfully coming back to me and in that extension I am trying to reload the data.
When I try to reload the data, the TableView comes nil. Why is it nil? When I use MVC pattern I do not see the TableView coming nil and everything works fine. Any clue?
Here is my TableView
#IBOutlet weak var matchTable: UITableView!
It is connected to the File Owner.
override func viewDidLoad() {
super.viewDidLoad()
setup()
matchTable.delegate = self
matchTable.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
presentor?.initiateFetch()
}
And here is the extension to ViewController.
extension MatchListingViewController: PresenterToViewProtocol{
func showMatches(match: MatchDetails?, banner: Banner?) {
matchD = match
bannerD = banner
matchTable.reloadData()
}
func showError() {
print("I am there")
}
}
Here when the function showMatches is called I get the data but when I reload it throws an error that matchTable is nil. Any help?
I think you forgot injection presentor or init protocol. The nil isn't come from tableView, need more code to know exactly which is wrong here.
in VC class:
extension MatchListingViewController: PresenterToViewProtocol
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.presentor?.initProtocol(self)
}
in presentor class:
func initProtocol(_ protocol: PresenterToViewProtocol) {
self.protocol = protocol
self.initiateFetch()
}
then
self.protocol?.showMatches(match: match, banner: banner)
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
}
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")
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.
I'm busy making an app with an account page. I want that users can logon via that page and as soon as they have done so successfully that the page reloads to display their account information rather than the standard message stating that they have to logon to make use of the page.
However when I get sent back to the account page from logging on the view doesn't really update. So therefore I am wondering if I can't reload the view after certain buttons are pressed that can check again wether the user is logged on or not and deal accordingly.
if you want to trigger layouting or just drawing there is setNeedsLayout and setNeedsDisplay
There is no built-in method to reload custom data (on iOS)
so do a reload and inside a reload -- call setNeedsDisplay
import UIKit
protocol MyViewDelegate {
func viewString() -> String;
}
class MyView : UIView {
var myViewDelegate : MyViewDelegate?
private var str : String?
func reloadData() {
if myViewDelegate != nil {
str = myViewDelegate!.viewString()
}
self.setNeedsDisplay()
}
override func drawRect(rect: CGRect) {
UIColor.whiteColor().setFill()
UIRectFill(self.bounds)
if str != nil {
let ns = str! as NSString
ns.drawInRect(self.bounds, withAttributes: [NSForegroundColorAttributeName: UIColor.blueColor(), NSFontAttributeName: UIFont.systemFontOfSize(10)])
}
}
}
class ViewController: UIViewController, MyViewDelegate {
func viewString() -> String {
return "blabla"
}
var v : MyView!
override func viewDidLoad() {
super.viewDidLoad()
v = MyView(frame: self.view.bounds)
self.view.addSubview(v)
v.myViewDelegate = self;
}
override func viewWillAppear(animated: Bool) {
v.reloadData()
}
}
In Swift use this,
If you wants to reload the UIView present in the viewController just use NotificationCenter.
In ViewController class add this in viewWillAppear
override func viewWillAppear(_ animated: Bool) {
//Trigger notification
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "update"), object: nil)
}
In UIView class add Observer for the notification
required init?(coder aDecoder: NSCoder) {
//Add Observer
NotificationCenter.default.addObserver(self, selector: #selector(updateList), name: NSNotification.Name(rawValue: "update"), object: nil)
}
#objc func updateList(){
//write Reload data here......
tableView.reloadData()
}
The Swift have really advanced itself and for many people if they don't know we can refresh the whole view with just one simple line of code.
viewWillAppear(true)