Store input number inside a variable from a textbox - ios

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.

Related

Value of type '[UILabel]' has no member 'text'

// ViewController.swift
// FunFacts
//
// Created by Moises Miguel Hernandez on 11/27/17.
// Copyright © 2017 Moises Miguel Hernandez. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
#IBOutlet var funFactLabel: [UILabel]!
override func viewDidLoad() {
super.viewDidLoad()
funFactLabel.text = "An Interesting Fact"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}//end of recive memory warning
}//end of viewController
I am using Xcode and Swift 4. I am just starting out and taking a course from team treehouse.
In the video tutorial they easily did the funFactLabel.text = "Some Fun fact"
When I try to do that it gives me this error:
Value of type '[UILabel]' has no member 'text'
I don't think I am doing anything wrong. I just want to change the text of the label to say: "Some Fun Fact".
Your problem is that [UILabel] is an array of UILabels.
Everything in braces [] is an array. So you could have an array of strings: [String]
Just change the type to a plain UILabel like so:
#IBOutlet var funFactLabel: UILabel!
You declared funFactLabel as an array of labels.
replace the line
#IBOutlet var funFactLabel: [UILabel]!
by
#IBOutlet var funFactLabel: UILabel!
Your funFactLabel is an array of UILabel.
Either do this :
#IBOutlet var funFactLabel: UILabel!
Or this but for this you have to take an array and you have to append your labes in it. You can't make an IBOutlet as an array:
override func viewDidLoad() {
super.viewDidLoad()
for label in funFactLabel{
label.text = "An Interesting Fact"
}
}
But I why an array of label as IBOutlet ? Never saw someone doing that. For each label take one IBOutlet.
Right click on label and Drag Referencing Outlets always, 2nd one. Ignore Course Detail, that's just text for my UILabel.
It seems like you have made an OutletCollection If you made an OutletCollection it will always an array try to choose only Outlet.
See here:
OutletCollection
#IBOutlet var funFactLabel: [UILabel]!
Outlet
#IBOutlet var funFactLabel: UILabel!

Cannot pass immutable value of type 'NSObject' as inout argument

This should work, but I've got no clue why it doesn't. The code is self-explanatory.
class Themer {
class func applyTheme(_ object: inout NSObject) {
//do theming
}
}
And I apply theme to the button like so:
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
Themer.applyTheme(&button)
}
The button object is a variable, yet the compiler throws an error.
Since button is an object, this syntax
Themer.applyTheme(&button)
means that you want to change the reference to that object. But this is not what you want. You want to change the referenced object so you simply need to write
Themer.applyTheme(button)
Finally you also don't need the inout annotation
class Themer {
class func applyTheme(_ object: AnyObject) {
//do theming
}
}
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
Themer.applyTheme(self.button)
}
}
But...
However, what should your applyTheme method do? It receives AnyObject and then what? You could make it a little but more specific and use a UIView as param
class Themer {
class func applyTheme(view: UIView) {
//do theming
}
}
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
Themer.applyTheme(view: button)
}
}
Now you have a chance to write meaningful code inside Themer.applyTheme.
inout is for the case that you want to change the reference, that is replace one object with another object. That's a very, very, very bad thing to do with an IBOutlet. That button is used in a view, connected up to lots of things, and if you change the variable, all hell will break lose.
Apart from that, listen to appzYourLife.

Swift: Delegation protocol not setting UILabel properly

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

#IB Action function inside of viewdidload in Swift

Is it possible to have an #IB Action function inside of viewDidLoad() ?
The action is a simple one - a Stepper that increases other label.text values accordingly. However, the values that the stepper needs to work with depend on the return content of a url - which are only known after the viewDidLoad() of course.
So I think I can't have the IBaction way up on top before the viewDidLoad(), and the error I get if I try to do my IB action inside of the viewDidLoad() is:
"Only instance methods can be declared ‘IBAction' ”
EDIT
Let me clarify myself, sorry for the confusion. I know I need an outlet to get the UIStepper values from. I have that:
#IBOutlet weak var stepper: UIStepper!
I then have an action also connected to same UIStepper that will increase/decrease value of a label's text (new_total) accordingly:
#IBOutlet weak var new_total: UILabel!
#IBAction func step_up_pass(sender: AnyObject) {
new_total.text = "\(Int(stepper.value))"
}
However, I want to start out with a value (todays_price) I'm getting back from a json request and use that as a starting point, to multiply it using the stepper and put the multiplied value into the label's text.
I have a struct in a separate file that defines my object so:
struct PassengerFromOtherBus {
var fname: String?
var lname: String?
var todays_price: Int?
init(json: NSDictionary) {
self.fname = json["fname"] as? String
self.lname = json["lname"] as? String
self.todays_price = json["todays_price"] as? Int
}
}
So later on in the view controller, inside of the viewDidLoad(), after connecting to the URL and then parsing it using NSJSONSerialization and a bunch of other code here (that I don't need to confuse you with) I finally have my value todays_price. So my question is, how do I get my action to use that value when it's only known inside of my viewDidLoad()? Xcode will not even let me connect the IBAction to anywhere inside the viewDidLoad function!
This is not done with an Action but with an Outlet. Connect the Stepper from IB as an Outlet to your ViewController. Then just set the values of the Stepper in ViewDidLoad.
I would never go directly from a UIStepper.value to UILabel.text.
Use an intermediary variable to store the value.
Do the same for the return from the JSON. By setting a didSet function on those variables you can update the UI when any of the values is updated.
class FirstViewController: UIViewController {
var todays_price: Int = 0 {
didSet { // didSet to trigger UI update
myLabel.text = "\(stepperValue * todays_price)"
}
}
var stepperValue : Int = 1 {
didSet { // didSet to trigger UI update
myLabel.text = "\(stepperValue * todays_price)"
}
}
#IBOutlet weak var myStepper: UIStepper!
#IBOutlet weak var myLabel: UILabel!
override func viewDidLoad() {
//
let returnValueFromJson = 10
todays_price = returnValueFromJson
}
#IBAction func stepperUpdate(sender: AnyObject) {
stepperValue = Int(myStepper.value)
}
}
Just add a variable to the top of your view controller to hold the value from your json request. Then in viewDidLoad you update that variable, and then you can use it to set your label and inside the IBAction (that doesn't have to be inside viewDidLoad).
So you would do something like this:
class WhateverViewController: UIViewController {
var todays_price: Int!
override func viewDidLoad() {
super.viewDidLoad()
todays_price = // The value you got from json goes here
new_total.text = "\(todays_price)"
}
#IBAction func step_up_pass(sender: AnyObject) {
new_total.text = "\(Int(stepper.value) * todays_price)"
}
}

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.

Resources