如何使用户在SwiftUI文本字段中仅使用数字输入货币,同时保留美元和。?
现在,我有以下几点:如何使用户在SwiftUI文本字段中仅使用数字输入货币,同时保留美元和。?,swift,swiftui,textfield,currency,Swift,Swiftui,Textfield,Currency,现在,我有以下几点: private var currencyFormatter: NumberFormatter = { let f = NumberFormatter() // allow no currency symbol, extra digits, etc f.isLenient = true f.numberStyle = .currency return f }() 我希望文本字段以$0.00开头作为占位符,但当用户开始输入时,前两个输入
private var currencyFormatter: NumberFormatter = {
let f = NumberFormatter()
// allow no currency symbol, extra digits, etc
f.isLenient = true
f.numberStyle = .currency
return f
}()
我希望文本字段以$0.00开头作为占位符,但当用户开始输入时,前两个输入将填充在美分中。。。因此,5055将逐步显示为:
步骤1用户点击5:$0.05
步骤2用户点击0:$0.50
步骤3用户点击5:$5.05
步骤4用户点击5:$50.55
如果金额超过999美元,则将插入逗号
如何做到这一点?现在我的totalInput类型为Double?要创建允许用户从右到左键入金额的货币字段,您需要一个可观察的对象绑定管理器、货币编号格式化程序,并使用onChange方法在每次值更改时进行观察:
import SwiftUI
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
这是相当于我为UIKit实现的自定义的SwiftUI。要创建允许用户从右到左键入金额的货币字段,您需要一个可观察的对象绑定管理器、货币编号格式化程序,并使用onChange方法在每次值更改时进行观察:
import SwiftUI
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
这是SwiftUI,相当于我为UIKit实现的自定义界面。我创建了一个围绕UITextfield的组件 你可以在这里查看 这是演示
我已经创建了一个围绕UITextfield的组件 你可以在这里查看 这是演示
这不符合OP的要求。OP希望从右向左输入值。它也不显示$0.00。这不符合OP的要求。OP希望从右向左输入值。它也不显示$0.00。为什么在扩展中使用fileprivate?这是要放在它自己的视图中,而不是放在ContentView中,以便在应用程序中使用吗?这是为了避免在代码的其他地方更改格式化程序设置。只要保留货币设置,您可以将其删除以在应用程序中的其他地方使用。@Rolando如果您仍然需要使用十进制类型而不是双精度类型的帮助,请告诉我在表示此类内容时使用十进制与双精度是否更好?使用Int/Double的其他变量进行计算的转换看起来很混乱。是。Double它不如Decimal精确。查看这篇文章。确保始终使用字符串初始值设定项,否则在强制转换为十进制之前,您的值将被解释为Double。正如我在你的另一篇文章中所评论的,如果你需要强制你的十进制数为Double或Int,你需要先将它转换为NSDecimalNumber。我会一直使用十进制。为什么在扩展中使用fileprivate?这是要放在它自己的视图中,而不是放在ContentView中,以便在应用程序中使用吗?这是为了避免在代码的其他地方更改格式化程序设置。只要保留货币设置,您可以将其删除以在应用程序中的其他地方使用。@Rolando如果您仍然需要使用十进制类型而不是双精度类型的帮助,请告诉我在表示此类内容时使用十进制与双精度是否更好?使用Int/Double的其他变量进行计算的转换看起来很混乱。是。Double它不如Decimal精确。查看这篇文章。确保始终使用字符串初始值设定项,否则在强制转换为十进制之前,您的值将被解释为Double。正如我在你的另一篇文章中所评论的,如果你需要强制你的十进制数为Double或Int,你需要先将它转换为NSDecimalNumber。我会一直使用十进制。
struct ContentView: View {
@ObservedObject private var bindingManager = TextBindingManager(amount: 0)
var decimal: Decimal { bindingManager.text.decimal / pow(10, Formatter.currency.maximumFractionDigits) }
var maximum: Decimal = 999_999_999.99
@State private var lastValue: String = ""
@State private var locale: Locale = .current {
didSet { Formatter.currency.locale = locale }
}
var body: some View {
VStack(alignment: .leading) {
TextField(bindingManager.text, text: $bindingManager.text)
.keyboardType(.numberPad)
.multilineTextAlignment(.trailing) // this will keep the text aligned to the right
.onChange(of: bindingManager.text) { string in
if string.decimal > maximum {
self.bindingManager.text = lastValue
} else {
self.bindingManager.text = decimal.currency
lastValue = self.bindingManager.text
}
return
}
}
.padding()
.onAppear {
Formatter.currency.locale = locale
}
}
}
class TextBindingManager: ObservableObject {
@Published var text: String = ""
var amount: Decimal = .zero
init(amount: Decimal) {
self.amount = amount
self.text = Formatter.currency.string(for: amount) ?? "$0.00"
}
}
fileprivate extension Formatter {
static let currency: NumberFormatter = .init(numberStyle: .currency)
}
extension NumberFormatter {
convenience init(numberStyle: Style) {
self.init()
self.numberStyle = numberStyle
}
}
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) ?? "" }
}