Cannot unwrap optional value to label - ios

I am new to this board. Please, excuse my bad english in advance.
I am trying to send a string from a subview to his parent view. If I try to set that string to a label, my app crashes with the message "unexpectedly found nil while unwrapping an Optional value".
Example code from the subview:
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.blueColor()
sendDataToVc("test")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func sendDataToVc(myString : String) {
let Vc = ViewController()
Vc.dataFromContainer(myString)
}
Example from the parent view:
class ViewController: UIViewController {
#IBOutlet weak var label1: UILabel!
var cacheStr1 : String!
var cacheStr2 : String?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
label1.text = ""
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func dataFromContainer(containerData : String){
label1.text = cacheStr1
}
#IBAction func changeLabel(sender: AnyObject) {
}
I have no more ideas what I am doing wrong. Thank you for your help.

The problem is this line:
let Vc = ViewController()
You are creating a new instance — a new ViewController instance. That's not what you want to do. You want to get a reference to an existing instance — the one that is your view controller's parent view controller, if that's what a View Controller is in relation to your TableViewController.

You better instance your ViewController form StoryBoard and define what you want to pass as property, and then set this property to the value that you need to show, and in the viewDidLoad of your ViewController update your view as you need

Related

UIscrollview delegation

I am trying to pass data between my two view controllers in my UIscrollview. I am trying to use delegation to send data between Viewcontroller1 and Viewcontroller2. The delegate is Viewcontroller, while the delegator is Viewcontroller1 and Viewcontroller2.
In the code posted below, when the switch in Viewcontroller1 is toggled, it makes the switch in Viewcontroller2 put to the "off" state. I keep on getting the
fatal error: unexpectedly found nil while unwrapping an Optional value
error when I run it, but I have no clue what is causing this problem. Any ideas why?
Below is the Viewcontroller that contains the Uiscrollview and the subviews/childviews
import UIKit
class ViewController: UIViewController, testing {
var vc1 = ViewController1(nibName: "ViewController1", bundle: nil)
var vc2 = ViewController2(nibName: "ViewController2", bundle: nil)
#IBOutlet weak var scrollView: UIScrollView!
func test1() {
vc2.switch2.on = false
}
override func viewDidLoad() {
super.viewDidLoad()
self.addChildViewController(vc1)
self.scrollView.addSubview(vc1.view)
vc1.didMoveToParentViewController(self)
var frame1 = vc2.view.frame
frame1.origin.x = self.view.frame.size.width
vc2.view.frame = frame1
self.addChildViewController(vc2)
self.scrollView.addSubview(vc2.view)
vc2.didMoveToParentViewController(self)
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width * 2, self.view.frame.size.height);
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
here is the Viewcontoller1 code
protocol testing{
func test1()
}
class ViewController1: UIViewController {
var delegate:testing?
#IBOutlet weak var switch1: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
let vc = ViewController()
self.delegate = vc
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func switch1toggled(sender: AnyObject) {
delegate?.test1()
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
and here is the Viewcontroller 2 code
import UIKit
class ViewController2: UIViewController {
#IBOutlet weak var switch2: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func switch2toggled(sender: AnyObject) {
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
sorry for the long post, I have been stuck for a week on how to change the state of another switch from toggling a switch in another class, and this was the most efficient way that I found
Try This:
ViewController1
class ViewController1: UIViewController {
let defaults = NSUserDefaults.standardUserDefaults()
let switch1Key = "view1Switch"
override func viewWillAppear(animated: Bool) {
view1Switch.on = defaults.boolForKey(switch1Key)
}
#IBOutlet weak var view1Switch: UISwitch!
#IBAction func view1SwitchChanged(sender: UISwitch) {
defaults.setBool(view1Switch.on, forKey: switch1Key)
}
}
ViewController2
class ViewController2: UIViewController {
let defaults = NSUserDefaults.standardUserDefaults()
let switch1Key = "view1Switch"
override func viewWillAppear(animated: Bool) {
view2Switch.on = defaults.boolForKey(switch1Key)
}
#IBOutlet weak var view2Switch: UISwitch!
#IBAction func view2SwitchChanged(sender: UISwitch) {
defaults.setBool(view2Switch.on, forKey: switch1Key)
}
}
This method syncs the state of the two UISwitches using viewWillAppear and NSUserdefaults. The basic thought pattern is that you save the state of the switch to NSUserdefaults so that when either ViewController1 or ViewController2 is instantiated the view1Switch or view2Switch outlet's .on property is set to the saved value.
Caveats:
The first value for the switch when ViewController1 is instantiated (in the first app run) will be off because boolForKey returns false when there is no saved value. This can be hacked by using view1Switch.on = true directly after view1Switch.on = defaults.boolForKey(switch1Key)
This method makes the switches have the same value. In order to make them have different values, you can use a ! operator like so in ViewController2 view2Switch.on = !defaults.boolForKey(switch1Key). This way switch 1 will always be the opposite of switch 2.
I recommend this method over delegation because, while delegation is powerful, its power doesn't seem needed here.
If you have any questions please ask! :D

Container view losing textField values when a button is tapped in the parent viewController

I have a viewController with a UISegmentedControl and a UIButton.
Within this viewController, I have two containers, each containing one viewController with a UITextField inside.
I want to save the values in the textField on the click of the button.
Here's the code I have written so far:
View Controller:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//
//
containerA.showView()
containerB.hideView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func buttonTapped(sender: UIButton) {
print(ContainerAViewController.sharedInstance.textFieldA)
}
#IBAction func segmentedControlValueChanged(sender: AnyObject) {
switch(sender.selectedSegmentIndex) {
case 0 : containerA.showView()
containerB.hideView()
case 1 : containerB.showView()
containerA.hideView()
default : containerA.showView()
containerB.hideView()
}
}
#IBOutlet weak var containerA: UIView!
#IBOutlet weak var containerB: UIView!
func hideView(view: UIView) {
view.userInteractionEnabled = false
view.hidden = true
}
func showView(view: UIView) {
view.userInteractionEnabled = true
view.hidden = false
}
}
extension UIView {
func hideView() -> UIView {
self.userInteractionEnabled = false
self.hidden = true
return self
}
func showView() -> UIView {
self.userInteractionEnabled = true
self.hidden = false
return self
}
}
ContainerAViewController:
class ContainerAViewController: UIViewController {
#IBOutlet weak var textFieldA: UITextField!
static let sharedInstance = ContainerAViewController()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
ContainerBViewController:
class ContainerBViewController: UIViewController {
#IBOutlet weak var textFieldB: UITextField!
static let sharedInstance = ContainerBViewController()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
When I tap the button, it gives me the following error:
fatal error: unexpectedly found nil while unwrapping an Optional value
Can somebody please help?
You should not try to manipulate another view controller's views. That violates the principle of encapsulation, an important principle in object-oriented development.
You should give your child view controllers (ContainerAViewController and ContainerBViewController) string properties, and have the code for those view controllers set that string property when the user enters text into the view controllers' text fields.
Ignoring that design flaw, your code doesn't make sense. You show your CLASS as ContainerAViewController, and yet your buttonTapped method is trying to ask a ContainerAViewController singleton for the value of a text field. That makes no sense.
You want to have properties in your parent view controller that point to your child view controllers.
You should implement a prepareForSegue method in your parent view controller, and in that prepareForSegue method, look for the embed segues that fire when the child view controllers are loaded. When that happens you should set your properties that point to the child view controllers.

Swift - Set a UI element from outside

I have this UIViewController
import UIKit
class UIViewController1: UIViewController {
#IBOutlet weak var someTitle: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
And I am trying to set someTitle when I instantiate it from another view controller:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewDidAppear(animated: Bool) {
let stb = UIStoryboard(name: "Main", bundle: nil)
let vc1 = stb.instantiateViewControllerWithIdentifier("someSTBID") as! UIViewController1
vc1.someTitle.text = "My Title" // it fails here!!!!!
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The reason it fails at the line above is that I was trying to force unwrapping a nil optional, which is someTitle.
Please show me a way to set someTitle in this situation.
Your second UIViewController hasn't been loaded yet, so the someTitle IBOutlet will be nil. You got two options:
The easy one: you force the load of the second UIViewController, for example: vc1.view is enough and then you set it (I don't recommend this)
The proper one: you let the second UIViewController be responsible for setting its own title at the right time. If you need to pass the "My title", you can simply pass it via a function like configureVc(title: String), or by exposing a variable like var title: String, so on viewDidLoad of the second UIViewController you would someTitle.text = title.
Do you need it to go into the superclass viewDidAppear, because it should work if you put it into the superclass viewDidLoad(). I hope that helps you and future viewers.

found nil while unwrapping optional

I am trying to create a simple application that consists of one Navigation controller with 2 screens controlled by a single UIViewController class. The app will allow the user to enter a number from 0 to 10 in a text field and when the user presses a button they will be taken to the 2nd screen showing if they guessed the randomly generated number. I get the "unexpectedly found nil while unwrapping an Optional value" error when I am accessing the text property of the label in the second screen. I dont uderstand why, I have connected the label with the class. Any suggetions?
This is my UI:
This is my Navigation controller class code:
class MyNavController: UINavigationController {
var ranNum:Int = 0
override func viewDidLoad() {
super.viewDidLoad()
ranNum = (Int)(arc4random_uniform(10))
NSLog("random number: \(ranNum)")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
This is my Generic View controller class:
class GenericViewController: UIViewController {
#IBOutlet weak var inputTextField: UITextField!
#IBOutlet weak var outputLabel: UILabel!
var setThisLabel: String = "You Win!"
#IBAction func guessTheNumber(sender: AnyObject) {
var generatedRanNumber = (parentViewController as! MyNavController).ranNum
var userGuessNumer = inputTextField.text.toInt()
if generatedRanNumber == userGuessNumer {
outputLabel.text = "You Win!"
} else if generatedRanNumber < userGuessNumer {
outputLabel.text = "Think Less..."
} else {
outputLabel.text = "Think Big..."
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
change from
var generatedRanNumber = (parentViewController as! MyNavController).ranNum
to
var generatedRanNumber = (navigationController as! MyNavController).ranNum
also notice that arc4random_uniform(10) returns a number between 0 and 9. you tell the user to guess a number between 0 and 10.
Actually the problem is you are pushing from one instance of Generic View Controller to another without passing on the data. It is generally not recommended to use a generic subclass of UIViewController in this way. If you want your code to work as is, place the UILabel in the same view as your other UI and don't segue (i.e. only have one instance of GenericViewController). Or create two subclasses of UIViewController one called "GuessViewController" and the other called "AnswerViewController" and pass the guess value between them in prepareForSegue. GuessViewController would handle getting the string from the user and evaluate it. AnswerViewController would whether it's right or wrong. It is also generally not necessary to subclass UINavigationController. You could place that code in the viewDidLoad of GuessViewController.

how to acces a variable in another class and change its value

I am new to Xcode and Swift so I don't know much about how it all works, but I am trying to make a pop-up view. I want a small view to pop up when I click a button. The view is a View Container (I don't know if that is the best way to do this so if not please tell me a better way to do this) and it starts out hidden then when I click a button it becomes visible. This View Container also has a button that if clicked, it will make the view hidden again.
Here is the code:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBOutlet weak var popUpView: UIView!
#IBAction func startButton(sender: UIButton) {
popUpView.hidden = false
}
}
import UIKit
class PopUpViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepareForSegue(segue:UIStoryboardSegue,
sender:AnyObject?)
{
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
#IBAction func backButton(sender: UIButton) {
ViewController().popUpView.hidden = true
}
}
When I run the app it starts fine because the start button is there and when I click it the pop up shows up but when I click the back button it gives me an error which says that in the console
Unknown class MKMapView in Interface Builder file.
fatal error: unexpectedly found nil while unwrapping an Optional value
and in line 31 ViewControler().popUpView.hidden = true
it says Thread 1: EXC_BAD_INSTRUCTION(code=EXC_I386_INVOP, subcode=0x0)
Can someone help. Thanks
Access popUpView variable from didPrepareForSeque method (this method gets called automatically when you segue to another view). Problem is that if you try to set value to soon (meaning, that button is not drawn on view), you will get nil error. Here is a little workaround. You use temporary variable (tmpValue) to store state of your button (to be hidden or not), so when viewDidLoad, you method will read this value and set button to hidden state as you intended.
In ViewController class declare temporary variable (must be optional):
var tmpValu:Bool?
Then in your PopUpViewController class remove this line from backButton action:
ViewController().popUpView.hidden = true
Instead, you will use prepareForSegue method, like this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destinationViewController = segue.destinationViewController as! ViewController
destinationViewController.tmpValu = true
}
Now, back in ViewController class in viewDidLoad add this code:
override func viewDidLoad() {
super.viewDidLoad()
if let value = tmpValu {
popUpView.hidden = value
}
}

Resources