Ben Ong
Ben Ong

Reputation: 931

UITextField display formatted as currency without decimal

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

Answers (3)

Matt
Matt

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

Piyush Sinroja
Piyush Sinroja

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

azamsharp
azamsharp

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

Related Questions