unwrap optional value nil - ios

I am new in ios developer.I pass my data to one view controller to another using function
var vc = MessageViewController()
vc.printSomething(address: address,id: id)
I pass address and id to MessageViewController.In MessageViewController
func printSomething(address: String,id: String) {
print("address",address)
print("id",id)
address_text.text = "\(address)"
}
Above code I print address,id but when i set address on UITextField address.text I got unwrapping error.

I used to create a class method to instantiate view controller from storyboard like this...
class MessageViewController: UIViewController {
//other methods
//MARK:- Class Method
class func viewController() -> MessageViewController {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "<Storyboard Id>") as! MessageViewController
}
}
You need to just use to instantiate view controller
var vc = MessageViewController.viewController()
You can set storyboard identifier like in below image.

Hi it is the issue with variable not assigned to address_text beacuse xib not initialized at that time of your method(printSomething) calling, before that take two varibles for saving the address and id in MessageViewController and assign id and address to those variables in printSomething method and you can assing those values to address_text in viewdidload method of MessageViewController

Related

Error passing value to view controller on dismiss | Swift 5

Error transferring value from one view controller to another on dismiss.
I have a value in ViewController2 that I would like to transfer to ViewController1 on dismiss. In view ViewController2 I do the following to dismiss:
func OtherFunction() {
passAndDismiss(scannedScript: stringCodeValue)
}
Note: stringCode value is indeed being passed as a String to scannedScript
func passAndDismiss(scannedScript: String) {
dismiss(animated: true, completion: {
let viewcontroller = ViewController1()
viewcontroller.Textfield1.text = scannedScript
})
}
The error occurs on line:
viewcontroller.Textfield1.text = scannedScript
The error I get is:
Unexpectedly found nil while implicitly unwrapping an Optional value
Two fatal issues:
ViewController1() is a new instance of the controller which is not the instance in the storyboard.
Solution: You need the real instance either with a segue or with instantiation from the storyboard.
Even if ViewController1() was the expected controller the outlets are not connected right after the initialization.
Solution: You have to declare a temporary property for the string in ViewController1 and assign the value to the label in viewDidLoad
Here you are initialising viewcontroller with wrong way. You should load from xib or storyboard.
In your way not initialise xib or storyboards’ ui element objects. Here textfield object nil and you are trying to access text property of that. That’s why app crashed. You should use following way:
let myViewController = MyViewController(nibName: "MyViewController", bundle: nil)
OR
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "someViewController")
If you are not using xib or storyboard then please initialise textfield object in that controller’s init method

Instance Member Cannot Be Used on Type when pushing UIViewController

I am programmatically setting up ViewControllers (no storyboard).
I want to pass data to the next VC, and while I know how to do that with a segue and a storyboard, I can't figure out how to do it purely programmatically.
I get the error "Instance Member Cannot Be Used on Type..."
// Create Next View Controller Variable
let nextViewController = CarbonCalculatorResultsViewController()
// Pass data to next view controller. There is already a variable in that file: var userInformation: UserInformation?
CarbonCalculatorResultsViewController.userInformation = userInformation
// Push next View Controller
self.navigationController?.pushViewController(nextViewController, animated: true)
Do I need to instantiate the next VC before I can pass data? That's what this answer seems to talk about yet I don't have a Storyboard. Thanks!
Step 1: Setup your destination class
In CarbonCalculatorResultsViewController class, declare a var to receive data like so:
class CarbonCalculatorResultsViewController: UIViewController {
var foo: String? {
didSet {
// What you'd like to do with the data received
print(foo ?? "")
}
}
ovevride func viewDidLoad() {
//
}
}
Step 2: Prepare data in your source class
let nextViewController = CarbonCalculatorResultsViewController()
// You have access of the variable in CarbonCalculatorResultsViewController
nextViewController.foo = <data_you_want_to_pass>
// Push next View Controller
self.navigationController?.pushViewController(nextViewController, animated: true)
Then, every time the CarbonCalculatorResultsViewController comes alive, the didSet{} of foo would be called.
The current sample code (above) is setting a value to a static variable (owned by the CarbonCalculatorResultsViewController.Type.
What I believe you want to implement is the following instead:
// Create Next View Controller Variable
let nextViewController = CarbonCalculatorResultsViewController()
// Pass data to next view controller. There is already a variable in that file: var userInformation: UserInformation?
nextViewController.userInformation = userInformation
// Push next View Controller
self.navigationController?.pushViewController(nextViewController, animated: true)
This sample code is setting a value to the instance variable userInformation on the type nextViewController.
You should pass the variable on the Object, not on the Class
Replace: CarbonCalculatorResultsViewController.userInformation = userInformation
With:
nextViewController.userInformation = userInformation
Note:
CarbonCalculatorResultsViewController is the Class.
nextViewController is the Object.
Your full code should look like this:
// Create Next View Controller Variable
let nextViewController = CarbonCalculatorResultsViewController()
// Pass data to next view controller. There is already a variable in that file: var userInformation: UserInformation?
nextViewController.userInformation = userInformation
// Push next View Controller
self.navigationController?.pushViewController(nextViewController, animated: true)

Navigation controller and remembering values

I have 2 view controller with navigation controller. First view controller has 4 text field and second view controller has 4 text field. To navigate first view controller to second I am using following code:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
var destinationVC:UIViewController
destinationVC = storyboard.instantiateViewController(withIdentifier: "SecondVC") as! SecondVC
navigationController?.show(destinationVC, sender: self)
To first from second view controller I am using
navigationController?.popViewController(animated: true)
However, even if the fields I have filled in first view controller keep the values when I go from first to second values I have written have disappear because of popviewcontroller method. What is the best way to remember values in second view controller?
You can have singleton where you can store the values as dictionary(or something else)
class Settings: NSObject {
static let shared = Settings()
public var dictionaryToStore: [String: String]?
private init() {
super.init()
}
}
And in your controller when poping
Settings.shared.dictionaryToStore = {"key1": textfield1.text, "key2": textfield2.text, ...
}
And in viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
textfield1.text = Settings.shared.dictionaryToStore["key1"]
textfield2.text = Settings.shared.dictionaryToStore["key2"]
...
}
Also you can create custom object and store it.
EDIT 1 **
To have variables after app has been terminated you can save dictionary in UserDefaults
class Settings: NSObject {
static let shared = Settings()
public var dictionaryToStore: [String: String]? {
set(newValue) {
let defaults = UserDefaults.standard
defaults.set(newValue, forKey: "SomeKey")
}
get {
let defaults = UserDefaults.standard
let loadedValue = defaults.object(forKey: "SomeKey")
return loadedKey
}
}
private init() {
super.init()
}
}
The key reason for values not to be remembered on the SecondVC is that you're using new instances of SecondVC each time you open it.
So you better create an instance (first 3 lines of your code do that job) of SecondVC once, somewhere in the beginning of FirstVC, and use it in show() func everytime you need to show SecondVC instead of creating multiple instance of SecondVC each time.
In that case you'll see all values "remembered" in the SecondVC.
You can use key-value storage like NSUserDefaults in your secondViewController to save data when viewController will disappear and load on viewDidLoad. Or save your data in some struct/object instance and pass it when you push secondViewController.
if you want to pass data from you first view controller to second view
how to pass data from first viewcontroller to second viewcontroller
in above code though you are making new instance of you view controller still you can pass data by setting variable of second view controller in first view controller before
navigationController?.show(destinationVC, sender: self)
like
destnationVC.variableTOSet = valueTopass
and then
navigationController?.show(destinationVC, sender: self)
and then in second view controller use that variable to use value
so that how you can pass data from your first controller to second view controller
now if you want to pass data from your second viewController to first controller then you can use delegates

How would I have one view controller access a variable from another view controller?

Everything I've seen on stack is passing the data from an input, onto another view controller on a button press. Let's say I have var banana that is an array of dictionaries, but once my function in ViewA.swift is done loading up banana, I want another viewController, say ViewB.swift to manipulate that data as it sees fit. I do NOT have a segue going from one view controller to the other.
EDIT: It's actually two TableViewControllers****
I've looked into NSNotificationCenter, but that doesn't seem to work with my variable type, which is an array of dictionaries
Use NSNotificationCenter for accessing data.
Try Below code
//Sent notification
let dictionary = ["key":"value"]
NSNotificationCenter.defaultCenter().postNotificationName("passData", object: nil, userInfo: dictionary)
//Receive notification
NSNotificationCenter.defaultCenter().addObserver(self,
selector:"myMethod:", name: "passData", object: nil)
//Receive notification method
func myMethod(notification: NSNotification){
print("data: \(notification.userInfo!["key"])")
Without using segue, you can instantiate the View controller, and set the public parameteres.
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("someViewController") as! ViewB
/* Here you have the reference to the view, so you can set the parameters*/
vc.parameterInViewB = banana
/* At this point you can present the view to the user.*/
self.presentViewController(vc, animated: true, completion: nil)
Make sure that you have given all ViewControllers an identifier, then instantiate them with:
guard let viewControllerB = storyboard?.instantiateViewControllerWithIdentifier("ViewControllerB") as? ViewControllerB else {
fatalError(); return
}
// then access the variable/property of ViewControllerB
viewControllerB.banana = whatEver
Added for clarification
This one works for me.
Just make sure that you have given the TableViewController an identifier otherwise you will not be able to instantiate it. Also make sure that you cast the result of instantiateViewControllerWithIdentifier to your TableViewController class otherwise you won't be able to access it's variables (I've seen that you were struggling with this; if you get an error that UIViewController doesn't have a member "myArray" then you probably have forgotten to cast the result)
class TableViewController: UITableViewController {
var myArray = [String]()
}
class ViewController: UIViewController {
func someEventWillTriggerThisFunction() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let tableViewController = storyboard.instantiateViewControllerWithIdentifier("TableViewController") as? TableViewController else {
fatalError(); return
}
tableViewController.myArray = ["Value1", "Value2", "Value3"]
/* if you want to present the ViewController use this: */
self.presentViewController(tableViewController, animated: true, completion: nil)
}
}

Loading a instance of a class dynamically in swift

I would like to init an instance of a class specified at runtime. I have the following set up.
//MyClass1 & MyClass2 inherit from UIViewController
let arr = MyClass1.self,MyClass2.self]
func didSelectRow(index:Int) {
var controllerRef = arr[index] //This returns a .Type
var controllerClass = controllerRef() //This returns “the class” MyClass1
//This is where I'm stuck. How do I initialize controller??
//Finally, push VC
self.navigationController?.pushViewController(controller, animated: true)
}
To initialize the VC with Objective C, I was able to do this as follows:
UIViewController *controller = [((UIViewController *)[controllerClass alloc]) initWithNibName:class1Nib bundle:nil];
With Swift, I’m expecting something like the syntax below, but it doesn’t work
//Doesn’t work
var controller = (controllerClass as UIViewController)(nibName: class1Nib, bundle:nil)
//Overriding the init method in MyClass1 and then calling the below doesn’t work
var controller = controllerClass(nibName: class1Nib, bundle:nil)
//The below works, but viewDidLoad() does not get called. It only loads the nib file
var controller = UIViewController(nibName: Class1Nib, bundle:nil)
Any help is appreciated. Thanks
Your question, with its repeated and meaningless "doesn't work" and "the below", is confused and confusing, so I will respond by assuming that what you want to do is instantiate and push a view controller given its class, and I will simply demonstrate in a few lines of code that this is possible.
Here is a function that returns a UIViewController subclass:
func provideVCClass() -> UIViewController.Type {
return ViewController.self
}
And here are lines from a button's action handler where we call that function and use the class to instantiate that view controller and push it onto the current navigation controller:
let vcclass = provideVCClass()
let vc = vcclass(nibName: "View", bundle: nil)
self.navigationController?.pushViewController(vc, animated: true)
I assure you that this does work: the correct view controller subclass is instantiated, its nib is loaded, and its view does appear in the interface.
So all I can say in answer to your question, which I don't fully understand, is Go Ye And Do Likewise.
EDIT Here's a little postscript. If your code actually says:
let arr = [MyClass1.self, MyClass2.self]
I would suggest you try saying
let arr : [UIViewController] = [MyClass1.self, MyClass2.self]
...just in case Swift is not inferring the correct type here.

Resources