Change a parameter from a single class to all other classes - swift

I am having a problem that I cant seem to figure it out, I am trying to change the language of the application through sender buttons but I cant change the parameter of the language from the signInViewController to other VC's I am getting an error.
In other classes I created a static let shared = resetPasswordViewController and then call it at the signInViewController to change the labels buttons etc but I am getting a nil error at labels and buttons when its trying to change the language
signInViewController:
class signInViewController: UIViewController {
#IBOutlet weak var welcomeLabelSignIn: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
}
#IBAction func changeLanguageToAlbanian(_ sender: Any) {
localizeSignIn()
resetPasswordViewController.shared.localizeResetPassword()
}
#IBAction func changeLanguageToSerbian(_ sender: Any) {
localizeSignIn1()
resetPasswordViewController.shared.localizeResetPassword()
}
#IBAction func changeLanguageToEnglish(_ sender: Any) {
localizeSignIn2()
resetPasswordViewController.shared.localizeResetPassword()
}
func localizeSignIn() {
welcomeLabelSignIn.text = NSLocalizedString("Welcome!", tableName: nil, bundle: changeLanguage.createBundlePath(lang: "sq" ), value: "", comment: "")
}
resetPasswordViewController:
class resetPasswordViewController: UIViewController {
static let shared = resetPasswordViewController()
#IBOutlet weak var enterEmailLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
localizeResetPassword()
}
func localizeResetPassword() {
enterEmailLabel.text = NSLocalizedString("Welcome!", tableName: nil, bundle: changeLanguage.createBundlePath(lang: "sq" ), value: "", comment: "")
}
Now you see that the (lang: "") is being called as static, I need to make a global one that when the client press the button to change the language it will replace that "" in every other class and then change the language.
I hope I was clear about the problem, for any questions feel free to comment please.

If you want to make a singleton LangHandler you should seperate it into another class so you can make reusable code without typing again
class LangHanlder
class LangHandler {
static let shared = LangHandler()
var lang: String
//Make default lang
private init(){
lang = "sq"
}
func changeLang(_ langChange: String){
lang = langChange
}
}
Have localized extension
extension String {
func localized(_ lang:String) ->String {
let path = Bundle.main.path(forResource: lang, ofType: "lproj")
let bundle = Bundle(path: path!)
return NSLocalizedString(self, tableName: nil, bundle: bundle!, value: "", comment: "")
}
}
How to use
When you called for the first time you always have default value
signInViewController:
class signInViewController: UIViewController {
#IBOutlet weak var welcomeLabelSignIn: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
}
#IBAction func changeLanguageToAlbanian(_ sender: Any) {
localizeSignIn()
}
#IBAction func changeLanguageToSerbian(_ sender: Any) {
// change localized handler language only
LangHandler.shared.changeLang("sr")
localizeSignIn()
}
#IBAction func changeLanguageToEnglish(_ sender: Any) {
// change localized handler language only
LangHandler.shared.changeLang("en")
localizeSignIn()
}
func localizeSignIn() {
welcomeLabelSignIn.text = "Welcome!".localized(LangHandler.shared.lang)
}
resetPasswordViewController:
class resetPasswordViewController: UIViewController {
#IBOutlet weak var enterEmailLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
localizeResetPassword()
}
func localizeResetPassword() {
// default is sq
enterEmailLabel.text = "Welcome!".localized(LangHandler.shared.lang)
}

Related

#IBOutlet property cannot have non-object type

I am trying to pass a struct between view controllers, but I get the compiler error "#IBOutlet property cannot have non-object type". I have tried to add #objc but still get the error. How can I pass this data between view controllers? Why do I get this error and how do I correct it? Thanks.
import UIKit
struct DocObject: Codable {
let filename: String
let doclink: Int
}
class ViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
// var nameText = ""
var obj = DocObject(filename: "filename", doclink: 123)
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func done(_ sender: Any) {
print ("In VC1 nameText ", obj)
performSegue(withIdentifier: "name", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! VCTwo2
vc.finalObj = obj
}
}
import UIKit
class VCTwo2: UIViewController {
#IBOutlet var finalObj: DocObject!
// var finalName = 0
override func viewDidLoad() {
super.viewDidLoad()
print ("In VC2 ", finalObj!)
}
}
You only define a property with #IBoutlet if the property is loaded from a xib or storyboard. Since you are passing the data structure from another view controller just define finalObj as a regular var.
class VCTwo2: UIViewController {
var finalObj: DocObject!
override func viewDidLoad() {
super.viewDidLoad()
}
}

Two windows are presented when I start my xib only project

I am using Xcode 10.1 two create a simple single window application. The issue is that I see two windows instantiated when I run it instead of the single one I expect. Here is my code
//AppDelegate
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
var mainWindowController: WindowController!
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
print("loading")
let mainWindowController = WindowController()
mainWindowController.showWindow(self)
self.mainWindowController = mainWindowController
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
print("closed")
}
}
//WindowController.swift
import Cocoa
class WindowController: NSWindowController, NSSpeechSynthesizerDelegate {
#IBOutlet weak var textField: NSTextField!
#IBOutlet weak var speakButton: NSButton!
#IBOutlet weak var stopButton: NSButton!
override var windowNibName: String {
return "Window"
}
let speechSynth = NSSpeechSynthesizer()
var isStarted: Bool = false {
didSet {
updateButtons()
}
}
override func windowDidLoad() {
super.windowDidLoad()
viewDidLoad()
}
func viewDidLoad() {
// super.viewDidLoad()
updateButtons()
speechSynth.delegate = self
print("view load")
// Do any additional setup after loading the view.
}
#IBAction func speakIt(sender: NSButton) {
let string = textField.stringValue
if string.isEmpty {
print("String is empty")
}
else {
print(string)
speechSynth.startSpeaking(string)
isStarted = true
}
}
#IBAction func stopIt(sender: NSButton) {
speechSynth.stopSpeaking()
isStarted = false
}
func updateButtons() {
if isStarted {
speakButton.isEnabled = false
stopButton.isEnabled = true
} else {
stopButton.isEnabled = false
speakButton.isEnabled = true
}
}
func speechSynthesizer(_ sender: NSSpeechSynthesizer, didFinishSpeaking finishedSpeaking: Bool) {
isStarted = false
}
}
My Xib has the option checked "Visible at launch". If I uncheck it, I see no window.
Basically, I am attempting a program from the book "Cocoa Programming for OSX". However, the book is obsolete. But I am trying to find my way through new Xcode and Swift. Any help?
Also a point worth noting is that only one window is functional, the second window IBOutlets and IBAction are not connected. So nothing happens in the second window.
I can't figure out why is this happening?

How to use file.swift into a swift created app(project)?

I have a task to create an app that checks if a String is a palindrome or not. And also I have instructed to create a file and in that file should be a function. But what I can't figure out is how to make a button run that function that is in the file.
func isPalidrone(phrase: String) -> Bool {
let isPalidrone = true
let reversed = String(phrase.reversed())
if reversed == phrase {
return isPalidrone
} else {
return !isPalidrone
}
}
class ViewController: UIViewController {
#IBOutlet weak var Label: UILabel!
#IBOutlet weak var textFieldOutlet: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buttonPressed(_ sender: Any) {
}
}
Like this:
#IBAction func buttonPressed(_ sender: Any) {
let pal = isPalidrone(phrase: self.textFieldOutlet.text!)
// now pal is true or false and you can decide what else to do ...
// for example:
self.Label.text = pal ? "It is a palindrome" : "It is not a palindrome"
}

Custom keyboard is crashing the app - Swift

I'm doing a test of a custom keyboard. This is what I need:
It has to have two UITextFields. Cannot be labels.
The keyboard is an embedded UIView.
The default keyboard should be disabled.
It cannot be a keyboard extension.
Not sure why the app is crashing. PS: Not all the keys are on the code yet. Here is an image of what I'm trying to do and the two View Controllers.
Edit: The error is: Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
First ViewController:
import UIKit
class HomeVC: UIViewController, ButtonTapDelegate {
#IBOutlet var textField1: UITextField!
#IBOutlet var textField2: UITextField!
#IBOutlet var keyboardView: UIView!
var buttonPressed = [String]()
override func viewDidLoad() {
addKeyboard(view: keyboardView)
buttonPressed = [String]()
textField1.inputView = UIView()
textField2.inputView = UIView()
}
func addKeyboard(view: UIView) {
let keyboard = KeyboardVC(nibName: "KeyboardVC", bundle: nil)
view.addSubview(keyboard.view)
addChild(keyboard)
}
func didTapButton(sender: UIButton) {
if sender.tag == 5 {
textField1.text?.append(contentsOf: " ")
} else if sender.tag == 6 {
textField1.text?.removeAll()
buttonPressed = [String]()
} else {
let val = sender.titleLabel?.text
textField1.text?.append(contentsOf: val!)
}
self.textField1.text = buttonPressed.joined(separator: "")
}
}
Here is the second View Controller:
import UIKit
protocol ButtonTapDelegate {
func didTapButton(sender: UIButton)
}
class KeyboardVC: UIViewController {
var delegate: ButtonTapDelegate!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buttons(_ sender: UIButton) {
delegate.didTapButton(sender: sender)
print(sender)
}
}
var delegate: ButtonTapDelegate!
An implicitly unwrapped optional is essentially a promise that you're definitely going to give the variable a value before you try to access it. The problem in this case is that you haven't done that. Most likely, you want to do this in your first view controller:
func addKeyboard(view: UIView) {
let keyboard = KeyboardVC(nibName: "KeyboardVC", bundle: nil)
keyboard.delegate = self // Now "delegate" will have a value before the function gets called
view.addSubview(keyboard.view)
addChild(keyboard)
}

Pass data between ViewController and ContainerViewController

I'm working on an app, and need to pass data between view and containerView. I need to send data and receive data from both Views.
Let me explain better:
I can change the Label Master (Touch the Container Button) by protocol, but I can not change the Label Container (Touch the Master button). What happens is the Master connects with the container by a following. But do not have a follow Container linking to the Master.
I tried to add but segue to, but it worked.
The Master View Controller:
import UIKit
protocol MasterToContainer {
func changeLabel(text:String)
}
class Master: UIViewController, ContainerToMaster {
#IBOutlet var containerView: UIView!
var masterToContainer:MasterToContainer?
#IBOutlet var labelMaster: UILabel!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "containerViewSegue" {
let view = segue.destinationViewController as? Container
view!.containerToMaster = self
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func button_Container(sender: AnyObject) {
masterToContainer?.changeLabel("Nice! It's work!")
}
func changeLabel(text: String) {
labelMaster.text = text
}
}
The Container View Controller:
import UIKit
protocol ContainerToMaster {
func changeLabel(text:String)
}
class Container: UIViewController, MasterToContainer {
var containerToMaster:ContainerToMaster?
#IBOutlet var labelContainer: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func button_Master(sender: AnyObject) {
containerToMaster?.changeLabel("Amazing! It's work!")
}
func changeLabel(text: String) {
labelContainer.text = text
}
}
Can someone help me?
All you need to do is keep a reference to Container in your master view controller.
That is, you should add an instance variable to Master that will hold a reference to the view controller, not just the view. You'll need to set it in prepareForSegue.
So the beginning of Master View Controller would look something like this:
class Master: UIViewController, ContainerToMaster {
#IBOutlet var containerView: UIView!
var containerViewController: Container?
#IBOutlet var labelMaster: UILabel!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "containerViewSegue" {
containerViewController = segue.destinationViewController as? Container
containerViewController!.containerToMaster = self
}
}
And then in your button function, simply change the label using the variable you just added.
Example:
#IBAction func button_Container(sender: AnyObject) {
containerViewController?.changeLabel("Nice! It's work!")
}
This means you can get rid of your MasterToContainer protocol too.
I tested this code, so I know it works, but unfortunately I am an Objective-C dev, and know nothing about best practices in Swift. So I don't know if this is the best way to go about it, but it certainly works.
Edit:
Here's the exact code I've tested:
Master.swift:
import UIKit
class Master: UIViewController, ContainerToMaster {
#IBOutlet var containerView: UIView!
#IBOutlet var labelMaster: UILabel!
var containerViewController: Container?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "containerViewSegue" {
containerViewController = segue.destinationViewController as? Container
containerViewController!.containerToMaster = self
}
}
#IBAction func button_Container(sender: AnyObject) {
containerViewController?.changeLabel("Nice! It's work!")
}
func changeLabel(text: String) {
labelMaster.text = text
}
}
Container.swift:
import UIKit
protocol ContainerToMaster {
func changeLabel(text:String)
}
class Container: UIViewController {
#IBOutlet var labelContainer: UILabel!
var containerToMaster:ContainerToMaster?
#IBAction func button_Master(sender: AnyObject) {
containerToMaster?.changeLabel("Amazing! It's work!")
}
func changeLabel(text: String) {
labelContainer.text = text
}
}
I solved it with this code
To send data from ViewController -> ContainerViewController
Class ViewController : UIViewController {
func sendData(MyStringToSend : String) {
let CVC = childViewControllers.last as! ContainerViewController
CVC.ChangeLabel( MyStringToSend)
}
}
in your ContainerViewController
Class ContainerViewController : UIViewController {
#IBOutlet weak var myLabel: UILabel!
func ChangeLabel(labelToChange : String){
myLabel.text = labelToChange
}
}
To send data from ContainerViewController -> ViewController
Class ContainerViewController : UIViewController {
func sendDataToVc(myString : String) {
let Vc = parentViewController as! ViewController
Vc.dataFromContainer(myString)
}
}
and in ViewController
Class ViewController : UIViewController {
func dataFromContainer(containerData : String){
print(containerData)
}
}
I hope this will help someone.
you can use this extension to access the container child
extension UIViewController {
func getContainerChild<vc:UIViewController>(_ viewController : vc,_ hasNavigation : Bool = true) -> (vc) {
guard let vc = self.children[0] as? UINavigationController else {return viewController}
if hasNavigation {
guard let childVC = vc.children[0] as? PurchasedHistoryListVC else {
return viewController}
return childVC as! vc
} else {
return vc as! vc
}
}
}
so you can do some thing like this in your view Controller
let vc = self.getContainerChild(yourChildViewControllerClass())
vc.functionName()