Adding Thousand Separator Automatically (Swift) - ios

I have a code for my calculator
How could I do so that when the user would enter the numbers he would be separated by a space automatically?
I've been trying to find an answer for a long time, but nothing fits that it was displayed right away
var currentInput: Double {
get {
return Double (displayResultLabel.text!)!
}
set {
let value = "\(newValue)"
let ValueArray = (value.components(separatedBy:"."))
if ValueArray[1] == "0" {
displayResultLabel.text = "\(ValueArray[0])"
} else {
displayResultLabel.text = "\(newValue)"
}
stillTyping = false
}
}
#IBAction func numberPressed(_ sender: UIButton) {
let number = sender.currentTitle!
if stillTyping {
if (displayResultLabel.text?.characters.count)! < 14 {
displayResultLabel.text = displayResultLabel.text! + number
}
} else {
displayResultLabel.text = number
stillTyping = true
}
}
Then what happened:
#IBAction func numberPressed(_ sender: UIButton) {
let number = sender.currentTitle!
if stillTyping {
if (displayResultLabel.text?.characters.count)! < 14 {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
let newNumber = NSNumber(value: Double(displayResultLabel.text! + number)!)
displayResultLabel.text = formatter.string(from: newNumber)
}
} else {
displayResultLabel.text = number
stillTyping = true
}
}
Error
var stillTyping = false
var dotIsPlaced = false
var firstOperand: Double = 0
var secondOperand: Double = 0
var operationSign: String = ""

It is better to accumulate your value in a separate string that doesn't have the formatting applied rather than using the text field as your data model. You can then format the decimal and display it in the label as required using a NumberFormatter:
let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
return formatter
}()
var currentInput: String = "0" {
didSet {
self.displayResultLabel?.text = self.currentDisplay
}
var currentValue: Double {
return Double(self.currentInput) ?? 0
}
var currentDisplay: String {
return formatter.string(from: NSNumber(value:self.currentValue)) ?? "0"
}
func addDigit(_ digit: Int) {
if currentInput.count < 14 {
let newValue = self.currentValue * 10 + Double(digit)
self.currentInput = "\(newValue)"
}
}
#IBAction func numberPressed(_ sender: UIButton) {
guard let digit = Int(sender.currentTitle!) else {
return
}
self.addDigit(digit)
}

This is what NumberFormatters are for
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
if let newNumber = formatter.number(from: displayResultLabel.text! + number){
displayResultLabel.text = formatter.string(from: newNumber)
}
Note that NumberFormatters go both ways, and you can (and probably should) use them to parse numbers from strings, too

Related

Changes in the datepicker widgets in iOS?

In our iOS app we have a timepicker widget to book appointments. This has worked fine for a long time. We just changed iOS developer and when he rebuilt the app the timepicker is broken for some reason we cannot tell. Has something changed recently in XCode/iOS that could explain why the layout suddenly is broken?
How it was
How it is now
This is the code responsible:
This timepicker is based on the standard iOS UIDatePicker.
class TimePickerController: UIViewController {
#IBOutlet weak var notAvailableLbl: UILabel!
#IBOutlet weak var fromPicker: UIDatePicker!
#IBOutlet weak var toPicker: UIDatePicker!
#IBOutlet weak var switchNotAvailable: UISwitch!
var object: EventElement?
var delegate:TimePickerControllerDelegate?
var name: DEFAULT_AVAILABILITY_CONST!
var startTime,endTime: Date?
var isFromSettings:Bool = false
var isNotAvailable: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.barTintColor = AppColors.Primary
switchNotAvailable.setOn(isNotAvailable, animated: true)
switchChanged(switchNotAvailable)
setPickerLocale(withIdentifier: "en_GB")
setPickerTimezone(withIdentifier: "UTC")
initPickerDates()
if object?.type == EVENT_CONST.lunch{
switchNotAvailable.isHidden = true
notAvailableLbl.isHidden = true
}else{
switchNotAvailable.isHidden = false
notAvailableLbl.isHidden = false
}
}
func setPickerLocale(withIdentifier locale: String) {
// Changing to 24 hrs
let local = NSLocale(localeIdentifier: locale) as Locale
fromPicker.locale = local
toPicker.locale = local
}
func setPickerTimezone(withIdentifier timeZone: String) {
//set timezone
fromPicker.timeZone = TimeZone.init(identifier: timeZone)
toPicker.timeZone = TimeZone.init(identifier: timeZone)
}
func initPickerDates() {
if let obj = object {
let startTime = obj.start.split(separator: "Z")
let endTime = obj.end.split(separator: "Z")
if(startTime.first == endTime.first)
{
fromPicker.date = getTimeFromString(stringTime: String("08:00"))
toPicker.date = getTimeFromString(stringTime: String("22:00"))
}
else{
fromPicker.date = getTimeFromString(stringTime: String(startTime.first ?? "08:00"))
toPicker.date = getTimeFromString(stringTime: String(endTime.first ?? "22:00"))
}
}else {
if let start = startTime,let end = endTime {
if(start == end)
{
// 8.00 to 22.00
fromPicker.date = start
toPicker.date = end.dateByAddingHours(hours: 14)
}
else{
fromPicker.date = start
toPicker.date = end
}
}
}
}
func getTimeFromString(stringTime: String) -> Date
{
let local = NSLocale(localeIdentifier: "en_GB") as Locale
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm"
dateFormatter.locale = local
dateFormatter.timeZone = TimeZone.init(identifier: "UTC")
let date = dateFormatter.date(from: stringTime) ?? Date()
print(date)
return date
}
#IBAction func btnDone(_ sender: Any) {
print(fromPicker.date)
print(toPicker.date)
updateEvent()
}
func updateEvent() {
var start = fromPicker.date.timeDisplay + "Z"
var end = toPicker.date.timeDisplay + "Z"
var createdDate:String = ""
var id:String = ""
var name:String = ""
if object == nil {
start = fromPicker.date.ISOISO8601StringForAvailability + "Z"
end = toPicker.date.ISOISO8601StringForAvailability + "Z"
createdDate = Date().toString(format: DateFormat.Custom("yyyy-MM-dd'T'HH:mm:ss")) + "Z"
id = UUID().uuidString.lowercased()
name = self.name.rawValue
}
else{
createdDate = object?.created ?? ""
id = object?.id ?? ""
name = (object?.name.rawValue) ?? ""
}
print("switch state",switchNotAvailable.isOn)
if switchNotAvailable.isOn {
if let startT = startTime,let endT = endTime {
let startDt = "08:00Z".formatDateWithTime(referenceDate: startT)
let endDt = "08:00Z".formatDateWithTime(referenceDate: endT)
start = startDt!.toString(format: DateFormat.Custom("yyyy-MM-dd'T'HH:mm"),timezone: "UTC") + "Z"
end = endDt!.toString(format: DateFormat.Custom("yyyy-MM-dd'T'HH:mm"),timezone: "UTC") + "Z"
}
else{
start = "08:00Z"
end = "08:00Z"
}
}
ActivityIndicator().showIndicator(backgroundColor: nil)
print("start and end date : ",start," ",end)
let type:String?
if isFromSettings == true{
type = object?.type.rawValue
}
else{
type = "AVAILABILITY_PATCH"
}
print(type ?? "AVAILABILITY_PATCH")
APICalls().updateAvailability(start: start, end: end, name: name, type: type ?? "AVAILABILITY_PATCH", created: createdDate,id: id ) { (response) in
ActivityIndicator().hideIndicator()
if let result = response
{
let res = Event.init(dictionary: result as? NSDictionary ?? [:])
DatabaseManager.getInstance().saveAvailability(object: res)
self.delegate?.didSelectTime(from: self.fromPicker.date, to: self.toPicker.date)
self.navigationController?.popViewController(animated: true)
}
}
}
To disable this time picker style, you have to add below line.
fromPicker.preferredDatePickerStyle = .wheels
In IOS 14 we have to define DatePickerStyle.
Please write these two lines in initPickerDates() method to set picker wheel style
fromPicker.preferredDatePickerStyle = .wheels
toPicker.preferredDatePickerStyle = .wheels
In IOS 14, we have multiple styles for date pickers. So this is the default behavior in iOS 14.
You just need to add the following line in the initPickerDates() method.
fromPicker.preferredDatePickerStyle = .wheels
toPicker.preferredDatePickerStyle = .wheels

Swift, converting a number to string, number gets rounded down

I'm having a bit of issue with my code...right now, I am passing a string containing a bunch of numbers, to get converted to a number, comma separators added, then converted back to a string for output. When I add a decimal to my string and pass it in, a number like 996.3658 get truncated to 996.366...
"currentNumber" is my input value, "textOutputToScreen" is my output...
func formatNumber() {
let charset = CharacterSet(charactersIn: ".")
if let _ = currentNumber.rangeOfCharacter(from: charset) {
if let number = Float(currentNumber) {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
guard let formattedNumber = numberFormatter.string(from: NSNumber(value: number)) else { return }
textOutputToScreen = String(formattedNumber)
}
}
else {
if let number = Int(currentNumber) {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
guard let formattedNumber = numberFormatter.string(from: NSNumber(value: number)) else { return }
textOutputToScreen = String(formattedNumber)
}
}
}
Thank you in advance for your help!
The issue there is that you have to set your NumberFormatter minimumFractionDigits to 4. Btw there is no need to initialize a NSNumber object. You can use Formatters string(for: Any) method and pass your Float. Btw I would use a Double (64-bit) instead of a Float (32-bit) and there is no need to initialize a new string g from your formattedNumber object. It is already a String.
Another thing is that you don't need to know the location of the period you can simply use contains instead of rangeOfCharacter method. Your code should look something like this:
extension Formatter {
static let number: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
return formatter
}()
}
func formatNumber(from string: String) -> String? {
if string.contains(".") {
guard let value = Double(string) else { return nil }
Formatter.number.minimumFractionDigits = 4
return Formatter.number.string(for: value)
} else {
guard let value = Int(string) else { return nil }
Formatter.number.minimumFractionDigits = 0
return Formatter.number.string(for: value)
}
}
let label = UILabel()
let currentNumber = "996.3658"
label.text = formatNumber(from: currentNumber) // "996.3658\n"
If you would like to assign the result to your var instead of a label
if let formatted = formatNumber(from: currentNumber) {
textOutputToScreen = formatted
}

How to make a full decimal point for a calculator on swift?

I've just started to study Xcode.
I've made all digits and math signs, but have no clue how to make a dot for calculator.
This is what I've done (deleted some repeating parts of math operations):
class ViewController: UIViewController {
var numberFromScreen: Double = 0
var firstNum: Double = 0
var operation: Int = 0
var mathSign: Bool = false
#IBOutlet weak var result: UILabel!
#IBAction func digits(_ sender: UIButton) {
if mathSign == true {
result.text = String (sender.tag)
mathSign = false
}
else {
result.text = result.text! + String (sender.tag)
}
numberFromScreen = Double (result.text!)!
}
#IBAction func buttons(_ sender: UIButton) {
if result.text != "" && sender.tag != 10 && sender.tag != 15 {
firstNum = Double (result.text!)!
if sender.tag == 11 {// divine
result.text = "/"
}
operation = sender.tag
mathSign = true
}
else if sender.tag == 15 {// calculate
if operation == 11 {
result.text = String(firstNum / numberFromScreen)
}
}
else if sender.tag == 10 {
result.text = ""
firstNum = 0
numberFromScreen = 0
operation = 0
}
}
}
In your case using the NumberFormatter would be a good option.
You can define it as follows:
private var formater: NumberFormatter {
let formater = NumberFormatter()
formater.maximumIntegerDigits = 9 // Change this value
formater.maximumFractionDigits = 9 // Change this value
formater.minimumFractionDigits = 9 // Change this value
formater.minimumIntegerDigits = 1 // Change this value
formater.maximumIntegerDigits = 9 // Change this value
formater.groupingSeparator = " "
formater.locale = Locale.current
formater.numberStyle = .decimal
return formater
}
And when you are setting the result to the label, you can go:
result.text = formater.string(from: NSDecimalNumber(value: yourValue))
If you are making a calculator I would recommend you that for bigger numbers or for numbers with many decimal places, you set the numberStyle property to .scientific.

Adding Double and a String to then display on a UILabel

I am trying to add up a value that is entered in the text field with a value specified as a double and then returning the value on a label. The code that I have is :
#IBOutlet weak var enterField: UITextField!
var weekOneTotal:Double = 0
#IBAction func addButton(_ sender: Any) {
addCorrectValue()
}
func addCorrectValue () {
guard let addAmount = convertAmount(input: enterField.text!) else {
print("Invalid amount")
return
}
let newValue = weekOneTotal += addAmount
secondScreen.weekOneAmountLabel.text = String(newValue)
}
func convertAmount (input:String) -> Double? {
let numberFormatter = NumberFormatter ()
numberFormatter.numberStyle = .decimal
return numberFormatter.number(from: input)?.doubleValue
}
Try this:
func addCorrectValue () {
guard let addAmount = Double(enterField.text!) else {
print("Invalid amount")
return
}
let newValue = weekOneTotal + addAmount
secondScreen.weekOneAmountLabel.text = "\(String(format: "%.1f", newValue))"
}
The .1 is the number of decimals that are shown. You can adjust that to your needs. Hope I understood the question and this works for you!
You probably want to increase value of weekOneTotal variable by converted amount and then you want to use this value as text of some label
weekOneTotal += addAmount
secondScreen.weekOneAmountLabel.text = String(weekOneTotal)

How to display indian and euro locale number value in words?

I am struggling to show the Indian and Euro Locale value in words. Please anyone help on this.
var outputString = ""
let valueString = "9,58,888.875"
let valueFormatter = NSNumberFormatter()
let outputFormatter = NSNumberFormatter()
outputFormatter.numberStyle = NSNumberFormatterStyle.DecimalStyle
valueFormatter.numberStyle = NSNumberFormatterStyle.SpellOutStyle
outputString = valueFormatter.stringFromNumber(outputFormatter.numberFromString(valueString as String)!)!
print(outputString)
I know it's very late. I have tried following solution, might not the best one but it's working for me. Hope it will be useful.
Note: - I have considered only till crore scale
#IBAction func startBtnClicked(sender: AnyObject) {
if var amountStr = ammountTextField.text
{
let numberFormater = NSNumberFormatter()
if amountStr.containsString(",")
{
amountStr = amountStr.stringByReplacingOccurrencesOfString(",", withString: "")
}
//eg: - amountStr = "45678432"
if let amountNumber = numberFormater.numberFromString(amountStr)
{
//First get currency format
let currencyFormatter = NSNumberFormatter()
currencyFormatter.numberStyle = .CurrencyStyle
currencyFormatter.currencySymbol = ""
currencyFormatter.locale = NSLocale(localeIdentifier: "en_IN")
let amountInCurrencyFormatStr = currencyFormatter.stringFromNumber(amountNumber)
//amountInCurrencyFormatStr = "4,56,78,432"
var outputStr = ""
//Get each component in array
if let amountInCurrencyFormatComponentArray = amountInCurrencyFormatStr?.componentsSeparatedByString(",")
{
//[4,56,78,432]
let scaleStrArr = ["thousand","lakh","crore"]
let spellFormatter = NSNumberFormatter()
spellFormatter.numberStyle = .SpellOutStyle
var scaleStrCount = 0
for var i = (amountInCurrencyFormatComponentArray.count - 1) ; i >= 0 ; i--
{
//Iterate array, and append scale strings (["thousand","lakh","crore"]) at proper place
if let currencyStr = spellFormatter.stringFromNumber(numberFormater.numberFromString(amountInCurrencyFormatComponentArray[i])!)
{
if i == (amountInCurrencyFormatComponentArray.count - 1)
{
outputStr = currencyStr
}
else
{
if scaleStrCount < scaleStrArr.count
{
outputStr = "\(currencyStr) \(scaleStrArr[scaleStrCount]) \(outputStr)"
scaleStrCount++
}
else
{
outputStr = "\(currencyStr) \(outputStr)"
}
}
}
else
{
print("ERROR: - Unable to spell")
}
}
}
else
{
print("ERROR: - No , in text field")
}
ammountTextField.text = amountInCurrencyFormatStr
print("OUTPUT --> \(outputStr)")
//OUTPUT -> "four crore fifty-six lakh seventy-eight thousand four hundred thirty-two"
}
else
{
print("ERROR: - No Number in text field")
}
}
else
{
print("ERROR: - No value in text field")
}
}

Resources