Adding multiple lines in UILabel - ios

I am trying to make a times table app with user's input value. Whenever I try to print result on a label, it just prints the last line and not a full table.
How can I append text in UILabel? Here is my code:
if let textBox = textField.text{
let textBoxNum = Int(textBox)
if let number = textBoxNum{
if number > 0{
let i = 1
while i <= 20{
label.text = "\(number) x \(i) = \(number * i)"
}
}
else{
label.text = "Please enter a positive whole number"
}
}
else{
label.text = "Please enter a positive whole number"
}
}

The assignment operator = just reassigns a new value to the text of your label. You need to add on to the existing text using the Compound Assignment Operator call addition assignment operator += which will assign new value by adding it to the existing value.
let a:String = "Hello"
print(a) // Hello
a += " World!"
print(a) // Hello World!
This same terminology will be applied to your case where you will use += on the text property of the label.
if let textBox = textField.text{
let textBoxNum = Int(textBox)
if let number = textBoxNum{
if number > 0{
let i = 1
while i <= 20{
label.text += "\(number) x \(i) = \(number * i)\n"
// Use of compound operator here will append the text so you have
// to use a new line escape character to print the text properly.
}
}
else{
label.text = "Please enter a positive whole number"
}
}
else{
label.text = "Please enter a positive whole number"
}
}
More about operators here:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html

Additional to the other answers (using += and \n for line breaks):
If you use a label, you'll have to set the number of lines to zero, which will allow unlimited number of lines:
label.numberOfLines = 0
But you might better want a UITextField to display multi-line text, which allows the user to scroll up and down if the text gets too long. You would only have to disable the editing / keyboard popping up, something like:
class TheViewController: UIViewController, UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.theTextField.delegate = self
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
return false
}
}

Related

Update output as user types in textfield

I have code to sum four text fields and output the total in a label. Currently the code sums the fields after finishing editing, that is, selecting another text field. Is there a way to sum the text fields as the user types?
#IBAction func TankFuelChanged(_ sender: Any) {
let leftMainTankQuantityValue = Int(leftMainTankQuantity.text ?? "") ?? 0
let rightMainTankQuantityValue = Int(rightMainTankQuantity.text ?? "") ?? 0
let auxTankQuantityValue = Int(auxTankQuantity.text ?? "") ?? 0
let tailTankQuantityValue = Int(tailTankQuantity.text ?? "") ?? 0
let total = leftMainTankQuantityValue + rightMainTankQuantityValue + auxTankQuantityValue + tailTankQuantityValue
totalFuelLoad.text = "\(total)"
What you are looking for is an event triggered when text field changes. You can drag an action from storyboard or you can add them programmatically by using addTarget similar to UIButton but need to use event editingChanged. Check the following code:
var allTextFields: [UITextField] {
return [leftMainTankQuantity, rightMainTankQuantity, auxTankQuantity, tailTankQuantity]
}
override func viewDidLoad() {
super.viewDidLoad()
allTextFields.forEach { $0.addTarget(self, action: #selector(onTextFieldChange), for: .editingChanged) }
}
#objc private func onTextFieldChange() {
updateResult()
}
private func updateResult() {
let strings: [String] = allTextFields.compactMap { $0.text } // Will remove all nil texts
let integers: [Int] = strings.compactMap { Int($0) } // Will remove all non-integer texts
let sum = integers.reduce(0, { $0 + $1 }) // Will compute a sum
print(sum) // TODO: update your result here
}
A method must be marked #objc because of the #selector next to that I hope code speaks for itself.

Number formatter not allowing decimals to show

Here is the obligatory "I'm new to programming" but, I've searched all available answers and have concluded that my issue may be more logic related than code, but I could be wrong about that too. I'm building a calculator app and everything is working except the numberFormatter (to show comma separators) in the display. Whenever I try to format the number in the display, I can't get the display to show the decimal and the commas.
If I start with a decimal .1234 , I get 0.1234 and if I type 12345 I get 12,345 but if i type 12345.678, I get 12,345. I'm losing the decimals. I've tested it and my function to remove extraneous "." doesn't seem to be the issue. And If I run the string extension numberFormatter outside of the label formatting controls it seems to work, but I need to guard against multiple decimals and extraneous "0"s.
I'm showing the code to the IBAction covering the buttons showing up on the display label, display.text which is the issue. All calculations after this are working fine, with the replacingOccurrences(of: ",", with: "") to create a clean string to convert to Double and calculate.
I'm using a sting extension to do the formatting. I've been working on and off on this for weeks. Any ideas? Do I have to refactor how I enter text into the label.text?
here is the code to add text to the UILabel display.
#IBAction func btnTouchDigit(_ sender: UIButton) {
let digit = sender.currentTitle!
if isUserTyping {
var formattedNumber = ""
print( "is user typting + String\(isUserTyping)")
// make sure we aren't adding a second period
var textCurrentlyInDisplay = display.text
textCurrentlyInDisplay = textCurrentlyInDisplay?.replacingOccurrences(of: ",", with: "")
if digit == "." && ((textCurrentlyInDisplay?.range(of: ".")) != nil) {
return
}
else {
formattedNumber = (textCurrentlyInDisplay! + digit)
print("formattedNumber = \(formattedNumber.twoFractionDigits)")
display.text = formattedNumber.twoFractionDigits
// put code here to format label.text to show thousand seperators
print("textCurrentlyInDisplay end = \(textCurrentlyInDisplay!)")
}
}
// make sure we aren't entering a bunch of zero's
else { print("else + \(isUserTyping)")
display.text = digit
if digit == "0" {return}
else if digit == "." {display.text = "0."}
// display.text = (digit == "." ? "0" : "") + digit
isUserTyping = true
}
}
Here is my extension to handle the string conversion for the numberFormatter.
extension String {
var twoFractionDigits: String {
let styler = NumberFormatter()
styler.minimumFractionDigits = 0
styler.maximumFractionDigits = 16
styler.numberStyle = .decimal
let converter = NumberFormatter()
converter.decimalSeparator = "."
if let result = converter.number(from: self) {
return styler.string(from: result)!
}
return ""
}
I found a hack to work around my problem. It's not pretty, but it works. I was able to get the numberFormatter to update and show the digits after the decimal but that led to a new issue. If you typed 12345.00 you would get 12,344 and not see the trailing 0's until you pressed another number. ex 12345.1 -> 12,345.1, 12345.001 -> 12,345.001, but 12345.00 -> 12,345.
I needed it to work dynamically so the user knows how many zeros are being entered. My hack was to split the final amount into an two arrays. One pre-formatted and one post-formatted. Then join the two together for the final display number.
I'd still love to find a more efficient solution, if anyone has any ideas.
Here is the updated code.
#IBAction func btnTouchDigit(_ sender: UIButton) {
let digit = sender.currentTitle!
if isUserTyping {
preFormattedNumber = (display.text?.replacingOccurrences(of: ",", with: ""))!
// set the limit for number of digits user can enter at once
if display.text!.count >= 16 {
return
}
else {
// make sure we aren't adding a second period
if digit == "." && ((preFormattedNumber.range(of: ".")) != nil) {
print("extra decimal pressed")
return
}
else {
preFormattedNumber = (preFormattedNumber + digit)
print("preFormattedNumber before Formatting = \(preFormattedNumber)")
}
// put code here to format label.text to show thousand seperators
if ((preFormattedNumber.range(of: ".")) != nil){
print("just checked for .")
let numPreFormat = preFormattedNumber
let numAfterFormat = preFormattedNumber.twoFractionDigits
let numArray = numPreFormat.components(separatedBy: ".")
let numArrayFormatted = numAfterFormat.components(separatedBy: ".")
let afterDecimal = numArray.last
let beforeDecimal = numArrayFormatted.first
let finalNumberToDisplay = beforeDecimal! + "." + afterDecimal!
print("numArray = \(numArray)")
print("final number to display = \(finalNumberToDisplay)")
print("numArray = \(numArray)")
display.text = finalNumberToDisplay
runningNumber = display.text!
}
else {
display.text = preFormattedNumber.twoFractionDigits
runningNumber = display.text!
}
}
}
// make sure we aren't entering a bunch of zero's
else { print("else + \(isUserTyping)")
preFormattedNumber = digit
display.text = preFormattedNumber
runningNumber = display.text!
if digit == "0" {return}
else if digit == "." { preFormattedNumber = "0.";
}
display.text = preFormattedNumber
runningNumber = display.text!
isUserTyping = true
}
}

How do I set up the buttons that are linked to didPressNumber to add to each other when pressed

How do I set up the buttons that are linked to didPressNumber to add to each other when pressed so lets say its a calculator and I want set it up where each button is pressed has a letter and number value when it is pressed it adds to the previous one press and I want to set up 2 labels one displaying the number value and one displaying the letter value and how would I set up the value of each number?
enum modes {
case not_set
case addition
case subtraction
case equals
}
#IBAction func didPressNumber(_ sender: UIButton) {
let stringValue:String? = sender.titleLabel?.text
if (lastButtonWasMode) {
lastButtonWasMode = false
labelString = "0"
}
labelString = labelString.appending(stringValue!)
updateText()
}
func updateText() {
guard let labelInt:Int = Int(labelString) else {
return
}
if (currentMode == .not_set) {
savedNum = labelInt
}
let formatter: NumberFormatter = NumberFormatter()
formatter.numberStyle = .decimal
let num:NSNumber = NSNumber(value: labelInt)
label.text = formatter.string(from: num)
}
func changeMode(newMode:modes) {
if (savedNum == 0) {
return
}
currentMode = newMode
lastButtonWasMode = true
}

how to ignore a value of a UITextField that has already been calculated in swift 3?

I'm trying to make an app that is very basic. One part of the app is that there are 4 textFields and a button that calculates the sum of these textFields.
The problem that I'm facing is that say I type the value 10 in the first textField then I press the button. The result would be 10. However, if I press it again ( without typing anything in the other textFields), the result would be 20!! Furthermore, if I type 20 in one of the other textFields, the result would be 40!!
The result SHOULD BE 30 NOT 40!!
one possible option I thought of (haven't tried it yet) is assigning 0 to all of the textFields when pressing the button. But I'd like the app to be smarter and keep tracks of the result.
if it helps, here's the code inside the button that calculates the sum:
#IBAction func calBtnPressed(_ sender: UIButton) {
var benifit:[Double] = []
var textFields: [Double] = []
if initialBalance.text?.isEmpty ?? true {
// do nothing
} else {
if let temp = initialBalance.text {
// these lines of code will convert arabic numbers to English ones in case the user uses Arabic number
let initialStr: String = temp
let initialFormatter: NumberFormatter = NumberFormatter()
initialFormatter.locale = NSLocale(localeIdentifier: "EN") as Locale!
let initialFinal = initialFormatter.number(from: initialStr)
benifit.append(Double(initialFinal!))
}
}
if income.text?.isEmpty ?? true {
// do nothing
} else {
if let temp = income.text {
// these lines of code will convert Arabic numbers to English ones in case the user uses Arabic number
let incomeStr: String = temp
let incomeFormatter: NumberFormatter = NumberFormatter()
incomeFormatter.locale = NSLocale(localeIdentifier: "EN") as Locale!
let incomeFinal = incomeFormatter.number(from: incomeStr)
benifit.append(Double(incomeFinal!))
}
}
if salaries.text?.isEmpty ?? true {
// do nothing
} else {
if let temp = salaries.text {
let salariesStr: String = temp
let salariesFormatter: NumberFormatter = NumberFormatter()
salariesFormatter.locale = NSLocale(localeIdentifier: "EN") as Locale!
let salariesFinal = salariesFormatter.number(from: salariesStr)
textFields.append(Double(salariesFinal!))
}
}
if tools.text?.isEmpty ?? true {
// do nothing
} else {
if let temp = tools.text {
let toolsStr: String = temp
let toolsFormatter: NumberFormatter = NumberFormatter()
toolsFormatter.locale = NSLocale(localeIdentifier: "EN") as Locale!
let toolsFinal = toolsFormatter.number(from: toolsStr)
textFields.append(Double(toolsFinal!))
}
}
if maintinance.text?.isEmpty ?? true {
// do nothing
} else {
if let temp = maintinance.text {
let maintinanceStr: String = temp
let maintinanceFormatter: NumberFormatter = NumberFormatter()
maintinanceFormatter.locale = NSLocale(localeIdentifier: "EN") as Locale!
let maintinanceFinal = maintinanceFormatter.number(from: maintinanceStr)
textFields.append(Double(maintinanceFinal!))
}
}
if other.text?.isEmpty ?? true {
// do nothing
} else {
if let temp = other.text {
let otherStr: String = temp
let otherFormatter: NumberFormatter = NumberFormatter()
otherFormatter.locale = NSLocale(localeIdentifier: "EN") as Locale!
let otherFinal = otherFormatter.number(from: otherStr)
textFields.append(Double(otherFinal!))
}
}
for textField in textFields {
sumExpenses += textField
}
for ben in benifit{
sumBenifit += ben
}
totalExpenses.text = String(sumExpenses)
totalAfterSubtractingExpenses.text = String( sumBenifit - sumExpenses )
sumBenifit -= sumExpenses
}
I think I found your problem.
You use a variable sumBenefit which isn't declared in your func, so I assume it is declared in your UIViewController.
Since it is an instance variable, it will not reset each time you click the button.
If you want to reset the values of sumExpenses and sumBenefits each time the button is pressed, then you'll have to do something like this:
sumExpenses = 0
for textField in textFields {
sumExpenses = Int(textField.text)!
}
sumBenefit = 0
for ben in benefit {
sumBenefit += ben
}
I am also making the assumption that you want a number from your textField in the first for-loop, because if sumExpenses is of type Int (or any other number for that matter) then sumExpenses += textField will not compile. You need to take the text of that textField and convert it to an Int.
Again, I am still not super clear what you are trying to do, but please let me know if this works for you, or if you need further clarification.

UITextField has trailing the whitespace after secureTextEntry toggle

I am trying to toggle the password text field, but I am facing a problem of white space at right side of text field.
//Eye button action.
#IBAction func btnEyePassword(sender: AnyObject)
{
//If password text is secure.
if (self.passrordText.secureTextEntry == true)
{
self.passrordText.secureTextEntry = false
}
else
{
self.passrordText.secureTextEntry = true
}
}
Swift 4 solution
func togglePasswordVisibility() {
password.isSecureTextEntry = !password.isSecureTextEntry
if let textRange = password.textRange(from: password.beginningOfDocument, to: password.endOfDocument) {
password.replace(textRange, withText: password.text!)
}
}
You don't need extra if statement for simple toggle isSecureTextEntry property, but you should replace text this way to force UITextField recalculate text width to avoid extra space.
UPDATE
Swift 4.2
Instead of
password.isSecureTextEntry = !password.isSecureTextEntry
you can do this
password.isSecureTextEntry.toggle()
Dont worry, in both cases when you print your textField.text your data will be same, there would not be white space.
Check with print(self.passrordText.text)
Declare var str : String = ""
#IBAction func btnEyePassword(sender: AnyObject)
{
//If password text is secure.
if (self.passrordText.secureTextEntry == true)
{
self.passrordText.secureTextEntry = false
self.passrordText.text = ""
self.passrordText.text = str
print(self.passrordText.text)
}
else
{
self.passrordText.secureTextEntry = true
str = self.passrordText.text!
print(self.passrordText.text)
}
}
This happens because of space consumption, "W" keeps much space while "i" keep lesser space.

Resources