Change app language programmatically WITHOUT restarting the app - swift

When I change the language from my app it doesn't take effect until I close the app and restart it. I don't want to restart it. I tried also presenting rootViewController but doesn't work. Is there any solution?

You can change the language in every Single of ViewControllers inside ViewDidLoad() So when User Clicked on the purposed Language Save it with UserDefaults.standard.set("En", forKey: "languages") So when you've saved languages You can Get the Selected languages by UserDefaults.standard.String(forKey: "languages") Which will give you En String Value
override func viewDidLoad() {
super.viewDidLoad()
//Get Language Func
GetLanguage()
}
#IBAction func ChangeToEn(_ sender:Any) {
SaveSelectedLanguage(SomeString: "En")
}
func SaveSelectedLanguage(SomeString:String) {
//Save Selected Language
UserDefaults.standard.set("En", forKey: SomeString)
//GetResult By Get Language Func
GetLanguage()
}
func GetLanguage {
//initializing
Switch UserDefualts.standard.string(forkey:"languages") {
case "En":
lbl.text = "Hello World"
case "Tr":
lbl.text = "Merhaba Dunya"
case "Fa"
lbl.text = "سلام دنیا"
}
}

if you want to get the phone language once the app is lunch like in settings
class ViewController: UIViewController {
#IBOutlet weak var HeadTitle: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
//when You are trying to change the language
//Get Phone Language
GetPhoneLanguage()
//Get Language
GetLanguege()
}
#IBAction func SaveEn(_ sender: Any) {
SaveLanguage(Value: "en")
GetLanguege()
}
#IBAction func SaveTr(_ sender: Any) {
SaveLanguage(Value: "tr")
GetLanguege()
}
func SaveLanguage(Value:String){
UserDefaults.standard.set(Value, forKey: "AppleLanguages")
UserDefaults.standard.synchronize()
}
func GetPhoneLanguage(){
if UserDefaults.standard.string(forKey: "AppleLanguages") == nil {
let preferredLanguage = Locale.preferredLanguages[0] as String
print (preferredLanguage) //en-US
let arr = preferredLanguage.components(separatedBy: "-")
let deviceLanguage = arr.first
UserDefaults.standard.set(deviceLanguage!, forKey: "AppleLanguages")
}
}
func GetLanguege() {
switch UserDefaults.standard.string(forKey: "AppleLanguages") {
case "en":
HeadTitle.text = "Hello World"
case "tr":
HeadTitle.text = "Merhaba Dunya"
default:
print("No Languages")
}
}
}

Related

UIStepper - start counting from 1

I have successfully implemented core data and UISteppers. Every time I try to edit a saved record the UI Stepper starts over from 0. Please help me to figure put what additional code I need to retain the already edited value.
// This function adds the stepper to a field
//issue: it does not remember the score when i edit it and starts over
#IBAction func counterStepperPressed(_ sender: UIStepper) {
counterTF.text = Int(sender.value).description
}
#IBAction func pointStepperPressed(_ sender: UIStepper) {
pointTF.text = Int(sender.value).description
}
#IBAction func savingsStepperPressed(_ sender: UIStepper) {
savingsTF.text = Int(sender.value).description
}
}
I have linked core data like so:
import CoreData
class AktieViewController: UIViewController {
#IBOutlet weak var counterStepper: UIStepper!
#IBOutlet weak var pointsStepper: UIStepper!
#IBOutlet weak var savingsStepper: UIStepper!
var selectedAktie: Aktie? = nil
override func viewDidLoad()
{
super.viewDidLoad()
if(selectedAktie != nil) {
savingsTF.text = selectedAktie?.saving
counterTF.text = selectedAktie?.counter
pointTF.text = selectedAktie?.point
}
}
#IBAction func saveAction(_ sender: Any) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context: NSManagedObjectContext = appDelegate.persistentContainer.viewContext
if(selectedAktie == nil)
{
let entity = NSEntityDescription.entity(forEntityName: "Aktie", in: context)
let newAktie = Aktie (entity: entity!, insertInto: context)
newAktie.saving = savingsTF.text
newAktie.point = pointTF.text
newAktie.counter = counterTF.text
do {
try context.save()
aktieList.append(newAktie)
navigationController?.popViewController(animated: true)
}
catch
{
print("context save error")
}
}
I also have an edit and delete function.
This function eventually solved my question:
#IBAction func counterStepperPressed(_ sender: UIStepper) {
let initialValue=Int(counterTF.text) ?? 0
let newValue=Int(sender.value)+initialValue
counterTF.text="\(newValue)"
}
I have managed to add the following code to remember the value in the stepper.
if let value=UserDefaults.standard.value(forKey: "counterStepper") as? Double {
counterStepper.value=value counterTF.text=String(describing: value)
And in the action I have added the following code.
#IBAction func counterStepperPressed(_ sender: UIStepper) {
counterTF.text=String(describing: sender.value)
UserDefaults.standard.setValue(sender.value, forKey: "counterStepper")
NotificationCenter.default.post(Notification.init(name: Notification.Name("StepperDidChangeValue")))
}
The only issue I have is that if I edit a second item it remembers the value of the first item. Somehow it is not remembering the original value of the item.

Change search field's icon

I try to implement search behavior like in Xcode: if you enter something in search field, icon changes color.
I delegate both searchFieldDidStartSearching and searchFieldDidEndSearching to controller and change the image.
The problem is icon's image changes only when window lose it's focus.
class ViewController: NSViewController {
#IBOutlet weak var searchField: NSSearchField!
func searchFieldDidStartSearching(_ sender: NSSearchField) {
print("\(#function)")
(searchField.cell as! NSSearchFieldCell).searchButtonCell?.image = NSImage.init(named: "NSActionTemplate")
}
func searchFieldDidEndSearching(_ sender: NSSearchField) {
print("\(#function)")
(searchField.cell as! NSSearchFieldCell).searchButtonCell?.image = NSImage.init(named: "NSHomeTemplate")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
Thanks in advance for any ideas/suggestions.
Although I don't know the reason, it works:
NSApp.mainWindow?.resignMain()
NSApp.mainWindow?.becomeMain()
Here is the whole code:
class MyViewController: NSViewController {
private lazy var searchField: NSSearchField = {
let searchField = NSSearchField(string: "")
if let searchButtonCell = searchField.searchButtonCell {
searchButtonCell.setButtonType(.toggle)
let filterImage = #imageLiteral(resourceName: "filter")
searchButtonCell.image = filterImage.tinted(with: .systemGray)
searchButtonCell.alternateImage = filterImage.tinted(with: .systemBlue)
}
searchField.focusRingType = .none
searchField.bezelStyle = .roundedBezel
searchField.delegate = self
return searchField
}()
...
}
extension MyViewController: NSSearchFieldDelegate {
func searchFieldDidStartSearching(_ sender: NSSearchField) {
sender.searchable = true
}
func searchFieldDidEndSearching(_ sender: NSSearchField) {
sender.searchable = false
}
}
extension NSSearchField {
var searchButtonCell: NSButtonCell? {
(self.cell as? NSSearchFieldCell)?.searchButtonCell
}
var searchable: Bool {
get {
self.searchButtonCell?.state == .on
}
set {
self.searchButtonCell?.state = newValue ? .on : .off
self.refreshSearchIcon()
}
}
private func refreshSearchIcon() {
NSApp.mainWindow?.resignMain()
NSApp.mainWindow?.becomeMain()
}
}
extension NSImage {
func tinted(with color: NSColor) -> NSImage? {
guard let image = self.copy() as? NSImage else { return nil }
image.lockFocus()
color.set()
NSRect(origin: NSZeroPoint, size: self.size).fill(using: .sourceAtop)
image.unlockFocus()
image.isTemplate = false
return image
}
}
I was having the same issue. A simple override fixed this issue for me
extension NSSearchField{
open override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
}
}
As you can see when you click inside the view it's still focussed on the search text field(as you can still type in it after you clicked underneath it). Since the change image is on when it loses focus, you should check if you clicked outside of the text field.
Solve problem by subclassing NSSearchFieldCell and assign this class to field's cell.
You don't even need to subclass NSSearchFieldCell.
When you create your NSSearchField from code, you can do something like this:
if let searchFieldCell = searchField.cell as? NSSearchFieldCell {
let image = NSImage(named: "YourImageName")
searchFieldCell.searchButtonCell?.image = image
searchFieldCell.searchButtonCell?.alternateImage = image // Optionally
}
If you're using storyboards, you can do the same in didSet of your #IBOutlet.

Automatically delete data from Firebase Database

I have seen some other questions asked but I am having trouble getting it to work. I have a Mac app coded in swift and it has a Firebase login but the user types a key in that is stored on Firebase, is there a way to automatically delete that key when the user has successfully used it?
This is my database.
This is the code that is used currently.
import Cocoa
import FirebaseAuth
import FirebaseDatabase
class LoginViewController: NSViewController {
#IBOutlet weak var textUsername: NSTextField!
#IBOutlet weak var textPassword: NSSecureTextFieldCell!
#IBOutlet weak var btnLogin: NSButton!
var keyArray = \[Int64\]()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear() {
}
func getLoginState() -> Bool{
let state = UserDefaults.standard.bool(forKey: "isRegistered")
if (state) {
return true
} else {
return false
}
}
override func viewDidAppear() {
let state = self.getLoginState()
if (state){
self.performSegue(withIdentifier: NSStoryboardSegue.Identifier(rawValue: "loginsegue"), sender: nil)
self.view.window?.close()
}
var ref: DatabaseReference!
ref = Database.database().reference()
let keyRef = ref.child("key1")
keyRef.observe(DataEventType.childAdded, with: { (snapshot) in
// let postDict = snapshot.value as? \[String : AnyObject\] ?? \[:\]
let keyStr = snapshot.value as? Int64
if let actualPost = keyStr{
self.keyArray.append(actualPost)
}
})
}
#IBAction override func dismissViewController(_ viewController: NSViewController) {
dismiss(self)
}
#IBAction func close(sender: AnyObject) {
self.view.window?.close()
}
#IBAction func onSignup(_ sender: Any) {
// self.performSegue(withIdentifier: NSStoryboardSegue.Identifier(rawValue: "gotosignup"), sender: sender)
// self.view.window?.close()
}
func dialogOK(question: String, text: String) -> Void {
let alert: NSAlert = NSAlert()
alert.messageText = question
alert.informativeText = text
alert.alertStyle = NSAlert.Style.warning
alert.addButton(withTitle: "OK")
alert.runModal()
}
#IBAction func onLogin(_ sender: Any) {
//self.btnLogin.isEnabled = false
var isKey = false
if (!self.textUsername.stringValue.isEmpty) {
for key in keyArray{
if(Int64(self.textUsername.stringValue)! == key)
{
UserDefaults.standard.set(true, forKey:"isRegistered")
self.performSegue(withIdentifier: NSStoryboardSegue.Identifier(rawValue: "loginsegue"), sender: nil)
self.view.window?.close()
isKey = true
}
}
if (!isKey){
self.dialogOK(question: "Error", text: "Invalid Key")
}
} else {
self.dialogOK(question: "Error", text: "Please Input Key")
}
}
}
You can't sort your database like that and expect a working code, even if there's any. It will make a messy code:
You need to:
Sort your database like [1220:0]. the key first. 0 & 1 as an indicator if it's used or not.
Once the user taps onLogin() you need to set the used key value to 1
Setup Cloud Functions to check if the used key is equal to 1, if yes. then remove the key.
Do the rest of the work.
Related Articles to get you started:
Extend Realtime Database with Cloud Functions
functions.database.RefBuilder

save/load TextField after it has been edited

I have this app with a TextField and a Save Button that I want to save when I close after it has been edited and load when I open but I cannot figure it out.
I have written something but when I try to start there comes always the
error: "Fatal 1: Fatal error: Unexpectedly found nil while unwrapping
an Optional value"
This is the code (that is in the my ViewController) but I don't know if its the best way to do it:
#IBOutlet weak var numText: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
if UserDefaults.standard.object(forKey: "num") == nil {
numText.text = "15"
}
else {
let number = UserDefaults.standard.object(forKey: "num") as! String
numText.text = number
}
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func saveButtonPressed(_ sender: UIButton) {
if numText.text == nil {
UserDefaults.standard.set("15", forKey: "num")
}
else {
UserDefaults.standard.set(numText.text, forKey: "num")
}
}
Add the text as a computed Property in the ViewController:
var SAVEDTEXTKEY = "num"
var savedText: String? {
set{
guard let value = newValue else {
UserDefaults.standard.removeObject(forKey: SAVEDTEXTKEY)
UserDefaults.standard.synchronize()
return
}
UserDefaults.standard.set(value, forKey: SAVEDTEXTKEY)
UserDefaults.standard.synchronize()
}
get{
return UserDefaults.standard.string(forKey: SAVEDTEXTKEY)
}
}
After that, when you want to save just do :
self.savedText = self.numText?.text
And when you want to retrieve it just do:
self.numText.text = self.savedText
The problem I think is that you're not calling
UserDefaults.standard.synchronize()
after setting the value in user defaults.

UserDefaults checking for nil value not working

Any idea why my if statement always gives else answer?
I noticed from the print that the object is stored as "optional()", maybe this optional state is different from nil?
class ViewController: UIViewController {
#IBOutlet weak var phoneLabel: UITextField!
#IBOutlet weak var phoneDisplayLabel: UILabel!
#IBAction func storeButton(_ sender: Any) {
UserDefaults.standard.removeObject(forKey: "Nb")
UserDefaults.standard.set("\(phoneLabel.text!)", forKey: "Nb")
print(UserDefaults.standard.object(forKey: "Nb") as Any)
}
#IBAction func retrieveButton(_ sender: Any) {
let phoneNB = UserDefaults.standard.value(forKey: "Nb")
if let phoneNbDisplay = phoneNB as? String {
if UserDefaults.standard.object(forKey: "Nb") != nil {
phoneDisplayLabel.text = "Your NB is \(phoneNbDisplay)"
}
else {
phoneDisplayLabel.text = "enter a number first"
}
}
}
}
The recommended way (by Apple!) is to register key/value pairs in UserDefaults to provide – as the class name implies – default values. Those default values are considered until the value is changed the first time.
The Swift benefit is that there is always a (non-optional) value which can be unwrapped safely.
As soon as possible (e.g. applicationWillFinishLaunching) add these lines. They are supposed to be executed every time the application launches. The key Nb is registered with an empty string default value.
let defaultValues = ["Nb" : ""]
UserDefaults.standard.register(defaults: defaultValues)
In storeButton don't remove the key and don't use String Interpolation for a String but check if the text property is nil and save an empty string in this case. The print line is nonsensical because reading from UserDefaults right after writing will not get the changes and you know what's been written
#IBAction func storeButton(_ sender: Any) {
UserDefaults.standard.set(phoneLabel.text ?? "", forKey: "Nb")
}
In retrieveButton get the string with the dedicated method string(forKey, unwrap it (due to registering the key there is always a value) and only check for empty string. And don't read the (same) value 3 times.
#IBAction func retrieveButton(_ sender: Any) {
let phoneNbDisplay = UserDefaults.standard.string(forKey: "Nb")!
if phoneNbDisplay.isEmpty {
phoneDisplayLabel.text = "enter a number first"
} else {
phoneDisplayLabel.text = "Your NB is \(phoneNbDisplay)"
}
}
As vadian commented, you should not and have no need to use KVC method value(forKey:) in this case.
Try this:
#IBAction func storeButton(_ sender: Any) {
UserDefaults.standard.removeObject(forKey: "Nb")
UserDefaults.standard.set(phoneLabel.text ?? "", forKey: "Nb") //<-You have no need to use String Interpolation.
print (UserDefaults.standard.object(forKey: "Nb") as Any)
}
#IBAction func retrieveButton(_ sender: Any) {
if let phoneNB = UserDefaults.standard.string(forKey: "Nb") {
if !phoneNB.isEmpty {
phoneDisplayLabel.text = "Your NB is \(phoneNB)"
} else {
phoneDisplayLabel.text = "enter a number first"
}
} else {
//### What do you want to do if "Nb" is not set?
}
}
try this
if (UserDefaults.standard.value(forKey: "etc") != nil)
{
if (UserDefaults.standard.value(forKey: "etc") as! String).character.count == 0
{
//value is ""
}else{
//having some value
}
}
You might forget to call synchronize while storing value.You can try this.
#IBAction func storeButton(_ sender: Any) {
UserDefaults.standard.removeObject(forKey: "Nb")
UserDefaults.standard.set("\(phoneLabel.text!)", forKey: "Nb")
UserDefaults.standard.synchronize()
print (UserDefaults.standard.object(forKey: "Nb") as Any)
}
#IBAction func retrieveButton(_ sender: Any) {
if let phoneNB = UserDefaults.standard.string(forKey: "Nb"){
phoneDisplayLabel.text = "Your NB is \(phoneNB)"
}else {
phoneDisplayLabel.text = "enter a number first"
}
}