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
}
}