I have some outlets in my view and I try to edit them programatically in a function.
Xcode says:
unexpectedly found nil while unwrapping an optional value
However, when I edit outlets in viewDidLoad() just after super.viewDidLoad(), it works like a charm.
With the function :
func test(){
localDeviceNameView.stringValue = "some stuff" //Found nil here
}
With viewDidLoad :
override func viewDidLoad() {
super.viewDidLoad()
localDeviceNameView.stringValue = "some stuff" //Works well
}
How can I make it work?
You can use didSet on your variable. Like this :
#IBOutlet var localDeviceNameView : UIView! {
didSet {
localDeviceNameView.stringValue = "some stuff"
}
}
What's good with this approach is that you set your stringValue only when the localDeviceNameView is set, which is right when it has been loaded from the NIB/Storyboard.
I think this is exactly what you need.
You are trying to call test() before views are completely loaded, so they are not exist yet. Do it this way:
class MyController: UIViewController {
var something = ""
override viewDidLoad() {
super.viewDidLoad()
localDeviceNameView.stringValue = something
}
}
Or call you'r test() method only if you sure than all views are loaded (viewDidLoad() was called)
Related
I am getting nil value error when I try to change UIlabel from a different swift file other than ViewController.
//ViewController.swift
import UIKit
class MainViewController: UIViewController {
#IBOutlet weak var myLabel: UILabel?
override func viewDidLoad() {
super.viewDidLoad()
myLabel?.text = "foo"
}
func label(stringVal: String? = nil){
myLabel?.text = stringVal!
}
}
//Source.swift
//Do working here and get a string value from struct
// I saved that string in stringValue
let VC = MainViewController()
VC.label(stringVal: stringValue!)
Please let me know what am I doing wrong.
View controller controls are not created in constructor, it is expected to be null. You need to show that view controller before to run label method and avoid the error.
A valid option would be to set an internal variable in the constructor and in viewDidLoad, when the label is already created, you could do
myLabel.text = stringValue // stringValue is comes from constructor.
I'm making a calculator app that solves for the missing variable and I've added a piece of code that dismisses the keyboard by tapping anywhere on the screen or pressing the "return" key.
This function was working before I added the if else statements to check which variable was missing (using .isNaN) so that it could solve for that missing variable.
Here is my code for that specific view controller:
import UIKit
class findSpeed: UIViewController, UITextFieldDelegate {
#IBOutlet weak var vValue: UITextField!
#IBOutlet weak var dValue: UITextField!
#IBOutlet weak var tValue: UITextField!
#IBOutlet weak var answer: UILabel!
#IBAction func solve(_ sender: UIButton) {
let v = Float(vValue.text! as String);
let d = Float(dValue.text! as String);
let t = Float(tValue.text! as String);
let solvev = (d!/t!);
let solved = (v!*t!);
let solvet = (d!/v!);
if (v!.isNaN) {
answer.text = "Answer: \(solvev)"
}
else if (d!.isNaN) {
answer.text = "Answer: \(solved)"
}
else {
answer.text = "Answer: \(solvet)"
}
}
override func viewDidLoad() {
super.viewDidLoad()
vValue.delegate = self;
dValue.delegate = self;
tValue.delegate = self;
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: Selector("dismiss")))
}
func dismiss() {
vValue.resignFirstResponder()
dValue.resignFirstResponder()
tValue.resignFirstResponder()
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
vValue.resignFirstResponder()
dValue.resignFirstResponder()
tValue.resignFirstResponder()
return true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
The error I'm receiving occurs when I tap the button that links to this view controller where the calculator is:
Picture of error
Use the debugger and see why the app is crashing. Clearly vValve is nil which means you didn't connect anything to the outlet in Interface Builder.
Fix your outlet connections and the error posted in your question will be fixed.
You also need to eliminate all of the forced unwrapping you are doing. Otherwise you are going to have a lot more crashes to deal with. I strongly urge you to review What does "fatal error: unexpectedly found nil while unwrapping an Optional value" mean?
I have the following Protocol:
protocol SoundEventDelegate{
func eventStarted(text:String)
}
which I call in this class:
class SoundEvent {
var text:String
var duration:Double
init(text: String, duration: Double){
self.text = text
self.duration = duration
}
var delegate : SoundEventDelegate?
func startEvent(){
delegate?.eventStarted(self.text)
}
func getDuration() -> Double{
return self.duration //TODO is this common practice?
}
}
Which I have my ViewController conform to:
class ViewController: UIViewController, SoundEventDelegate {
//MARK:Properties
#IBOutlet weak var beginButton: UIButton!
#IBOutlet weak var kleinGrossLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//DELEGATE method
func eventStarted(text:String){
kleinGrossLabel.text = text
}
//MARK: actions
#IBAction func startImprovisation(sender: UIButton) {
var s1:Sentence = Sentence(type: "S3")
var s2:Sentence = Sentence(type: "S1")
var newModel = SentenceMarkov(Ult: s1, Penult: s2)
s1.start()
beginButton.hidden = true
}
}
But when I run the app kleinGrossLabel.text does not change. Am I referring to the label in the wrong way? Or is it the way that I do delegation that is incorrect?
Here are links to the complete Class definitions of Sentence and SentenceMarkov
https://gist.github.com/anonymous/9757d0ff00a4df7a29cb - Sentence
https://gist.github.com/anonymous/91d5d6a59b0c69cba915 - SentenceMarkov
You never set the delegate property. It's nil. It will never be called.
First off it's not common practice to have a setter in swift. if you want to have a readonly property you can use private(set) var propertyName
in other cases simply access the property like mentioned in the comment
Also i don't see a reason why you eventArray in sentence is of type [SoundEvent?] not [SoundEvent] as SoundEventdoes not seem to have a failable initialiser
Like mentioned before you need to not only implement the SoundEventDelegate protocol but also set the delegate
the problem is that you can't really access the SoundEventDelegate from the viewcontroller because you instantiate the SoundEvents inside Sentence
var soundEventDelegate: SoundEventDelegate?
the easiest way to do this would be adding a soundEventDelegate property for sentence and setting it like this:
let s1:Sentence = Sentence(type: "S3")
let s2:Sentence = Sentence(type: "S1")
s1.soundEventDelegate = self
s2.soundEventDelegate = self
and inside sound you would need the set the delegate for every event to the soundEventDelegate of Sentence
you could do it like this:
var soundEventDelegate: SoundEventDelegate? = nil {
didSet {
eventArray.forEach({$0.delegate = soundEventDelegate})
}
}
or write another initialiser that takes the delegate
hope this helps
p.s: you shouldn't inherit form NSObject in swift excepts it's really necessary
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.
In my application I have a textbox that should be filled with a Double and the number should be saved into a variable but there's an error.
I dragged and dropped the textbox into ViewController.swift so it should be linked. I created a #IBOutlet. I called the textbox mmolText and the variable mmol.
I tried something like: var mmol = mmolText.text but it shows an error:
'ViewController.Type' does not have a member named 'mmolText'.
What's the problem? How can I solve it? Besides the type of the content of the textbox is a string but I should convert it into Double.
Here the code of ViewController.swift is:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var mmolText: UITextField!
var mmol = mmolText.text
#IBOutlet weak var mmolLabel: UILabel!
#IBOutlet weak var mgLabel: UILabel!
#IBAction func convertBM(sender: AnyObject) {
}
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.
}
}
It seems like we probably simply want mmol to exist as a convenient way for getting the text property out of the mmolText textfield, right? So why not use a computed property:
var mmol: String {
get {
return mmolText.text ?? ""
}
set {
mmolText.text = newValue
}
}
The get makes use of the nil coalescing operator. UITextField's text property hasn't been updated with the Objective-C nullability annotations yet, so we need to handle the case of it potentially returning nil.
If we want this to be readonly, we can simply omit the set part.
If we want this as a Double, we can modify the above computed property to look more like this:
var mmol: Double {
get {
return ((mmolText.text ?? "0") as NSString).doubleValue
}
set {
mmolText.text = String("%f", newValue)
}
}
And again, if we want this to be readonly, we can simply omit the set half. And of course, the format string can be played around with to get the string version of the double to show up exactly as you intend when using this set method.
class ViewController: UIViewController {
#IBOutlet weak var mmolText: UITextField!
var mmol: String!
override func viewDidLoad() {
super.viewDidLoad()
mmol = mmolText.text
}
}
This way it works. I can remember something like because at that stage, the properties can exist. Which means, it can be there or it isn't. That's why you can't do it like that.
Don't pin me on this explanation though, I'm not very sure.
mmolText is a property on self. You can't refer to it there because self has not been initialized yet.
You'll have to assign it within awakeFromNib, viewDidLoad, etc.