0

I am doing a currency type UITextField. The behavior I need is like this:

enter image description here

I managed to create the decimal part. But I have problems adding in the grouping separators for thousands. How do I group the integer part here?

Here is the code so far:

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet weak var textField: UITextField!

    let DIGITS = ["0","1","2","3","4","5","6","7","8","9","0"]
    let DECIMAL_SEPERATOR = ","
    let THOUSAND_SEPERATOR = "."
    let DECIMAL_DIGITS = 2
    let DECIMAL_DIGITS_DEFAULT_STRING = "00"

    override func viewDidLoad() {
        super.viewDidLoad()
        textField.delegate = self
        textField.text = "0\(DECIMAL_SEPERATOR)\(DECIMAL_DIGITS_DEFAULT_STRING)"
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    //Textfield
    func textFieldTriggerDone(){

        if !textField.text!.containsString(DECIMAL_SEPERATOR){
            textField.text = textField.text! + DECIMAL_SEPERATOR + DECIMAL_DIGITS_DEFAULT_STRING
        }

        print("Add missing parts here")
    }

    func textFieldTriggerDecimalTyped(){
        textField.text = textField.text! + DECIMAL_SEPERATOR
    }

    func textFieldShouldAddNumber(text: String, range: NSRange, replacement: String, dots: Int) -> Bool{

        let nsstring = NSString(string: text)
        let decimalRange = nsstring.rangeOfString(DECIMAL_SEPERATOR)

        if range.location > decimalRange.location{

            let parts = text.componentsSeparatedByString(DECIMAL_SEPERATOR)
            if parts.count > 1{
                if parts[1].characters.count > (DECIMAL_DIGITS-1){
                    return false
                }
            }

        }else if range.location < decimalRange.location{

            if textField.text!.characters.count == 1 && textField.text! == "0"{
                textField.text = replacement
                return false
            }else{
                let insertIndex = text.startIndex.advancedBy(range.location-dots)
                var finalText = text

                if replacement.characters.count > 0 {
                    finalText.insert(replacement.characters.first!, atIndex: insertIndex)
                    textField.text = finalText
                    let begin = textField.beginningOfDocument
                    let pos = textField.positionFromPosition(begin, offset: (range.location+1))
                    let cursorpos = textField.textRangeFromPosition(pos!, toPosition: pos!)
                    textField.selectedTextRange = cursorpos
                    //textFieldAddThousandSeperators()
                    return false
                }
            }

        }

        return true
    }

    func textFieldShouldReturn(textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }

    func textFieldDidEndEditing(textField: UITextField) {
        textFieldTriggerDone()
    }

    func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {

        let dots = textField.text!.occurancesOf(THOUSAND_SEPERATOR)


        if string == ""{
            if textField.text!.characters.count > 1{
                return true
            }else{
                textField.text! = "0"
                return false
            }
        }

        if range.location == 0 && string == "0"{
            return false
        }

        if DIGITS.contains(string){
            return textFieldShouldAddNumber(textField.text!, range: range, replacement: string, dots: dots)
        }

        if string == DECIMAL_SEPERATOR || string == THOUSAND_SEPERATOR{

            if textField.text!.containsString(DECIMAL_SEPERATOR){
                return false
            }

            textFieldTriggerDecimalTyped()
            return false
        }

        return false
    }

}
Swifty Swift
  • 175
  • 1
  • 1
  • 7

1 Answers1

0

Check out this answer: https://stackoverflow.com/a/40469426/6863743

code:

override func viewDidLoad() {
    super.viewDidLoad()

    textField.addTarget(self, action: #selector(myTextFieldDidChange), for: .editingChanged)
}

func myTextFieldDidChange(_ textField: UITextField) {

    if let amountString = textField.text?.currencyInputFormatting() {
        textField.text = amountString
    }
}

extension String {

// formatting text for currency textField
func currencyInputFormatting() -> String {

    var number: NSNumber!
    let formatter = NumberFormatter()
    formatter.numberStyle = .currencyAccounting
    formatter.currencySymbol = "$"
    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))

    // if first number is 0 or all numbers were deleted
    guard number != 0 as NSNumber else {
        return ""
    }

    return formatter.string(from: number)!
    }
}

To get it to convert back to something you can do calculations with, you can just write a function to take out the "," and "." and so on.

Hope this helps a bit, it's the best solution I've found for currency formatting.

JoniVR
  • 1,839
  • 1
  • 22
  • 36