Nil value when referring to IBOutlets - swift, Cocoa - swift

I am having an issue where my NSTextField IBOutlets are showing up as nil, even though they are connected to the storyboard. In the simplified example below, I have a button that, when pressed, should list the string value of the 3 text labels.
Here is the code for the button:
import Foundation
import Cocoa
class ViewController: NSObject{
#IBAction func pushButton(sender: AnyObject) {
let oneText = Texts()
oneText.listTextFields()
}
}
Here is the code for the NSTextField list:
import Foundation
import Cocoa
class Texts: NSObject{
#IBOutlet weak var l1: NSTextField!
#IBOutlet weak var l2: NSTextField!
#IBOutlet weak var l3: NSTextField!
var textArray = [NSTextField]()
func listTextFields (){
self.textArray = [self.l1,self.l2,self.l3]
for var i = 0; i < textArray.count; i++ {
let text = textArray[i]
print(text.stringValue)
}
}
}
I have verified that the IBOutlets are all connected, but I get a "fatal error: unexpectedly found nil while unwrapping an Optional value" message when I run the program and press the button. Looking at the debugger, it appears that the tree NSTextfields are "nil."
What am I doing wrong?

You're not loading Texts from your storyboard, so it knows nothing about your outlets. Texts() creates a new instance of the object, which you then call the method on.
You presumably have an existing Texts object somewhere in interface builder, ViewController should have an outlet to that.

Related

Swift - Can't access struct properties

Learning swift but a little confused as to the below. I have struct created and instantiated, but I cannot access the property of the struct inside of my view controller class unless the struct is inside one my my class methods. See below, why would this be the case?
class WeatherViewController: UIViewController, UITextFieldDelegate, WeatherManagerDelegate {
//create new weather manager struct
var weatherManager = WeatherManager()
//can't access property here, but I can access it inside of functions within this
//class, see below under viewDidLoad()
weatherManager.delegate = self
#IBOutlet weak var conditionImageView: UIImageView!
#IBOutlet weak var temperatureLabel: UILabel!
#IBOutlet weak var cityLabel: UILabel!
#IBOutlet weak var searchTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
//can access property here
weatherManager.delegate = self
}
The problem isn't where the WeatherManager object is declared and created. The problem is that this line:
weatherManager.delegate = self
is a command (technically a statement), not a declaration. (The line above it is a declaration, one that happens to also set the weatherManager property's default value). This is a pretty universal rule in most languages in the C++/Java family -- see short C++ example below. A command (statement) must be inside some method (or function, in non-OOP programming), not at the top level of a file or class. In Swift, actions like setting an object's delegate would typically go in the view controller's viewDidLoad.
int x = 0; // legal: declaring a global variable
x = x + 42; // NOT legal: this is a statement, not a declaraiton
int main()
{
x = x + 42; // legal: now we're inside a function
return 0;
}

Why array's append method cannot be used in viewController?

I am beginner of swift. I tried to use array's append method in my code but it doesn't work. How should I implement the array correctly?
The error messages:
Swift Compiler Error Group
ViewController.swift:16:5: Expected declaration
ViewController.swift:11:7: In declaration of 'ViewController'
I tried to use array's append method in my code but it doesn't work.
import UIKit
class ViewController: UIViewController { //Error msg: In declaration of 'ViewController'
#IBOutlet weak var dice: UIImageView!
#IBOutlet weak var dice2: UIImageView!
var dices : [String] = []
dices.append("Hi") //Error: Expected declaration
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func rollPressed(_ sender: UIButton) {
dice.image = UIImage(named: "dice3")
}
}
I expect I can add "hi" into the array dices.
You should call the append inside a function after the vc is fully initated
class ViewController: UIViewController { //Error msg: In declaration of 'ViewController'
#IBOutlet weak var dice: UIImageView!
#IBOutlet weak var dice2: UIImageView!
var dices : [String] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
dices.append("Hi") // e.x here
}
#IBAction func rollPressed(_ sender: UIButton) {
dice.image = UIImage(named: "dice3")
}
}
Or replace
var dices : [String] = []
with
var dices = ["Hi"]
SH_Khan is right. I'll explain why though.
When defining a class, the first level of indentation is only for its methods and properties, aka func, var, and let. (You can also define other classes/structs/enums in there too)
Calling those functions or system functions like Array.append() or print("dog sweat") must happen inside of another function. The reason why is that your application's live logic is literally just functions all the way down. No function gets called unless it's inside of another function first. (The only exceptions are Swift's quick and dirty initializations like setting a default value to a var outside of an init() { } or another function.)
A dog doesn't wake up from its nap unless you make some noise. It won't do it on its own. (crappy metaphor, but yeah)
I hope that made any sense.

Variable in label Name - Swift

Is it possible to use a variable in a label name.
For example, my label is called button1text and I have a variable var x = 1.
Is there a way to do thisbutton(x)text?
No. You can not have variable name in identifier.
Variable names are evaluated at compile time, so no, it's not possible (at runtime).
Alternatively use an array or assign tags to the labels and get the label with viewWithTag
You can use reflection with Mirror. The capabilities are very limited in the context you want but you can try something like:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var button1: UIButton!
#IBOutlet weak var button2: UIButton!
#IBOutlet weak var button3: UIButton!
#IBAction func didTapGoButton(_ sender: Any) {
let mirror = Mirror(reflecting: self)
for child in mirror.children {
if let v = child.label, v == "button2" {
(child.value as! UIButton).titleLabel?.text = "changed"
}
}
}
}

RxSwift: upgrade model state when textfield gets focus

I have a UIViewController with 3 UITextFields. Every time either of the fields gets focus, I want to set a new text value for a tip label above. What is the best way to achieve this with RxSwift?
This does what you're looking for. Any time a UITextField would send a textFieldDidBeginEditing: delegate message, you instead get an Observable for that. Then you map that Observable into the correct string for that text field. Then combine all 3 of those Observables into just one, where the latest event is from the text field that most recently called that delegate message. Then you bind that value to the tooltip's text.
import UIKit
import RxSwift
import RxCocoa
class ViewController: UIViewController {
let disposeBag = DisposeBag()
#IBOutlet weak var tooltip: UILabel!
#IBOutlet weak var textField1: UITextField!
#IBOutlet weak var textField2: UITextField!
#IBOutlet weak var textField3: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
let textField1Text = textField1.rx_controlEvent(.EditingDidBegin)
.map { "text field 1 message" }
let textField2Text = textField2.rx_controlEvent(.EditingDidBegin)
.map { "text field 2 message" }
let textField3Text = textField3.rx_controlEvent(.EditingDidBegin)
.map { "text field 3 message" }
Observable.of(textField1Text, textField2Text, textField3Text).merge()
.bindTo(tooltip.rx_text)
.addDisposableTo(disposeBag)
}
}

Reference non-initiated variable at top of file

this is a bit odd to say but essentially here is my code below:
import UIKit
class BarcodeScanPopover: UIViewController, UIPopoverPresentationControllerDelegate {
#IBOutlet weak var navbar: UINavigationItem!
#IBOutlet weak var product: UILabel!
#IBOutlet weak var productimage: UIImageView!
#IBOutlet weak var scanner: UIView!
var scan: MTBBarcodeScanner = MTBBarcodeScanner(previewView: scanner);
override func viewDidLoad() {
So the issue I'm having is I can't declare "scan" without initiating the MTBBarcodeScanner object, but at the same time, I can't initiate the MTBBarcodeScanner object without calling "scanner" which is not possible at the top of the file. Unfortunately MTBBarcodeScanner() is not a valid init and causes crashes so that is not possible either.
I need to do this because I need to access "scan" at different points in the code - not just in one code method.
Any suggestions?
If you are sure you will always have an instance of the MTBBarcodeScanner after the view loaded, declare it as MTBBarcodeScanner!: var scan: MTBBarcodeScanner!. That makes it an implicitly unwrapped optional, which is allowed to be nil unless you try to access some property/function on it.
You should therefore then make sure that you always assign something to it before ever accessing it in any other way. That can and should be done in viewDidLoad:
scan = MTBBarcodeScanner(previewView: scanner)
You can let your MTBBarCodeScanner instance be an optional, and initially set it to nil.
var scan: MTBBarcodeScanner? = nil
Thereafter call your initializer to update its value as soon as scanner instance is available to you (e.g. in viewDidLoad).