Adding Model file to view controller, help getting UI working - ios

I wrote a Model file for my basic app and I'm in the process of applying the model to the UI. It has 3 manual inputs and a text area for the "results" once the calculate button has been pressed.
I'm really struggling with making the UI do what I want it to do, I just want the results displaying, I tried print() but get unresolved identifiers in view controller.
How do I get the .value of my results from the Model file in the view controller? Or have to initialize those again?
Model File (Struct)
struct FuelMaths {
var rate: Double = 3.7 //set initialValvue
var tank: Int = 110
var laps: Int = 13
var totalFuel: Double
var totalStops: Double
init (rate: Double, tank: Int, laps: Int) {
self.rate = rate // declare an instance
self.tank = tank
self.laps = laps
totalFuel = rate * Double(laps)
totalStops = totalFuel / Double(tank)
}
func result () {
print(totalFuel.value)
print(totalStops.value)
}
}
View Controller
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var rate: UITextField!
#IBOutlet weak var laps: UITextField!
#IBOutlet weak var tank: UITextField!
let calculate = FuelMaths(rate: 3.7, tank: 110, laps: 13)
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.
}
#IBAction func calculate(sender: UIButton) {
}
#IBOutlet weak var display: UITextView!
}
Thanks all.

(Edit: I originally said you didn't instantiate the model at class scope; you did, but you misnamed it as a verb calculate rather than a good noun like fuelCalculationModel. But anyway, I'm using it the way you've written it: which is as sort of a one-off where the values are passed in at construction time and those values are assumed final so that the result is calculated immediately. It is possible to write a model that is designed to persist and adjust to changes in its source value over time, but you need to understand property observers or computed properties for that.)
See if the solution is helpful:
#IBAction func calculate(sender: UIButton) {
if let rateVal = Double(rate.text!),
tankVal = Int(tank.text!),
lapVal = Int(laps.text!) {
let fuelModel = FuelMaths(rate: rateVal, tank: tankVal, laps: lapVal)
display.text = "Total fuel: \(fuelModel.totalFuel) Total Stops: \(fuelModel.totalStops)"
}
else {
display.text = "One of the text fields did not have a valid number."
}
}
Ultimately you want the model to be a persistent object at class-level scope, not function scope, and you want it to recalculate its results whenever its source values are changed, but wait on that until you understand this, especially if-let when constructing numbers out of parsed Strings.

Related

How to display the correct label.text depending on a randomised array index

Beginner Here,
I'm coding a project that randomly matches up DOTA 2 heroes.
I'm currently pulling the hero image into the ImageViews from the fighters array using Int.random.
However, I'm now trying to implement a corresponding label underneath the image to display the name of the hero currently in the ImageView.
I'm initially trying to do this for fighterOne at fighters["1"] which would be Dragon Knight.
I've tried various 'if' statements, none of which seem to work. I've also scoured forums and documentation, but can't find the solution anywhere.
#IBOutlet weak var fighterOne: UIImageView!
#IBOutlet weak var matchButton: UIButton!
#IBOutlet weak var fighterOneName: UILabel!
let fighters = ["1","2","3","4","5","6","7","8","9","10","11","12",]
var randomIndex1: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
updateFighterName()
updateFighters()
}
///// FUNCTIONS /////
func updateFighterName() {
if fighterOne.image == UIImage(named: "1") {
fighterOneName.text = "Dragon Knight"
} else {
fighterOneName.text = "Wrong"
}
}
func updateFighters() {
randomIndex1 = Int.random(in: 0...11)
fighterOne.image = UIImage(named: fighters[randomIndex1])
}
I can't make sense of the result I'm getting from this. When I run the app, the "Dragon Knight" is occasionally displayed, however, it's not under the correct hero image, it's very erratic as to when it's displayed, and there appears to be no logical display sequence from what I can tell.
Any help would be appreciated and let me know if you need more information!
You need to call updateFighters() before calling updateFighterName() inside your viewDidLoad() because you depend on what it does.
Your viewDidLoad() should look like this:
override func viewDidLoad() {
super.viewDidLoad()
updateFighters()
updateFighterName()
}
And you need to replace:
if fighterOne.image == UIImage(named: "1") {
}
With:
if fighterOne.image.isEqual(UIImage(named: "1")) {
}

Adding user inputs from textfield into an array

I am trying to add users names that they have put in a textfield to a basic array. I don’t even know where to start!
The functionality basically is this:
User adds a name in text field
Name is stored in an array so it can be referenced again somewhere else.
As you can tell I’m new to Xcode. I have been taking classes all of which have failed to explain this process. Any help is good Help ! Please :):)
// my view controller so far
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var name1: UITextField!
#IBOutlet weak var name2: UITextField!
#IBOutlet weak var name3: UITextField!
var namesArray : [String] = []
override func viewDidLoad() {
super.viewDidLoad()
name1.delegate = self
name2.delegate = self
name3.delegate = self
self.namesArray.append(self.name1.text!)
self.namesArray.append(self.name2.text!)
self.namesArray.append(self.name3.text!)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
if textField.returnKeyType == UIReturnKeyType.done {
self.namesArray.append(self.name1.text!)
print(namesArray)
}
return true
}
I am going to assume you have all your logic mashed into one view controller since you are still learning and not worried about larger architecture decisions yet.
At the top of your class, you need an array variable:
// Creates an array and initializes it to be empty.
var userNames: [String] = []
I suspect you also have a button, so that when you press on the button, it grabs the text from the textfield and stores it in the array. You need to set up an IBAction method for that button, and in that method...
// Grab the text, save it in the array, and clear the text.
self.userNames.append(self.myTextField.text)
self.myTextField.text = ""
Later on, you can easily see the list of userNames by accessing self.userNames. e.g.:
let firstUser = self.userNames.first
let lastUser = self.userNames.last
let user47 = self.userNames[46] // assuming you have that many users

bindTo rx_text of a UITextField did not trigger the UITextField's emit event

I'm a new one to learn RxSwift.
I modified the Simple Numbers example in the RxSwift Example App, which will add three numbers into a result number.
I add a testStr UITextField, and an upperCase UILabel. I map testStr to uppercase and bindTo the upperCase label, that's good. And I also map testStr to its length, and bindTo the num1 field. Strange things happen, although the contents of the num1 field changes, it does not emit any event, so it has no effect on the result label. Even if I input some number into another number field, the result number does not count the num1.
Have I made any wrong use of bindTo? In what way can I make the num1 emit an event?
Thanks!!!
#IBOutlet weak var num1: UITextField!
#IBOutlet weak var num2: UITextField!
#IBOutlet weak var num3: UITextField!
#IBOutlet weak var result: UILabel!
#IBOutlet weak var testStr: UITextField!
#IBOutlet weak var upperCase: UILabel!
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Observable.combineLatest(num1.rx_text, num2.rx_text, num3.rx_text) {
(textval1, textval2, textval3) -> Int in
return (Int(textval1) ?? 0) + (Int(textval2) ?? 0) + (Int(textval3) ?? 0)
}
.map{$0.description}
.bindTo(result.rx_text)
.addDisposableTo(disposeBag)
let obStr = testStr.rx_text
obStr
.map {$0.uppercaseString}
.bindTo(upperCase.rx_text)
.addDisposableTo(disposeBag)
obStr
.map{ $0.characters.count }
.map{ $0.description }
.bindTo(num1.rx_text)
.addDisposableTo(disposeBag)
}
You need to use a Subject such as Variable to store the value. You can see an example of this in this answer in the section called Using Variables.
The reason it doesn't work is because rx_text will only omit a next element when it's changed by the user, not programmatically (as you're doing). This is because rx_text is really using this method from UIControl to get notified of changes to the field:
public class UIControl : UIView {
public func addTarget(target: AnyObject?, action: Selector, forControlEvents controlEvents: UIControlEvents)
}
However, that method does not call the action method on target when the change happens programmatically. Only when it happens due to a user changing something.
So, you should see a next event if you were to change the field programmatically and then the user were to tap into (or out of) the field. However, that's not what you want.
Instead, refer to that answer I linked you to and it will work.

How can I store a variable (Int) permanently after app closes and Display it?

I am trying to store a variable named total which holds the number of rows in UITableView. Since the user types strings for UITableView, the variable is listTable which is String array.
I have my total as total = listTable.count
ISSUE:
Every time the app launches it displays 0 as number of rows used, however once you go to the UITableView ViewController and come back to the main ViewController it automatically corrects its self to the current amount of rows.
Code Used:
import UIKit
var listTable = [String]()
var total = listTable.count
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var numberOfRows: UILabel!
#IBOutlet weak var TypeToCreateRow: UITextField!
#IBOutlet weak var AddRow: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
total = listTable.count
NSUserDefaults.standardUserDefaults().setObject(total, forKey: "rows")
let showRow = NSUserDefaults.standardUserDefaults().objectForKey("rows")!
numberOfRows.text = "\(showRow)"
print(showRow)
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func RowAdd(sender: UIButton) {
listTable.append(TypeToCreateRow.text!)
TypeToCreateRow.text = " "
numberOfRows.text = "\(total)" }
As simple as :
[[NSUserDefaults standardUserDefaults]setObject:value forKey:#"key"];
and to find it after :
NSUserDefaults *user = [NSUserDefaults standardUserDefaults];
[user objectForKey:#"login"]
You set the object first before reading it. That's why you write 0 first and read it immediately.
These two lines don't achieve anything.
NSUserDefaults.standardUserDefaults().setObject(total, forKey: "rows")
let showRow = NSUserDefaults.standardUserDefaults().objectForKey("rows")!
You need to setObject after you have the correct value, later in code. Not at the start.
Also as JAL mentioned you should use setInteger instead of object, as it is more optimized.

Incrementing Array Element By One In Swift

I am Currently working on a swift project in XCode, and I've encountered an issue where I cannot increment an array's one element by through various endeavours.
My ViewController.Swift
import UIKit
class FailureViewController: UIViewController {
#IBOutlet weak var failFactLabel: UILabel!
let failFact = Failure()
override func viewDidLoad() {
super.viewDidLoad()
failFactLabel.text = failFact.failArray[0]
}
#IBAction func showFunFact() {
//this is where the issue is
}
}
For the function showFunFact I have previously tried
for var x = 0; x < failArray.cout; x++ {
failFactLabel.text = failFact.failArray[x]
}
but encountered the error: "Use of Unresolved Identifier".
Putting this aside, I decided to use
for var x = 0; x < 10; {
x+=1
}
Although this does not generate an error however, I see it stops at the first element in my array. I tried +=5 and it displays the 5th element in my array once, I believe this code runs through once. I need it to be a consistently working piece so it continuously displays the next element, I am stumped because this should theoretically work since it is called a "Loop" Any help or suggestion is Appreciated!
Objective Redefined:
I am trying to show one fact at a time, before this I have used a random function to generate a random fact every time however things tend to get repetitive and therefore I decided to start on the initial array index is 0 and grab the next fact (next element in array) every time the button is pressed (Thanks to Luk's Question)
You need to create an instance variable to hold the current factIndex, if that is what you actually are trying:
import UIKit
class FailureViewController: UIViewController {
#IBOutlet weak var failFactLabel: UILabel!
var factIndex = 0
let failFact = Failure()
override func viewDidLoad() {
super.viewDidLoad()
failFactLabel.text = failFact.failArray[factIndex]
}
#IBAction func showFunFact() {
factIndex++
if factIndex >= failFact.failArray.count then {
factIndex = 0
}
failFactLabel.text = failFact.failArray[factIndex]
}
}

Resources