Reputation: 931
I am trying to make a UITextField
where the input is permanently displayed in currency format even though the user is editing. It should work like:
$0
user type 1
$1
user type 2
$12
user type 0
$120
user type 0
$1,200
user type 0
$12,000
I manage to get the final formatting using NumberFormatter
like
textField.text = (numberFormatter.string(from: NSNumber(value: Int(textField.text!)!)))?.components(separatedBy: ".")[0]
But once I format the text in the textField, subsequent input will cause error like:
$1,000
user type 0
$1,0000 //number formatter cannot recognise this as a format of currency
Is there anyway I can manipulate the input to allow the user to only need to key in numbers but the value is displayed in proper currency formatting with the symbol plus commas - e.g.$1,200,000. Regardless of the amount of digit - but logically up to 10 is enough.
My current textField shouldChangeCharactersInRange
is like this:
textField.text = numberFormatter.number(from: textField.text!)?.description
if(range.length == 1){ //Backspace
if(textField.text?.characters.count == 1){
textField.text = "$0"
return false
}
return true
}
if(string.trimmingCharacters(in: CharacterSet.decimalDigits.inverted) == ""){
textField.text = (numberFormatter.string(from: NSNumber(value: Int(textField.text!)!)))?.components(separatedBy: ".")[0]
return false
}
if(string == ".") {
textField.text = (numberFormatter.string(from: NSNumber(value: Int(textField.text!)!)))?.components(separatedBy: ".")[0]
return false
}
textField.text = (numberFormatter.string(from: NSNumber(value: Int(textField.text!.append(string)!)!)))?.components(separatedBy: ".")[0]
return false
Upvotes: 1
Views: 4576
Reputation: 1761
Try below code
extension ViewController: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let text: NSString = (textField.text ?? "") as NSString
let finalString = text.replacingCharacters(in: range, with: string)
// 'currency' is a String extension that doews all the number styling
amountTextField.text = finalString.currency
// returning 'false' so that textfield will not be updated here, instead from styling extension
return false
}
}
extension String {
var currency: String {
// removing all characters from string before formatting
let stringWithoutSymbol = self.replacingOccurrences(of: "$", with: "")
let stringWithoutComma = stringWithoutSymbol.replacingOccurrences(of: ",", with: "")
let styler = NumberFormatter()
styler.minimumFractionDigits = 0
styler.maximumFractionDigits = 0
styler.currencySymbol = "$"
styler.numberStyle = .currency
if let result = NumberFormatter().number(from: stringWithoutComma) {
return styler.string(from: result)!
}
return self
}
}
Upvotes: 6
Reputation: 160
// ViewController.swift
// TextfieldDemo
//
// Created by piyush sinroja on 21/12/16.
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
@IBOutlet weak var txtDigit: UITextField!
var strDigit: String = String()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let label = UILabel(frame: CGRect(x :0,y :0,width :10,height: 10))
label.text = "$"
self.txtDigit.leftViewMode = .always
self.txtDigit.leftView = label
txtDigit.layer.cornerRadius = 4.0
txtDigit.layer.masksToBounds = true
txtDigit.layer.borderColor = UIColor.lightGray.cgColor
txtDigit.layer.borderWidth = 1.0
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func textFieldDidBeginEditing(_ textField: UITextField) {
}
func textFieldDidEndEditing(_ textField: UITextField) {
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
return true
}
func textField(_ textFieldToChange: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textFieldToChange == txtDigit{
let aSet = NSCharacterSet(charactersIn:"0123456789").inverted
let compSepByCharInSet = string.components(separatedBy: aSet)
let numberFiltered = compSepByCharInSet.joined(separator: "")
if numberFiltered == "" {
let new = txtDigit.text!
let fsf = new.substring(to: new.index(new.endIndex, offsetBy: -1))
let currentString = fsf
let findStr = commaStrSet(currentString: currentString)
txtDigit.text = findStr
return false
}
else{
let currentString = (textFieldToChange.text! as NSString)
.replacingCharacters(in: range, with: string)
let findStr = commaStrSet(currentString: currentString)
txtDigit.text = findStr
}
}
return false
}
func commaStrSet(currentString: String) -> String {
var replaceStr = currentString.replacingOccurrences(of: ",", with: "")
let length = replaceStr.characters.count
switch length {
case 4:
replaceStr.insert(",", at: replaceStr.index(replaceStr.startIndex, offsetBy: 1))
case 5:
replaceStr.insert(",", at: replaceStr.index(replaceStr.startIndex, offsetBy: 2))
case 6:
replaceStr.insert(",", at: replaceStr.index(replaceStr.startIndex, offsetBy: 1))
replaceStr.insert(",", at: replaceStr.index(replaceStr.startIndex, offsetBy: 5))
case 7:
replaceStr.insert(",", at: replaceStr.index(replaceStr.startIndex, offsetBy: 2))
replaceStr.insert(",", at: replaceStr.index(replaceStr.startIndex, offsetBy: 5))
case 8:
replaceStr.insert(",", at: replaceStr.index(replaceStr.startIndex, offsetBy: 1))
replaceStr.insert(",", at: replaceStr.index(replaceStr.startIndex, offsetBy: 4))
replaceStr.insert(",", at: replaceStr.index(replaceStr.startIndex, offsetBy: 7))
case 9:
replaceStr.insert(",", at: replaceStr.index(replaceStr.startIndex, offsetBy: 2))
replaceStr.insert(",", at: replaceStr.index(replaceStr.startIndex, offsetBy: 5))
replaceStr.insert(",", at: replaceStr.index(replaceStr.startIndex, offsetBy: 8))
case 10:
replaceStr.insert(",", at: replaceStr.index(replaceStr.startIndex, offsetBy: 1))
replaceStr.insert(",", at: replaceStr.index(replaceStr.startIndex, offsetBy: 4))
replaceStr.insert(",", at: replaceStr.index(replaceStr.startIndex, offsetBy: 7))
replaceStr.insert(",", at: replaceStr.index(replaceStr.startIndex, offsetBy: 10))
case 11,12,13,14,15:
replaceStr.insert(",", at: replaceStr.index(replaceStr.startIndex, offsetBy: 1))
replaceStr.insert(",", at: replaceStr.index(replaceStr.startIndex, offsetBy: 4))
replaceStr.insert(",", at: replaceStr.index(replaceStr.startIndex, offsetBy: 7))
replaceStr.insert(",", at: replaceStr.index(replaceStr.startIndex, offsetBy: 10))
default:
break
}
return replaceStr
}
}
Upvotes: 0
Reputation: 20094
You can use the UITextField's leftView and rightView properties as shown below: This will only take care of displaying the "$" sign at the left of the input field.
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel(frame: CGRect(x :0,y :0,width :10,height: 10))
label.text = "$"
self.moneyTextField.leftViewMode = .always
self.moneyTextField.leftView = label
}
Upvotes: 0