I am new to swift, so apologies for funny question, but I am quite tanged in this optional type and the unwrapping thing.
So, I am trying to create a calculator for which I have a UITextField to display the digits while pressed or result after calculation and off course some buttons representing digits.
Now I have action methods attached which my digit buttons and return button properly.
I have my UITextField declared like following which is implicitly unwrapped and a mutable string array which is also unwrapped-
#IBOutlet weak var displayTextField: UITextField!
var digitArray : [String]!
The append digit method works fine which just take the digit from the button and displays it in the textfield by appending. But I am getting a Bad Access error in the enter method. I guess, I am trying to add the item in the array improperly. Can anyone please help.
#IBAction func appendDigit(sender: UIButton) {
let digit = sender.currentTitle!
displayTextField.text = digit + displayTextField.text
}
#IBAction func enter(sender: UIButton) {
digitArray.append(displayTextField.text)
}
digitArray is declared but not initialized.
var digitArray = [String]()
The initializer syntax is either a pair of parentheses after the type let x = Type() or in case of an array with the type annotation and a pair of square brackets let x : [Type] = [].
As the compiler infers the type, the declaration [String] is not needed.
Declare variables non optional whenever possible, you will get more and better help by the compiler.
Convert initializer to
var digitArray = [String]()
Related
I am trying to pass data from textfield, back to previous controller using delegation. I am stuck when I try to assign Int value in this call.
This problem is fairly easy, however I cannot find a simple solution. I have been trying different approaches with additional properties that should hold this value, but with no succeed. What do I have to do with this budgetAmount.text to be properly converted?
protocol BudgetDelegate: class {
func enteredBudgetData(info: String, info2: Int)
}
class AddBudgetViewController: UIViewController {
var budget: Budget?
weak var delegate: BudgetDelegate? = nil
#IBOutlet weak var budgetName: UITextField!
#IBOutlet weak var budgetAmount: UITextField!
//
#IBAction func saveContent(_ sender: UIButton) {
if ((budgetName.text?.isEmpty)! && (budgetAmount.text?.isEmpty)!) {
navigationController?.pop(animated: true)
} else {
-> delegate?.enteredBudgetData(info: budgetName.text!, info2: budgetAmount.text!)
navigationController?.pop(animated: true)
}
}
}
Error Cannot convert value of type 'String' to expected argument type 'Int'
The info2 parameter of your protocol method is of type Int but you are passing budgetAmount.text! which is of course a String. You need to pass an Int.
Perhaps you need to convert the text in the textfield to an Int.
delegate?.enteredBudgetData(info: budgetName.text!, info2: Int(budgetAmount.text!) ?? 0)
BTW - you are making several terrible uses of the ! operator. You should spend time learning about optionals and how to safely work with then.
So based on your question, you just want to pass an Int data coming from a UITextField. And based on your description, you do not have any problem with delegation.
Converting a String into an Int is easy:
Example:
let num = "1"
if let intNum = Int(num) {
// There you have your Integer.
}
I'm confused why the label.text = textField.text line works even though textField.text is an optional. Generally I'd write it as:
if let text2 = textField.text {
label.text = text2
}
The above code works fine but why does the label.text = textField.text work fine too? I thought it's a must to unwrap optionals? When must I use the if let syntax and when do I not have to?
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var label: UILabel!
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
label.text = ""
}
#IBAction func setTextButtonTapped(_ sender: Any) {
label.text = textField.text
}
#IBAction func clearTextButtonTapped(_ sender: Any) {
textField.text = ""
label.text = ""
}
}
UILabel text is an optional String.
UITextField text is an optional String.
An optional can accept an optional or a non-optional.
A non-optional can only accept a non-optional.
You only need to unwrap an optional when you need a non-optional from it.
As vacawama pointed out:
You can assign an optional to an optional; you can assign a
non-optional to an optional, but you cannot assign an optional to a
non-optional.
So you need to unwrap the optional if you wanted to assign it to a non-optional.
And to unwrap an optional, it's always safer to use if let statement, which extracts the non-optional version of the optional and assign it to a variable for further usage.
It sounds like you're not familiar with implicitly unwrapped optionals.
label is an implicitly unwrapped UILabel, and textField is an implicitly unwrapped UITextField
That's what the ! in the declaration
#IBOutlet weak var label: UILabel!
means.
Thus, it's legal to assign things to it without unwrapping it:
label.text = "some string"
However, that's not necessarily safe. If label is nil, that code will crash.
If label was a normal optional:
#IBOutlet weak var label: UILabel?
you'd have to use code like this:
label?.text = "some string"
Xcode sets up outlets as implicitly unwrapped optionals by default, since if an outlet is not connected, it's good to have your code crash so you know something is wrong.
Another part of this is that it's ok to compare 2 optionals with each other:
If you have
var a: Int?
var b: Int?
It's legal to test a == b. (Put formally, Optionals are Equatable.) That expression will evaluate to true if both are nil, or if both contain the same value. You'll get false if one is nil and the other contains a value, or if both contain different values.
(Note that optionals are not Comparable however. You could not use a < b, for example, since it's not define if nil > 0 or nil < 0.)
I've just started to do some simple programming with Swift, things like building a simple calculator and stuff like that. Now I would like to create an app that allows me to sort a bunch of integer values which the user is typing into the TextField. Down here is what I've got so far. Could you please tell me where my mistake is?
class ViewController2: UIViewController {
#IBOutlet var randomNumbers: UITextField!
#IBOutlet var finalResult: UITextView!
#IBAction func SortNumbers(_ sender: UIButton) {
let sortedNumbers = Int[randomNumbers.text]
let sortedNubers = sortedNumbers.sort{$1>$2}
finalResult.text = String(sortedNumbers)
}
it's not the best answer you could get, but it might solves your problem:
#IBAction func sortNumbers(_ sender: UIButton) {
let stringWithNumbers = "1 23 12 4 5 12"
let sortedNumbers = stringWithNumbers.components(separatedBy: " ").flatMap { Int($0) }
let sortedNubers = sortedNumbers.sorted { $0 > $1 }
print(sortedNubers.description)
}
You're not converting it into an Int array properly. If we can assume that the input string is a comma-separated list with no spaces:
let sortedNumbers = randomNumbers.text.components(separatedBy: ",").map{ Int($0) ?? 0 }
components splits it into a string array using commas as a reference, and then map converts each element into an Int (and ?? 0 catches any invalid input and prevents it from being an array of optionals).
Also, for the sake of your sanity and that of anyone who might have to read your code later, avoid using nearly-identical variable names like sortedNumbers and sortedNubers. If you need to use multiple variables for the same data, make their differences more descriptive (for instance, the first one should probably be unsortedNumbers in this case).
Your mistake is in trying to treat a single string as if it were an array of numbers. You're missing two steps in there:
Parsing: Take the string that the user has typed and turn it into numbers
Formatting: Take the stored numbers and turn them back into a string to display
Lurking in that first step is the possibility that the user has not actually typed in integers. An approach that just ignores non-integer input would look like:
#IBAction func sortTextAction(_ sender: UIButton) {
guard let text = randomNumbers.text else {
finalResult.text = "" // clear out - no result on empty input
return
}
// Parse String -> [Int]:
// Split into words, then turn words into Ints,
// while discarding non-Int words
let words = text.components(separatedBy: CharacterSet.whitespacesAndNewlines)
let numbers = words
.map({ Int($0) }) // String -> Int?
.flatMap({ $0 }) // discard any nil values
let sorted = numbers.sort()
// Format [Int] -> String: glue the sorted Ints together with a space between each
finalResult.text = sorted
.map({ String(describing: $0 }) // Int -> String: turn 1 into "1"
.joined(separator: " ") // [String] -> String: turn [1, 2, 3] into "1 2 3"
}
}
Lurking behind both of these is localization: What one locale writes as "1,000" might be "1 000" or "1.000" elsewhere. For that, you'd pull in NumberFormatter to handle the conversions in a locale-aware way.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var display: UILabel!
var inMid = false
#IBAction func appendDigit(sender: UIButton) {
let digit = sender.currentTitle!
if inMid{
display.text = display.text! + digit
}else{
display.text = digit
inMid = true
}
}
var operandStack = Array<Double>()
#IBAction func enter() {
inMid = false
operandStack.append(displayValue)
println("operandStack = \(operandStack)")
}
var displayValue:Double{
get {
return NSNumberFormatter().numberFromString(display.text!)!.doubleValue
}
set{
display.text = "\(newValue)"
}
}
}
This is the part of code used in the latest Standford IOS 8 course using swift to build a calculator(Youtube address: https://www.youtube.com/watch?v=QLJtT7eSykg)
Every time I call enter() (press enter), a new number is supposed to be saved in a stack. For example: "8, enter()" --> {8}, "16, enter()" --> {8,16}.
I got confused about the computed property "displayValue" here. There is nothing assigned to the "newValue". If there is something like "displayValue = 8", then I know "newValue" is 8, and it all makes sense. But there is not such thing.
How come it still works?
(What I mean is not the name "newValue" itself,I know it is a default setting by Swift, instead, the missing of assigned value is the one that confuses me)
"newValue" is an implicitly defined variable in swift.
What he does is a very neat way of letting the label show the values of the double "displayValue" Every time displayValue is changed, the label is automatically updated with the newest (double) value.
Or when you write: displayValue = 45.0, the label will also show this value. Very handy when you constantly need to update textfield or labels with data you get from databases, rest interfaces, etc. What "newValue" does is taking the last "setter" value holding that.
Alright, so I'm building an app that takes in a sequence of int values using a text field and encrypts it depending upon the initialized cipher. Originally I use the .toInt() function to covert my numerical string values into an array of integer. Then I add the cipher to each value in the array of integers however I'm having difficulty outputting the int array back to the text-field with the encrypted sequence. I have already thoroughly researched this and I can't seem to find an adequate function to convert my int array (named: code) back into a string which I can initialize as message1.text = the string numerical sequence.
Here is my code so far:
class ViewController: UIViewController {
#IBOutlet var text: UITextField!
#IBOutlet var Lable: UILabel!
#IBOutlet var Button: UIButton!
#IBOutlet var message1: UITextField!
#IBOutlet var Button2: UIButton!
var name = 0
#IBAction func Button(sender: UIButton) {
name = text.text.toInt()!
}
#IBAction func Button2(sender: UIButton) {
var code = Array (message1.text).map{String($0).toInt()! }
for var n = 0 ; n < code.count; n++
{
code[n] = code[n] + name
}
var StringArray = (code : String()) // This is the mistake, I want to convert the code array into a joint string,
message1.text = StringArray
}
I already know that what I attempted to do doesn't work cause when I run the app and press Button2 I'am left with no text at all in the text-field thus it is a legitimate statement that String array is not being initialized as expected.
Any help is much appreciated, Thank you for your time I'm starting off in app programming and I'm still struggling at relatively simple stuff so I understand if the answer might seem overly palpable.
All you need to do is remap the values back to strings and join them:
var stringOfCodes = "".join( code.map { "\($0)" } )
This gives you a single string from the code values. I feel like there is a more elegant way to accomplish this, but this is what immediately came to mind.
If you want to create a string out of an array:
var spaceSeparatedString = " ".join(StringArray)
This will create a string out of the array using a space as the separator. Eg: "string1 string2 string3".