I am looking for a textfield currency formatter such that it fulfils the following criterias:
- It should be formatted(comma separated) as I am typing
- 10 digits before decimal point and 2 digits after it, should be allowed
- It should allow a regex for (2)
- When we cut, cursor should remain at the same place
- When we type in the middle of the currency, cursor should not shift to left.
- It should support localization (Commas and Periods) in regex.
I have tried alot of solutions:
Using
NSCharacterSet
(This is the closest but regex fails here due to interchange of.
and,
during localization, also we have used.decimal
type here to avoid the$
in textField)class func checkTextField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { guard let textBeforeEditing = textField.text else { return true } if ((string == "0" || string == "") && (textField.text! as NSString).range(of: ".").location < range.location) { return true } var currentPosition = 0 if let selectedRange = textField.selectedTextRange { currentPosition = textField.offset(from: textField.beginningOfDocument, to: string == "" ? selectedRange.end : selectedRange.start) } let allowedCharacterSet = NSCharacterSet(charactersIn: "0123456789.").inverted let filtered = string.components(separatedBy: allowedCharacterSet) let component = filtered.joined(separator: "") let isNumeric = string.replacingOccurrences(of: ",", with: "") == component var textFieldString : String = "" var numberWithoutCommas : String = "" guard isNumeric else { return false } let formatter = NumberFormatter() formatter.numberStyle = .decimal textFieldString = (textField.text! as NSString).replacingCharacters(in: range, with: string) numberWithoutCommas = textFieldString.replacingOccurrences(of: ",", with: "") let formattedNumberWithoutCommas = formatter.number(from: numberWithoutCommas) guard let formattedNumber = formattedNumberWithoutCommas, var formattedString = formatter.string(from: formattedNumber) else { textField.text = nil return false } if string == "." && range.location == textField.text?.count { formattedString = formattedString.appending(".") } textField.text = formattedString currentPosition = getCursorPositionForTextField(string: string, cursorPosition: currentPosition, formattedString: formattedString, textBeforeEditing: textBeforeEditing) handleTextFieldCursor(cursorPosition: currentPosition, textField: textField) return false }
Using
NumberFormatter
but cursor shifts to end on every cut/pasteextension String { func currencyInputFormatting() -> String { var number: NSNumber! let formatter = NumberFormatter() formatter.numberStyle = .currency formatter.maximumFractionDigits = 2 formatter.minimumFractionDigits = 2 var amountWithPrefix = self // remove from String: "$", ".", "," let regex = try! NSRegularExpression(pattern: "[^0-9]", options: .caseInsensitive) amountWithPrefix = regex.stringByReplacingMatches(in: amountWithPrefix, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, self.characters.count), withTemplate: "") let double = (amountWithPrefix as NSString).doubleValue number = NSNumber(value: (double / 100)) guard number != 0 as NSNumber else { return "" } return formatter.string(from: number)! } }
I have spent almost a day or two finding a 100% workable solution but not able to resolve. Any help will be appreciated
EDIT
I have come quite close to the solution with the help of the @denis_lor answer but still unable to achieve the interchange of comma with period. Here's my updated code, am I missing something? It works fine with english but not with spanish.
class func checkTextField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let textBeforeEditing = textField.text else {
return true
}
if ((string == "0" || string == "") && (textField.text! as NSString).range(of: "\(NSLocalizedString("core_decimal_separator_symbol", comment: ""))").location < range.location) {
return true
}
var currentPosition = 0
if let selectedRange = textField.selectedTextRange {
currentPosition = textField.offset(from: textField.beginningOfDocument, to: string == "" ? selectedRange.end : selectedRange.start)
}
let allowedCharacterSet = NSCharacterSet(charactersIn: "0123456789\(NSLocalizedString("core_decimal_separator_symbol", comment: ""))").inverted
let filtered = string.components(separatedBy: allowedCharacterSet)
let component = filtered.joined(separator: "")
let isNumeric = string.replacingOccurrences(of: NSLocalizedString("core_thousand_separator_symbol", comment: ""), with: "") == component
var textFieldString : String = ""
var numberWithoutCommas : String = ""
guard isNumeric else {
return false
}
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
textFieldString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
numberWithoutCommas = textFieldString.replacingOccurrences(of: NSLocalizedString("core_thousand_separator_symbol", comment: ""), with: "")
let formattedNumberWithoutCommas = formatter.number(from: numberWithoutCommas)
guard let formattedNumber = formattedNumberWithoutCommas, var formattedString = formatter.string(from: formattedNumber) else {
textField.text = nil
return false
}
if string == NSLocalizedString("core_decimal_separator_symbol", comment: "") && range.location == textField.text?.count {
formattedString = formattedString.appending(NSLocalizedString("core_decimal_separator_symbol", comment: ""))
}
textField.text = formattedString
currentPosition = getCursorPositionForTextField(string: string, cursorPosition: currentPosition, formattedString: formattedString, textBeforeEditing: textBeforeEditing)
handleTextFieldCursor(cursorPosition: currentPosition, textField: textField)
return false
}