I am having some trouble using the text in textField. I've copied the relevant code below (and removed some things here and there that shouldn't affect the errors I am getting).
The code builds and runs fine.
I can input text into the textFields (email, password).
But when I try to use the text anywhere, it errors: fatal error: attempt to bridge an implicitly unwrapped optional containing nil on the Alamofire code, specifically the parameters.
Even when I just try to print(email) I get a nil error.
Am I missing something here? I feel like this should be easy but I can't seem to figure it out.
class loginPageViewController: UIViewController, TextFieldDelegate {
private var email: TextField!
private var password: TextField!
func loginButton(sender: RaisedButton!){
let parameters = [
"email" : email,
"password" : password
]
Alamofire.request(.POST, "https://kidgenius.daycareiq.com/api/v1/sessions", parameters: parameters, encoding: .URL)
//other alamofire code not relevant to question
}
override func viewDidLoad() {
super.viewDidLoad()
prepareEmail()
preparePassword()
prepareLoginButton()
}
private func prepareEmail() {
let email : TextField = TextField(frame: 100, 100, 200, 45))
email.delegate = self
email.placeholder = "Email"
//I removed some non relevant code here, just styling stuff
view.addSubview(email)
}
private func preparePassword() {
let password : TextField = TextField(100, 200, 200, 25))
password.secureTextEntry = true
password.delegate = self
password.placeholder = "Password"
//some more removed styling code
view.addSubview(password)
}
private func prepareLoginButton() {
let loginButton : RaisedButton = RaisedButton(frame: 100, 250, 150 , 35))
loginButton.setTitle("Login", forState: .Normal)
loginButton.addTarget(self, action: "loginButton:", forControlEvents: UIControlEvents.TouchUpInside)
//removed some styling code here
view.addSubview(loginButton)
}
}
The issue is you declare your email variable on the top
private var email: TextField!
And then you also declare the email inside the function prepareEmail() again
let email : TextField = TextField(frame: 100, 100, 200, 45))
But your private var email: TextField! never gets instantiated
In order to fix your issue, remove the let when you instantiate the variable inside your prepareEmail()
Related
I'm creating a combo box programmatically and I would like to cause the selection from the drop down menu to call a function.
I can do it if i'm creating a button using - myButton.action = #selector(some_function(_:)), but it doesn't work with an NSComboBox.
here is an example from my code:
func populate_scroller_with_combobox(json_file: Array<Any>, panel: NSView)
{
let combox = NSComboBox()
combox.identifier = NSUserInterfaceItemIdentifier(rawValue: "combobox1")
combox.addItem(withObjectValue: "None")
combox.addItems(withObjectValues: json_file_content)
combox.numberOfVisibleItems = 10
combox.isEditable = false
combox.action = #selector(some_function(_:))
combox.selectItem(withObjectValue: "None")
panel.addSubview(combox)
combox.frame = CGRect(x:190, y: 30, width: 170, height: 26)
}
#objc func some_function(_ sender: NSButton)
{
print ( "Combobox value changed." )
}
It's been a looooonnnnggg time since I've used an NSComboBox.
Reviewing the docs, it seems to follow the data source & delegate pattern that table views use.
You need to have some object conform to the NSComboBoxDelegate protocol and set it as the combo box's delegate. Then your delegate's comboBoxSelectionDidChange(_:) method will be called when the user makes a selection.
You could create a custom NSControl that manages an NSComboBox and serves as the combo box's delegate, and have the control generate an IBAction with an event type of valueChanged when the user selects an item.
here is the solution in case that anyone else run into the same issue:
add to your ViewController Class 'NSComboBoxDelegate'.
to your combobox creation function add 'combox.delegate = self'.
add another function called func 'comboBoxSelectionDidChange(_ notification: Notification)' which will be triggered by the selection changes
class MainViewController: NSViewController, NSComboBoxDelegate
{
override func viewDidLoad()
{
super.viewDidLoad()
}
func populate_scroller_with_combobox(json_file: Array<Any>, panel: NSView)
{
let combox = NSComboBox()
combox.identifier = NSUserInterfaceItemIdentifier(rawValue: "combobox1")
combox.addItem(withObjectValue: "None")
combox.addItems(withObjectValues: json_file_content)
combox.numberOfVisibleItems = 10
combox.isEditable = false
combox.selectItem(withObjectValue: "None")
combox.delegate = self
panel.addSubview(combox)
combox.frame = CGRect(x:190, y: 30, width: 170, height: 26)
}
func comboBoxSelectionDidChange(_ notification: Notification)
{
print("Combobox value changed.")
}
}
I try to automatically fill in a login formula and confirm data by clicking the login button. I would like to use Swift 5, macOS 10.15 and a WKWebView.
The first step is done, I can fill in my login data like this:
import Cocoa
import WebKit
class ViewController: NSViewController, WKUIDelegate, WKNavigationDelegate {
let webView = WKWebView()
let button = NSButton()
override func viewDidLoad() {
super.viewDidLoad()
button.frame = CGRect(x: 400, y: 0, width: 40, height: 20)
button.image = NSImage(named: NSImage.Name("Button"))
button.target = self
button.action = #selector(self.fillIn)
self.view.addSubview(button)
self.webView.uiDelegate = self
self.webView.navigationDelegate = self
webView.frame = CGRect(x: 0, y: 0, width: 400, height: 270)
view.addSubview(webView)
let url = URL(string: "https://lobby.ogame.gameforge.com/de_DE/hub")
let request = URLRequest(url: url!)
webView.load(request)
}
#objc func fillIn() {
setWebViewValue(name: "email", data: "user#web.de")
setWebViewValue(name: "password", data: "pwdpwd")
}
func setWebViewValue(name: String, data: String) {
let jsFunc = "document.getElementsByName(\"\(name)\")[0].value = \"\(data)\""
webView.evaluateJavaScript(jsFunc) {(result, error) in
print(result, error)
}
}
}
But when I click the login button, all data will be removed. So the email and password input fields are empty while logging in and I get an invalid input data error.
Example
Please note, even if the email address is wrong, the address and password input fields are not cleared when clicking to the login button.
Question:
Why is the auto-filled data deleted when you click the login button and why is the data that was entered normally not deleted?
Check the javascript code on the page to see if it is clearing the fields on it's own or maybe it was waiting for typing into the fields.
I want a picture to move to the bottom. If I press a button the pic should move down by 1.
I added the picture and a button:
var corX = 0
var corY = 0
var runter: UIButton = UIButton.buttonWithType(UIButtonType.System) as UIButton
var image = UIImage(named: "panzerBlau.jpg");
var panzer = UIImageView(frame: CGRectMake(corX, corY, 30, 40)); //
override func viewDidLoad() {
super.viewDidLoad()
panzer.image = image; //
self.view.addSubview(panzer); //
runter.frame = CGRectMake(100, 30, 10 , 10)
runter.backgroundColor = UIColor.redColor()
view.addSubview(runter)
runter.addTarget(self, action: "fahren", forControlEvents:UIControlEvents.TouchUpInside)
}
At least I said in function "fahren" to move the picture down by 1.
func fahren(){
corY += 1
panzer.frame = CGRectMake(corX, corY, 30, 40) //
self.view.addSubview(panzer);
}
So my problem is: I get several errors with these corX and corY thing. Without them it works perfectly but than its like a single-use button. The errors are: ViewController.Type does not have a member named corX and ViewController.Type does not have a member names panzer Where I get the errors I made // to show in which lines.
PS: I use Xcode Beta5
Here's the complete code without anything else:
import UIKit
class ViewController: UIViewController {
var corX = 0
var corY = 0
var runter: UIButton = UIButton.buttonWithType(UIButtonType.System) as UIButton
var image = UIImage(named: "panzerBlau.jpg");
var panzer = UIImageView(frame: CGRectMake(corX, corY, 30, 40));
override func viewDidLoad() {
super.viewDidLoad()
panzer.image = image;
self.view.addSubview(panzer);
runter.frame = CGRectMake(100, 30, 10 , 10)
runter.backgroundColor = UIColor.redColor()
view.addSubview(runter)
runter.addTarget(self, action: "fahren", forControlEvents:UIControlEvents.TouchUpInside)
}
func fahren(){
corY += 100
panzer.frame = CGRectMake(corX, corY, 30, 40)
self.view.addSubview(panzer);
}
}
#MartinR has pointed out the major issue here:
var corX = 0
var corY = 0
var panzer = UIImageView(frame: CGRectMake(corX, corY, 30, 40))
The problem is that a Swift default initializer cannot refer to the value of another property, because at the time of initialization, the property doesn't exist yet (because the instance itself doesn't exist yet). Basically, in panzer's default initializer you are implicitly referring to self.corX and self.corY - but there is no self because self is exactly what we are in the middle of creating.
One workaround is to make the initializer lazy:
class ViewController: UIViewController {
var corX : CGFloat = 0
var corY : CGFloat = 0
lazy var panzer : UIImageView = UIImageView(frame: CGRectMake(self.corX, self.corY, 30, 40))
// ...
}
That's legal because panzer doesn't get initialized until later, when it is first referred to by your actual code. By that time, self and its properties exist.
Your dependent property needs to be:
lazy
Have an explicit : Type
Use self. to access other properties
Example:
let original = "foo"
// Good:
lazy var depend: String = self.original
// Error:
var noLazy: String = self.original // Error: Value of type '(NSObject) -> () -> URLData' has no member 'original'
lazy var noType = self.original // Error: Value of type '(NSObject) -> () -> URLData' has no member 'original'
lazy var noSelf: String = original // Error: Instance member 'original' cannot be used on type 'YourClass'
I'm addressing the title of the question:
Both lazy and computed properties help you deal with when the initial value for a property is not known until after the object is initialized. But there are some differences. I've highlighted the differences with bold.
If you simply need to initialize a variable after some other variable(s) is initialized then you should use lazy ie if the point is to simply add a delay (so all required properties get initialized before) then using lazy is the right way to go for it.
But if you need to constantly change a variable based on another, then you need a computed property that would work both ways:
if the computed property set then it sets the variables its related stored properties
if the stored properties are set (or are reset again) then it will trigger a change in then computed property.
if you change the lazy property's value it won't affect the storied properties that it was based on. see here
A good example for using a lazy property would be that once you have firstName & lastName then you would lazily instantiate a fullName and likely you would never change the firstName lastName of your object your fullName is a onetime only...
Or perhaps something that can only be done by lazy properties is that up until you don't access the property it won't ever get initialized, therefore this would decrease the initialization load of your class. Loading a heavy calculation.
Additionally using the lazy will signal to other developers: "Hey first go read about the other properties and understand what they are...then come to this lazy property...since the value of this is based on them + this is likely a heavy computation that shouldn't be accessed too early..."
As for computed property a good example would be if you set the temperature to Fahrenheit then you also want your celsius temperature to change its value...and if you set the celsius temperature then again you want to change your Fahrenheit value.
As a result computed property would add extra computation...and if your computation is very simple and isn't called too frequently then it's nothing to worry about but if it get's called too often or is very CPU-consuming then it might be better to think of other options...
//
// ViewController.swift
//
// Created by Shivank Agarwal on 19/05/18.
// Copyright © 2018 Shivank Agarwal. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
var corX = 0
var corY = 0
var runter: UIButton = UIButton()
var image = UIImage(named: "panzerBlau.jpg")
var panzer = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
panzer.image = image;
self.view.addSubview(panzer);
panzer.frame = CGRect(x: CGFloat(corX), y: CGFloat(corY), width: 30, height: 40)
runter.backgroundColor = UIColor.red
view.addSubview(runter)
view.addSubview(panzer)
runter.addTarget(self, action: Selector(("fahren")), for:UIControlEvents.touchUpInside)
}
private func fahren(){
corY += 100
}
private func updatePanzerFrame(){
panzer.frame = CGRect(x: CGFloat(corX), y: CGFloat(corY), width: 30, height: 40)
}
}
Note: Do not add panzer imageView every time when user tap only add it on viewDidLoad()
I want a picture to move to the bottom. If I press a button the pic should move down by 1.
I added the picture and a button:
var corX = 0
var corY = 0
var runter: UIButton = UIButton.buttonWithType(UIButtonType.System) as UIButton
var image = UIImage(named: "panzerBlau.jpg");
var panzer = UIImageView(frame: CGRectMake(corX, corY, 30, 40)); //
override func viewDidLoad() {
super.viewDidLoad()
panzer.image = image; //
self.view.addSubview(panzer); //
runter.frame = CGRectMake(100, 30, 10 , 10)
runter.backgroundColor = UIColor.redColor()
view.addSubview(runter)
runter.addTarget(self, action: "fahren", forControlEvents:UIControlEvents.TouchUpInside)
}
At least I said in function "fahren" to move the picture down by 1.
func fahren(){
corY += 1
panzer.frame = CGRectMake(corX, corY, 30, 40) //
self.view.addSubview(panzer);
}
So my problem is: I get several errors with these corX and corY thing. Without them it works perfectly but than its like a single-use button. The errors are: ViewController.Type does not have a member named corX and ViewController.Type does not have a member names panzer Where I get the errors I made // to show in which lines.
PS: I use Xcode Beta5
Here's the complete code without anything else:
import UIKit
class ViewController: UIViewController {
var corX = 0
var corY = 0
var runter: UIButton = UIButton.buttonWithType(UIButtonType.System) as UIButton
var image = UIImage(named: "panzerBlau.jpg");
var panzer = UIImageView(frame: CGRectMake(corX, corY, 30, 40));
override func viewDidLoad() {
super.viewDidLoad()
panzer.image = image;
self.view.addSubview(panzer);
runter.frame = CGRectMake(100, 30, 10 , 10)
runter.backgroundColor = UIColor.redColor()
view.addSubview(runter)
runter.addTarget(self, action: "fahren", forControlEvents:UIControlEvents.TouchUpInside)
}
func fahren(){
corY += 100
panzer.frame = CGRectMake(corX, corY, 30, 40)
self.view.addSubview(panzer);
}
}
#MartinR has pointed out the major issue here:
var corX = 0
var corY = 0
var panzer = UIImageView(frame: CGRectMake(corX, corY, 30, 40))
The problem is that a Swift default initializer cannot refer to the value of another property, because at the time of initialization, the property doesn't exist yet (because the instance itself doesn't exist yet). Basically, in panzer's default initializer you are implicitly referring to self.corX and self.corY - but there is no self because self is exactly what we are in the middle of creating.
One workaround is to make the initializer lazy:
class ViewController: UIViewController {
var corX : CGFloat = 0
var corY : CGFloat = 0
lazy var panzer : UIImageView = UIImageView(frame: CGRectMake(self.corX, self.corY, 30, 40))
// ...
}
That's legal because panzer doesn't get initialized until later, when it is first referred to by your actual code. By that time, self and its properties exist.
Your dependent property needs to be:
lazy
Have an explicit : Type
Use self. to access other properties
Example:
let original = "foo"
// Good:
lazy var depend: String = self.original
// Error:
var noLazy: String = self.original // Error: Value of type '(NSObject) -> () -> URLData' has no member 'original'
lazy var noType = self.original // Error: Value of type '(NSObject) -> () -> URLData' has no member 'original'
lazy var noSelf: String = original // Error: Instance member 'original' cannot be used on type 'YourClass'
I'm addressing the title of the question:
Both lazy and computed properties help you deal with when the initial value for a property is not known until after the object is initialized. But there are some differences. I've highlighted the differences with bold.
If you simply need to initialize a variable after some other variable(s) is initialized then you should use lazy ie if the point is to simply add a delay (so all required properties get initialized before) then using lazy is the right way to go for it.
But if you need to constantly change a variable based on another, then you need a computed property that would work both ways:
if the computed property set then it sets the variables its related stored properties
if the stored properties are set (or are reset again) then it will trigger a change in then computed property.
if you change the lazy property's value it won't affect the storied properties that it was based on. see here
A good example for using a lazy property would be that once you have firstName & lastName then you would lazily instantiate a fullName and likely you would never change the firstName lastName of your object your fullName is a onetime only...
Or perhaps something that can only be done by lazy properties is that up until you don't access the property it won't ever get initialized, therefore this would decrease the initialization load of your class. Loading a heavy calculation.
Additionally using the lazy will signal to other developers: "Hey first go read about the other properties and understand what they are...then come to this lazy property...since the value of this is based on them + this is likely a heavy computation that shouldn't be accessed too early..."
As for computed property a good example would be if you set the temperature to Fahrenheit then you also want your celsius temperature to change its value...and if you set the celsius temperature then again you want to change your Fahrenheit value.
As a result computed property would add extra computation...and if your computation is very simple and isn't called too frequently then it's nothing to worry about but if it get's called too often or is very CPU-consuming then it might be better to think of other options...
//
// ViewController.swift
//
// Created by Shivank Agarwal on 19/05/18.
// Copyright © 2018 Shivank Agarwal. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
var corX = 0
var corY = 0
var runter: UIButton = UIButton()
var image = UIImage(named: "panzerBlau.jpg")
var panzer = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
panzer.image = image;
self.view.addSubview(panzer);
panzer.frame = CGRect(x: CGFloat(corX), y: CGFloat(corY), width: 30, height: 40)
runter.backgroundColor = UIColor.red
view.addSubview(runter)
view.addSubview(panzer)
runter.addTarget(self, action: Selector(("fahren")), for:UIControlEvents.touchUpInside)
}
private func fahren(){
corY += 100
}
private func updatePanzerFrame(){
panzer.frame = CGRect(x: CGFloat(corX), y: CGFloat(corY), width: 30, height: 40)
}
}
Note: Do not add panzer imageView every time when user tap only add it on viewDidLoad()
I have found this answer How to check text field input at real time?
This is what I am looking for. However I am having trouble actually implementing this code. Also my current geographical location makes googling almost impossible.
I want to be able to change the background color of the next text field if the correct number is entered into the previous text field. textfieldTwo background color will change to green if the correct value is entered in textFieldOne. If the value is incorrect then nothing will happen. Please help me out. I have two text fields called textFieldOne and textFieldTwo and nothing else in the code.
Just pop this in your main view controller in an empty project (try using iphone 6 on the simulator)
import UIKit
class ViewController: UIViewController {
var txtField:UITextField!
var txtFieldTwo:UITextField!
var rightNumber = 10
override func viewDidLoad() {
super.viewDidLoad()
//txtFieldOne
var txtField = UITextField()
txtField.frame = CGRectMake(100, 100, 200, 40)
txtField.borderStyle = UITextBorderStyle.None
txtField.backgroundColor = UIColor.blueColor()
txtField.layer.cornerRadius = 5
self.view.addSubview(txtField)
//txtFieldTwo
var txtFieldTwo = UITextField()
txtFieldTwo.frame = CGRectMake(100, 150, 200, 40)
txtFieldTwo.borderStyle = UITextBorderStyle.None
txtFieldTwo.backgroundColor = UIColor.blueColor()
txtFieldTwo.layer.cornerRadius = 5
self.view.addSubview(txtFieldTwo)
txtField.addTarget(self, action: "checkForRightNumber", forControlEvents: UIControlEvents.AllEditingEvents)
self.txtField = txtField
self.txtFieldTwo = txtFieldTwo
}
func checkForRightNumber() {
let number:Int? = self.txtField.text.toInt()
if number == rightNumber {
self.txtFieldTwo.backgroundColor = UIColor.greenColor()
} else {
self.txtFieldTwo.backgroundColor = UIColor.blueColor()
}
}
}
EDIT: Adding a version with IBOutlets and IBActions
Note that in this example the IBAction is connected to txtFieldOne on Sent Events / Editing Changed
Also, make sure your Text Fields border colors are set to None. In the storyboard, the way to do this is to choose the left most option with the dashed border around it. That's so you can color the backgrounds. You can use layer.cornerRadius to set the roundness of the border's edges.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var txtField: UITextField!
#IBOutlet weak var txtFieldTwo: UITextField!
var rightNumber = 10
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func checkForRightNumber(sender: AnyObject) {
let number:Int? = self.txtField.text.toInt()
if number == rightNumber {
self.txtFieldTwo.backgroundColor = UIColor.greenColor()
} else {
self.txtFieldTwo.backgroundColor = UIColor.blueColor()
}
}
}