问题
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.12
- When the user taps 3. We should display
1.23
- When the user taps 4. We should display
12.34
How can I do that with Swift?
回答1:
For Swift 3. Input currency format on a text field (from right to left)
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)!
}
}
回答2:
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 • Swift 5.1 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?) {
print(#function)
// 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
print("doubleValue:", doubleValue)
}
}
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 { $0.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(#function)
print("currencyField:",currencyField.text!)
print("decimal:", currencyField.decimal)
print("doubleValue:",(currencyField.decimal as NSDecimalNumber).doubleValue, terminator: "\n\n")
}
}
Sample project
回答3:
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
回答4:
My final code thanks for your help
extension Double {
var twoDigits: Double {
let nf = NSNumberFormatter()
nf.numberStyle = NSNumberFormatterStyle.DecimalStyle
nf.minimumFractionDigits = 2
nf.maximumFractionDigits = 2
return self
}
}
var cleanText:String!
let number:String = sender.currentTitle as String!
if(amountDisplay.text != nil)
{
cleanText = String(Array(amountDisplay.text!).map{String($0)}.filter{ $0.toInt() != nil }.map{Character($0)} ) as String
cleanText = cleanText + number
}else{
cleanText = number
}
amount = (Double(cleanText.toInt()!) / 100).twoDigits
formatter.locale = NSLocale(localeIdentifier: currencies[current_currency_index])
amountDisplay.text = "\(formatter.stringFromNumber(amount!)!)"
回答5:
Here is a code for swift 2
@IBOutlet weak var txtAmount: UITextField!
//MARK: - UITextField Delegate -
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool{
if string.characters.count == 0 {
return true
}
let userEnteredString = textField.text ?? ""
var newString = (userEnteredString as NSString).stringByReplacingCharactersInRange(range, withString: string) as NSString
newString = newString.stringByReplacingOccurrencesOfString(".", withString: "")
let centAmount : NSInteger = newString.integerValue
let amount = (Double(centAmount) / 100.0)
if newString.length < 16 {
let str = String(format: "%0.2f", arguments: [amount])
txtAmount.text = str
}
return false //return false for exact out put
}
Note : Connect delegate for textField from storyboard or programatically
回答6:
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
来源:https://stackoverflow.com/questions/29782982/how-to-input-currency-format-on-a-text-field-from-right-to-left-using-swift