I have a UISwitch in the settings menu in my app and I am having difficulties trying to get the initial state to be 'on'. Even if I set the initial state to be 'on' in the attributes inspector, it still sets it to 'off' when the is opened for the first time.
Basically at the moment, the switch will be set to 'off' when the app opened for the first time however it does save the state if you change it so that works fine.
Here is my code:
#IBAction func dupOffOnSwitch(sender: AnyObject) {
if dupSwitch.on == true {
autoAdjust = true
println(autoAdjust)
} else {
autoAdjust = false
println(autoAdjust)
}
override func viewWillAppear(animated: Bool) {
dupSwitch.on = NSUserDefaults.standardUserDefaults().boolForKey("autoAdjustSettings")
println(NSUserDefaults.standardUserDefaults().boolForKey("autoAdjustSettings"))
}
'autoAdjust' is declared under import UIKit as true.
In order to set NSUserDefaults.standardUserDefaults().boolForKey("autoAdjustSettings") to true for the first time you ever launch your app, you can replace the application:didFinishLaunchingWithOptions: method of your AppDelegate class with this code:
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
//If app has never been launched...
if !NSUserDefaults.standardUserDefaults().boolForKey("isNotFirstLaunch") {
//Set autoAdjustSettings and isNotFirstLaunch to true
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "autoAdjustSettings")
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "isNotFirstLaunch")
//Sync NSUserDefaults
NSUserDefaults.standardUserDefaults().synchronize()
}
return true
}
Of course, you can do the previous code in your UIViewController subclass but the AppDelegate should be the place for those settings.
Once done, your UIViewController subclass should look like this:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var dupSwitch: UISwitch!
#IBAction func dupOffOnSwitch(sender: AnyObject) {
dupSwitch.on = (sender as UISwitch).on //Bool
NSUserDefaults.standardUserDefaults().setBool(dupSwitch.on, forKey: "autoAdjustSettings")
NSUserDefaults.standardUserDefaults().synchronize()
}
override func viewDidLoad() {
super.viewDidLoad()
dupSwitch.on = NSUserDefaults.standardUserDefaults().boolForKey("autoAdjustSettings")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Unless you have a good reason to, I don't think you need to use the viewWillAppear: method in your UIViewController subclass for your NSUserDefaults statements. viewDidLoad: should be the right place for that.
I fixed my issue by adding this:
dupSwitch.on = NSUserDefaults.standardUserDefaults().boolForKey("autoAdjustSettings")
println(NSUserDefaults.standardUserDefaults().boolForKey("autoAdjustSettings"))
userReturnedAuto = NSUserDefaults.standardUserDefaults().boolForKey("userReturnedAuto")
if userReturnedAuto == false {
dupSwitch.on = true
userReturnedAuto = true
NSUserDefaults.standardUserDefaults().setBool(userReturnedAuto, forKey: "userReturnedAuto")
NSUserDefaults.standardUserDefaults().setBool(userReturnedAuto, forKey: "autoAdjustSettings")
}
Probably not the ideal solution but it works.
Related
I've declared a boolean with default value true, if my UISwitch is on I want boolean variable to be true else false, my code works fine until I try to store that boolean in UserDefaults when I reset the xCode Simulator I check with print method but it is not really saved...
any solution will be appericated.
var userDefaultSamarxvo:Bool = true
let defaults = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
defaults.set(userDefaultSamarxvo,forKey: "Samarxvo") // set of userdefault
if userDefaultSamarxvo {
print("hello")
}else{
print("olleh")
}
}
#IBAction func samarxvoDidChange(_ sender: UISwitch) {
if sender.isOn {
userDefaultSamarxvo.toggle()
print("samarxo")
}else{
userDefaultSamarxvo.toggle()
print("samarxo 1 ")
}
}
In viewDidLoad you want to load the saved value from user defaults. When the switch changes you need to save the new value to user defaults.
Setting a local variable (userDefaultSamarxvo) doesn't change what is stored in the user defaults; Local variables don't bind to user defaults storage.
There is an added complication; bool(forKey) will return false if there is no value for the key in UserDefaults. If you want the initial value to be true then you need to handle that in some way. You can use object(forKey) which returns an optional
var userDefaultSamarxvo:Bool = true {
didSet {
UserDefaults.standard.set(userDefaultSamarxvo, forKey:switchKey)
}
}
let defaults = UserDefaults.standard
let switchKey = "Samarxvo"
#IBOutlet weak var theSwitch: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
if defaults.object(forKey: switchKey) != nil {
userDefaultSamarxvo = defaults.bool(forKey: switchKey)
}
self.theSwitch.isOn = userDefaultSamarxvo
}
#IBAction func samarxvoDidChange(_ sender: UISwitch) {
userDefaultSamarxvo = sender.isOn
}
I have used a didSet clause to update the user defaults value when the property changes.
Im having button in all viewcontrollers to change language
LanguageViewController.swift
class LanguageViewController: UIViewController {
#IBAction func actionChange(_ sender: Any) {
L102Language.currentAppleLanguage()
L102Language.setAppleLAnguageTo(lang: "en")
// below code to refresh storyboard
self.viewDidLoad()
}
}
L102Language.swift
class func currentAppleLanguage() -> String{
let userdef = UserDefaults.standard
let langArray = userdef.object(forKey: APPLE_LANGUAGE_KEY) as! NSArray
let current = langArray.firstObject as! String
let endIndex = current.startIndex
let currentWithoutLocale = current.substring(to: current.index(endIndex, offsetBy: 2))
return currentWithoutLocale
}
/// set #lang to be the first in Applelanguages list
class func setAppleLAnguageTo(lang: String) {
let userdef = UserDefaults.standard
userdef.set([lang,currentAppleLanguage()], forKey: APPLE_LANGUAGE_KEY)
userdef.synchronize()
}
I inherited LanguageViewController in all my FirstViewCOntroller, SecondController as below
class FirstViewController: LanguageViewController {
}
class SecondController: LanguageViewController {
}
If I call self.viewDidLoad() it fails to change language from view defined in storyboard. How to reload storyboard, so that the language should change in all viewcontroller,if any button from any viewcontroller is clicked? Thanks!
You can use NotificationCenter for reloading the view controllers content, this will also reload the content of view controllers that are not visible.
extension Notification.Name {
static let didChangeLanguage = Notification.Name("didChangeLanguage")
}
override func viewDidLoad() {
//Add a listener
NotificationCenter.default.addObserver(self, selector: #selector(onDidChangeLanguage(_:)), name: .didChangeLanguage, object: nil)
}
#IBAction func actionChange(_ sender: Any) {
L102Language.currentAppleLanguage()
L102Language.setAppleLAnguageTo(lang: "en")
// Notify about the change.
NotificationCenter.default.post(name: .didChangeLanguage, object: self, userInfo: nil)
}
#objc func onDidChangeLanguage(_ notification:Notification) {
// reload content using selected language.
}
Correct me if I'm wrong. but I think you don't need to reload all view controllers. you just need to update them when they get displayed, view controllers are behind the presented one are not visible for the user.
for doing that you can do something like this:
var currentLanguage = ""
override func viewDidLoad() {
currentLanguage = currentAppleLanguage()
loadContentForLanguage(currentLanguage)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// this will be executed every time this sceen gets display
if currentLanguage != currentAppleLanguage() {
currentLanguage = currentAppleLanguage()
loadContentForLanguage(currentLanguage)
}
}
func loadContentForLanguage(_ currentLanguage: String) {
//here it goes whatever you currently have in viewDidLoad
}
My apologies if this does not compile, my swift is really rusty.
This question already has answers here:
How do i keep UISwitch state when changing ViewControllers?
(3 answers)
Closed 5 years ago.
I would like to save the State of an UISwitch after to change between View Controllers. Any help would be greatly appreciated!
I have a first View Controller with an UISwitch, to control the music in the background in different View Controllers:
#IBOutlet weak var SwitchMusic: UISwitch!
let defaults = UserDefaults.standard
var switchON : Bool = false
#IBAction func checkState(_ sender: UISwitch) {
if (sender.isOn == true)
{
switchON = true
defaults.set(switchON, forKey: "switchON")
MusicHelper.sharedHelper.playBackgroundMusic()
}
if (sender.isOn == false)
{
switchON = false
defaults.set(switchON, forKey: "switchON")
MusicHelper.sharedHelper.stopBackgroundMusic()
}
}
And a Second View Controller to load or no the music in the background if the switch is On or Off:
override func viewDidLoad() {
super.viewDidLoad()
if defaults.value(forKey: "switchON") != nil{
let switchON: Bool = defaults.value(forKey: "switchON") as! Bool
if switchON == true{
MusicHelper.sharedHelper.playBackgroundMusic()
}
else if switchON == false{
MusicHelper.sharedHelper.stopBackgroundMusic()
}
}
}
Also I have a class with the music:
class MusicHelper {
let defaults = UserDefaults.standard
static let sharedHelper = MusicHelper()
var musicBackgroundIntro:AVAudioPlayer = AVAudioPlayer()
func playBackgroundMusic() {
do {
let audioPath = Bundle.main.path(forResource: "Music", ofType: "mp3")
try musicBackgroundIntro = AVAudioPlayer(contentsOf: NSURL(fileURLWithPath: audioPath!) as URL)
musicBackgroundIntro.numberOfLoops = -1
musicBackgroundIntro.prepareToPlay()
musicBackgroundIntro.play()
} catch {
print("Cannot play the file")
}
}
func stopBackgroundMusic() {
musicBackgroundIntro.stop()
}
}
Now it is working perfectly the music in the background between View Controllers, and it is possible to turn off and on... but unfortunately do not save the current state of the UISwitch, and always when I enter in the First View Controller the state of the Switch is On.
Also any idea that how will be possible to apply in a Slider too?
Any help would be greatly appreciated!
Thanks
The easiest way for you would be to create a static var isSwitchOn: Bool = false
This state will be preserved between back and forth transitions.
You should reflect the state, if music is playing...
class MusicHelper {
public isPlaying: Bool {
get {
return musicBackgroundIntro.isPlaying
}
}
// your stuff here..
}
That way in other view controllers:
SwitchMusic.isOn = MusicHelper.sharedHelper.isPlaying
If you need other view controllers to update in response to this, you can add a delegate event (aka observer) if necessary.
Try something like that: Use the UISwitch as an #IBOutlet.
#IBOutlet weak var checkState: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
self.checkState.addTarget(self, action: #selector(action(sender:)), for: .valueChanged)
}
// Save state
func action(sender: UISwitch) {
let userDefaults = UserDefaults.standard
userDefaults.set(sender.isOn, forKey:"identifier")
}
// Retrieve state
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let userDefaults = UserDefaults.standard.bool(forKey: "identifier")
self.checkState.setOn(userDefaults, animated: false)
}
You can give the switch a default value when it's created in viewcontroller1.
Assign the (default.value(forKey:"switchOn") as! Bool ) ?? false to that switch.
I am very new to swift. And need your help!
I want that, when the user logs in for the second time , the app should directly take it to the next view controller named CoreView. It should not ask for details, but I don't know why its not working. And it's asking for details everytime the app is launched. Please check the below code. I am not getting any sort of error too. Unless and until the app is killed or logged out, the user should be able to log in directly .
func pref_write()
{
// To write the data to NSUserDefaults
let prefs = NSUserDefaults.standardUserDefaults() // make a reference
print("OTP:\(OTP)")
// Adding values. Creating objects in prefs
prefs.setObject(OTP, forKey: "OTP")
print("check_OTP:\(check_OTP)")
prefs.setObject(U_ID, forKey: "U_ID")
print("Check_U_ID:\(check_U_ID)")
prefs.synchronize()
self.performSegueWithIdentifier("ContinueToCoreView", sender: self)
}
And in the viewDidLoad function:
override func viewDidLoad()
{
super.viewDidLoad()
//Read the data
self.performSegueWithIdentifier("ContinueToCoreView", sender: self)
pref_write()
let prefs = NSUserDefaults.standardUserDefaults()
check_OTP = prefs.objectForKey("OTP")!
check_U_ID = prefs.objectForKey("U_ID")!
prefs.objectForKey("U_ID")
print("prefs:\(prefs)")
prefs.synchronize()
}
Thanks!
Create a class as
class User_Details : NSObject
{
var user_id : String?
var user_otp : String?
var otp_verified : Bool?
init(u_id:String, otp:String?, verified:Bool)
{
super.init()
self.user_id = u_id
self.otp_verified = verified
self.user_otp = otp
}
}
In AppDelegate,
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
{
navController = self.window?.rootViewController as? UINavigationController
if self.checkIfUserLoggedIn()
{
let user_details = NSUserDefaults.standardUserDefaults().objectForKey("user_details") as! User_Details
self.moveToNextScreen(user_details)
}
return true
}
//AppDelegate Class or in the class which is globally accessible
func pref_write_user(user_details : User_Details)
{
let prefs = NSUserDefaults.standardUserDefaults()
prefs.setObject(user_details, forKey: "user_details")
prefs.setBool(true, forKey: "is_user_login")
//After saving the OTP for current user, check for otp verified, move to OTP Screen
self.moveToNextScreen(user_details)
}
func moveToNextScreen(user_details : User_Details)
{
if user_details.otp_verified == false
{
// Move to OTP screen
let viewController = self.navController?.storyboard?.instantiateViewControllerWithIdentifier("otpScreen")
self.navController?.pushViewController(viewController!, animated: false)
}
else // Move to Home Screen
{
let viewController = self.navController?.storyboard?.instantiateViewControllerWithIdentifier("homeScreen")
self.navController?.pushViewController(viewController!, animated: false)
}
}
func logoutUser()
{
let prefs = NSUserDefaults.standardUserDefaults()
prefs.setObject(nil, forKey: "user_details")
prefs.setBool(false, forKey: "is_user_login")
}
func checkIfUserLoggedIn() -> Bool
{
let prefs = NSUserDefaults.standardUserDefaults()
if prefs.boolForKey("is_user_login")
{
if let _ = prefs.objectForKey("user_details")
{
return true
}
else
{
//User details not found for some reason, so setting the inital values and return false
self.logoutUser()
}
}
return false
}
Login Class :
Call the API for login by providing the basic credential, get the user_id and user_otp, save them to NSUserDefaults
func requestLoginToServer()
{
//Perform basic server action
....
//In Success Block write this
let appDel = UIApplication.sharedApplication().delegate as! AppDelegate
// pass the values as return by the server
let user_details = User_Details(u_id: "123", otp: "1234", verified: false)
appDel.pref_write_user(user_details)
appDel.moveToNextScreen(user_details)
}
Please try this way. I just rearranged your code.
First it will check the login credentials with in the didload method of initial view controller. If it not there it will call the method pref_write() . Please make sure that the values used in pref_write() method are not nil
override func viewDidLoad()
{
super.viewDidLoad()
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
// You can give conditions for your need like if(prefs.valueForKey("U_ID") != nil))
// It will check the user defaults whether you already login
if(prefs.valueForKey("OTP") != nil) {
self.performSegueWithIdentifier("ContinueToCoreView", sender: self)
}
else{
pref_write()
}
}
// Make sure the Values are not nil
func pref_write()
{
// To write the data to NSUserDefaults
let prefs = NSUserDefaults.standardUserDefaults() // make a reference
print("OTP:\(OTP)")
// Adding values. Creating objects in prefs
prefs.setObject(OTP, forKey: "OTP")
print("check_OTP:\(check_OTP)")
prefs.setObject(U_ID, forKey: "U_ID")
print("Check_U_ID:\(check_U_ID)")
prefs.synchronize()
self.performSegueWithIdentifier("ContinueToCoreView", sender: self)
}
Hope its working...
I keep getting fatal error:
unexpectedly found nil while unwrapping an Optional value.
I know the general sense of the error, however I don't know what I'm doing wrong to cause it.
I've narrowed the issue down to an unwind segue of mind:
import UIKit
import Foundation
import CoreData
import Alamofire
import FastImageCache
import SwiftyJSON
class ViewControllerTabBar: UITabBarController {
var shouldLogin = false
var user: User? {
didSet {
if user != nil {
} else {
shouldLogin = true
}
}
}
var nextURLRequest: NSURLRequest?
var coreDataStack: CoreDataStack!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
UITabBar.appearance().tintColor = UIColor(red: 99/255, green: 222/255, blue: 154/255, alpha: 1)
if let fetchRequest = coreDataStack.model.fetchRequestTemplateForName("UserFetchRequest") {
let results = try! coreDataStack.context.executeFetchRequest(fetchRequest) as! [User]
user = results.first
}
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if shouldLogin {
performSegueWithIdentifier("login", sender: self)
shouldLogin = false
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func unwindToPhotoBrowser (segue : UIStoryboardSegue) {
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "login" && segue.destinationViewController.isKindOfClass(UINavigationController.classForCoder()) {
let navigationController = segue.destinationViewController as! UINavigationController
if let oauthLoginViewController = navigationController.topViewController as? OauthLoginViewController {
oauthLoginViewController.coreDataStack = coreDataStack
}
if self.user != nil {
coreDataStack.context.deleteObject(user!)
coreDataStack.saveContext()
}
}
}
}
The issue seems to be with
#IBAction func unwindToPhotoBrowser (segue : UIStoryboardSegue) {
}
I've double checked my identifier and the connection in the story board. I have a similar project that uses a similar set up for login and that project seems to get the same error when this IBAction is removed so that's why I think it might be the issue. When I search for the action in the Find Navigator it doesn't show up with the "M" icon beside it whereas with my other project is does.