How to input currency format on a text field (from right to left) using Swift?

前端 未结 9 1044
甜味超标
甜味超标 2020-11-22 01:57

I have a number let’s say 0.00.

  • When the user taps 1. We should have 0.01
  • When the user taps 2. We should display 0.
相关标签:
9条回答
  • 2020-11-22 02:42

    Try this piece of code:

    struct DotNum {
      private var fraction:String = ""
      private var intval:String = ""
      init() {}
      mutating func enter(s:String) {
        if count(fraction) < 2 {
          fraction = s + fraction
        } else {
          intval = s + intval
        }
      }
      private var sFract:String {
        if count(fraction) == 0 { return "00" }
        if count(fraction) == 1 { return "0\(fraction)" }
        return fraction
      }
      var stringVal:String {
        if intval == ""  { return "0.\(sFract)" }
        return "\(intval).\(sFract)"
      }
    }
    var val = DotNum()
    val.enter("1")
    val.stringVal
    val.enter("2")
    val.stringVal
    val.enter("3")
    val.stringVal
    val.enter("4")
    val.stringVal
    
    0 讨论(0)
  • 2020-11-22 02:45

    You can create a currency text field subclassing UITextField. Add a target for UIControlEvents .editingChanged. Add a selector method to filter the digits from your textfield string. After filtering all non digits from your string you can format again your number using NumberFormatter as follow:

    Xcode 11.5 • Swift 5.2 or later

    import UIKit
    
    class CurrencyField: UITextField {
        var decimal: Decimal { string.decimal / pow(10, Formatter.currency.maximumFractionDigits) }
        var maximum: Decimal = 999_999_999.99
        private var lastValue: String?
        var locale: Locale = .current {
            didSet {
                Formatter.currency.locale = locale
                sendActions(for: .editingChanged)
            }
        }
        override func willMove(toSuperview newSuperview: UIView?) {
            // you can make it a fixed locale currency if needed
            // self.locale = Locale(identifier: "pt_BR") // or "en_US", "fr_FR", etc
            Formatter.currency.locale = locale
            addTarget(self, action: #selector(editingChanged), for: .editingChanged)
            keyboardType = .numberPad
            textAlignment = .right
            sendActions(for: .editingChanged)
        }
        override func deleteBackward() {
            text = string.digits.dropLast().string
            // manually send the editingChanged event
            sendActions(for: .editingChanged)
        }
        @objc func editingChanged() {
            guard decimal <= maximum else {
                text = lastValue
                return
            }
            text = decimal.currency
            lastValue = text
        }
    }
    

    extension CurrencyField {
        var doubleValue: Double { (decimal as NSDecimalNumber).doubleValue }
    }
    

    extension UITextField {
         var string: String { text ?? "" }
    }
    

    extension NumberFormatter {
        convenience init(numberStyle: Style) {
            self.init()
            self.numberStyle = numberStyle
        }
    }
    

    private extension Formatter {
        static let currency: NumberFormatter = .init(numberStyle: .currency)
    }
    

    extension StringProtocol where Self: RangeReplaceableCollection {
        var digits: Self { filter (\.isWholeNumber) }
    }
    

    extension String {
        var decimal: Decimal { Decimal(string: digits) ?? 0 }
    }
    

    extension Decimal {
        var currency: String { Formatter.currency.string(for: self) ?? "" }
    }
    

    extension LosslessStringConvertible {
        var string: String { .init(self) }
    }
    

    View Controller

    class ViewController: UIViewController {
    
        @IBOutlet weak var currencyField: CurrencyField!
        override func viewDidLoad() {
            super.viewDidLoad()
            currencyField.addTarget(self, action: #selector(currencyFieldChanged), for: .editingChanged)
            currencyField.locale = Locale(identifier: "pt_BR") // or "en_US", "fr_FR", etc
        }
        @objc func currencyFieldChanged() {
            print("currencyField:",currencyField.text!)
            print("decimal:", currencyField.decimal)
            print("doubleValue:",(currencyField.decimal as NSDecimalNumber).doubleValue, terminator: "\n\n")
        }
    }
    

    Sample project

    0 讨论(0)
  • 2020-11-22 02:45

    Just for fun: copied Thomas's answer (full credits -and points- to him please) into a file to run as a Swift 4.1 script (with minor fixes):

    dotnum.swift:

    #!/usr/bin/swift
    
    struct DotNum {
        private var fraction:String = ""
        private var intval:String = ""
        init() {}
        mutating func enter(_ s:String) {
            if fraction.count < 2 {
              fraction = s + fraction
            } else {
              intval = s + intval
            }
        }
        private var sFract:String {
            if fraction.count == 0 { return "00" }
            if fraction.count == 1 { return "0\(fraction)" }
            return fraction
        }
        var stringVal:String {
            if intval == ""  { return "0.\(sFract)" }
            return "\(intval).\(sFract)"
        }
    }
    
    var val = DotNum()
    val.enter("1")
    print(val.stringVal)
    val.enter("2")
    print(val.stringVal)
    val.enter("3")
    print(val.stringVal)
    val.enter("4")
    print(val.stringVal)
    

    Then run it in a terminal:

    $ chmod +x dotnum.swift
    $ ./dotnum.swift
    0.01
    0.21
    3.21
    43.21
    
    0 讨论(0)
提交回复
热议问题