Getting out of closure Swift - swift

I'm building this program to use during Trivial Pursuit for questions. I'm having trouble getting the size of the dictionary to be stored in the size variable from the function. I have tried every way you can store a variable but I always get the value of zero.
var size:Int = Int()
#IBOutlet var questionLabel: UILabel!
#IBOutlet var answerLabel: UILabel!
#IBAction func answerButton(sender: AnyObject) {
}
func getSize() {
self.size = scienceDictionary.count
}
var scienceDictionary = ["What is the chemical process that converts sugar into alcohol?": "Fermentation",
"Where in your body would you find you hippocampus?": "Brain",]
override func viewDidLoad() {
super.viewDidLoad()
print(size)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

You should just make size a computed property:
var size: Int {
return scienceDictionary.count
}
and your getSize() function will become superfluous.

You're not calling getSize() anywhere. You should call it for the code inside to be executed, like this:
override func viewDidLoad() {
super.viewDidLoad()
getSize()
print(size)
}

By default, your size variable is 0. Based on the code you show, getSize() is never called, so size never changes.
However, I recommend not doing what you want to do. Instead of using a size variable, simply type scienceDictionary.count wherever you want to find the size. This is much clearer and requires a lot less extra work.

Related

How to access a text field value and convert to double

I am working on trying to build a tip calculator using swift but am running into a multitude of problems. I have a text box where the user enters in the bill amount and then they can adjust a slider to indicate the percentage that they want to tip. I have managed to get the slider and label associated with it to work where changing the value on the slider changes the label. However, I can't figure out how to get the text field to work. I attempted to create an action for the bill amount text box being changed but no matter what I put in it, it doesn't seem like the code is ever being executed (Whenever I run the code and click in the text box, the keyboard won't go away so maybe this is part of the problem?). All that I need is to be able to access the value in the text box field inside of my action when I click the calculate button but I can't seem to even get the value to show up much less convert it to a double so I can perform calculations on it. I am super new to swift and really want to learn what I am doing wrong. I have tried multiple tutorials and similar questions on here but none of them work. I appreciate all help y'all can give.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var txtBillAmount: UITextField!
#IBOutlet weak var lblTipPercentage: UILabel!
#IBOutlet weak var sldTipPercentage: UISlider!
#IBOutlet weak var lblTipAmount: UILabel!
#IBOutlet weak var lblTotalAmount: UILabel!
var tipPercentage = 10
var billAmount = ""
#IBAction func valueChanged(sender: AnyObject) {
let currentValue = Int(sldTipPercentage.value)
lblTipPercentage.text = "\(currentValue)%"
tipPercentage = currentValue
}
#IBAction func btnCalculate(sender: AnyObject) {
lblTipAmount.text = "\(billAmount)"
}
#IBAction func txtBillAmountValueChanged(sender: AnyObject) {
}
override func viewDidLoad() {
super.viewDidLoad()
//txtBillAmount.delegate = self
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func btnReset(sender: UIButton) {
sldTipPercentage.value = 10
lblTipPercentage.text = "10%"
txtBillAmount.text = ""
lblTipAmount.text = "--"
lblTotalAmount.text = "--"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
This is a screenshot of how the UI looks
Error that I am getting
[Xcode >= 7 and Swift > 2]
Try like this
#IBAction func btnCalculate(sender: AnyObject) {
if let textValue = txtBillAmount.text {
let billAmnt = Double(textValue)
let tipAmount = (billAmnt*tipPercentage)/100
lblTipAmount.text = "\(tipAmount)"
}
}
If you are using Xcode-6 and Swift < 2 then try the following way. because String initializer for Double is only available in Swift 2 (Xcode 7). [Recommend update your Xcode]
#IBAction func btnCalculate(sender: AnyObject) {
let billAmnt = (txtBillAmount.text! as NSString).doubleValue
let tipAmount = (billAmnt*tipPercentage)/100
lblTipAmount.text = "\(tipAmount)"
}

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.

Would using Swift's Guard or if Let Keywords take care of not having to use this checkNil function?

I understand that programming in a language such as Swift, intent can be expressed in numerous ways. I'm following a tutorial and saw the code below which isn't what I'm used to seeing because the author created a function to check for nil. Could the checkNil function be avoided by simply using guard or if let statements or does improve the code somehow? It's a great tutorial and I'm simply looking to improve my knowledge of interpreting other developer's code to find ways to be more concise myself.
import UIKit
class ViewController: UIViewController {
private let rideService = DummyRideService()
private var rides = [Ride]()
#IBOutlet var from: UITextField!
#IBOutlet var to: UITextField!
#IBOutlet var ridesTableView: UITableView!
#IBAction func findRouteButtonClicked(){
let fromText = self.checkNil(from.text as AnyObject?)
let toText = self.checkNil(to.text as AnyObject?)
}
func checkNil(_ string: AnyObject?) -> String {
return string == nil ? "": string as! String
}
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.
}
}
there are many ways to get rid of the entire checkNil(_:) nonsense quickly.
idea #1
I'd just simply make the outlets optional weak properties then use optional chaining, like:
#IBOutlet private weak var from: UITextField?
#IBOutlet private weak var to: UITextField?
then you could use this:
#IBAction func findRouteButtonClicked() {
let fromText = from?.text ?? ""
let toText = to?.text ?? ""
}
idea #2
or if you want to make it more elegant, you could create an extension on UITextField, like:
extension UITextField {
var alwaysText: String {
return self.text ?? ""
}
}
so you could replace the let lines with these more readable ones, like
#IBAction func findRouteButtonClicked() {
let fromText = from.alwaysText
let toText = to.alwaysText
}
You can use optional binding (under the optionals section) to check if the value is nil in this code:
let fromText = self.checkNil(from.text as AnyObject?)
can be changed to
if let fromText = from.text {
// Run bit of code
}
or as you aren't using that value straight away, you can do:
let fromText = from.text ?? ""
using the coalescing operator which is equivalent to using that check-for-nil function.
Functions known for usage to gain re usability , he constructs it once and uses it's code twice inside findRouteButtonClicked of course you can use guard or let statements but what if there are many strings to check their nullability in different parts of the VC

Swift: display a random array index every button click

I have a swift class that reads lines from a text document and prints out the first line. After, every time a button is clicked a new line is read out.
What I want is to have a random line printed out the first time, and then a random line printed out after every button click.
Here's what I have so far:
import Foundation
import UIKit
class InfoController: UIViewController {
// MARK: Properties
#IBOutlet weak var difficultylevel: UILabel!
var i:Int = 0
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func readFile(){
if let path = NSBundle.mainBundle().pathForResource("easymath", ofType: "txt"){
var data = String(contentsOfFile:path, encoding: NSUTF8StringEncoding, error: nil)
if let content = data {
let myStrings = content.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet())
let randomIndex = Int(arc4random_uniform(UInt32(myStrings.count)))
difficultylevel.text = myStrings[randomIndex]
}
}
}
#IBAction func difficultybutton(sender: UIButton) {
difficultylevel.text = // TODO insert random index of "myStrings" array here
}
}
However, I cannot access the myStrings array at the TODO portion inside the button click. Any help on how to set this up?
Variable scope in Swift is limited to the brackets of the function. So to make myStrings available outside of your readFile() function, you need to declare it as a property for the class:
#IBOutlet var difficultyLevel: UILabel? // BTW your IBOutlet should not be weak
var i: Int = 0
var myStrings: [String]?
Since you are going to use the random functionality over and over, we can abstract the function like this
func randomString() -> String? {
if let strings = myStrings {
let randomIndex = Int(arc4random_uniform(UInt32(myStrings.count)))
return strings[randomIndex]
}
return nil
}
then your instantiation will be like this
if let content = data {
myStrings = content.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet())
difficultyLevel.text = randomString()
}
Then, your difficultybutton function will be (with an abstracted random string function)
// Changed the name for better readibility
#IBAction func difficultyButtonTapped(sender: UIButton) {
difficultyLevel.text = randomString()
}
Finally, there isn't any code that calls the readFile function, so you should add it to probably the viewDidLoad function as #CharlesCaldwell points out
override func viewDidLoad() {
super.viewDidLoad()
readFile()
}

Updating a integer as a label in swift

I want to change the number value of a label by pressing a button. I used to get errors saying you can't put a number in a string, so, I did string(variable). It now displays the basic number, 1, but when I click it, it doesn't update!
Here is how I set up the variable:
First, I set up the button, to IBAction. Here is the code inside of it:
#IBOutlet weak var NumberOfExploits: UILabel!
#IBAction func exploiter(sender: AnyObject) {
var hacks = 0
hacks += 1
NumberOfExploits.text = String(hacks)
}
Can someone help be find out how to get it to change numbers?
First:
let is used for constants, these can not be changed.
var is used for variables.
Second:
Doing plus one is done with += 1 or with = myVariable + 1
var myVariable = 1
myVariable += 1
label.text = string(myVariable)
It is important to understand that your variables only live as long as the enclosing class or function is alive. So when you declare a variable, constant inside a function (using var let is a declaration) it will be be gone when the function is complete. A UIViewcontroller is there for as long as you want it to be. So declare things in there that you will need later.
import UIKit
// things that live here are at the "global level"
// this is a viewcontroller. It is a page in your app.
class ViewController: UIViewController {
// things that live here are at the "class level"
var myClassCounter : Int = 0
// button in interfacebuilder that is connected through an outlet.
#IBOutlet weak var myButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// this is a function that gets called when the view is loaded
}
override func viewDidAppear(animated: Bool) {
// this is a function that gets called when the view appeared
}
// button function from interface builder
#IBAction func myButtonIsTouched(sender: AnyObject) {
// this will be destroyed when the function is done
var myFunctionCounter = 0
myFunctionCounter += 1
// this will be stored at class level
myClassCounter += 1
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You can also just do
label.text = "\\(variable)"
which will display the number as string.
Most important declare the variable within the class but outside any function
var variable = 1
then write the IBAction function this way
#IBAction func pressButton(sender : UIButton)
{
label.text = "\(++variable)"
}
The syntax ++variable increments the variable by one.
The syntax "\(v)" creates a string of anything which is string representable