SwiftUI实现TextField输入时实时货币格式化的问题
在SwiftUI TextField中实时显示BRL格式化货币
要实现TextField内实时展示BRL格式的货币,核心是处理原始数值和格式化显示文本的双向绑定,同时解决格式化后光标乱跑的问题,以下是完整实现方案:
1. 自定义货币格式化工具
先封装工具类处理数值与BRL货币字符串的互转:
import Foundation class CurrencyFormatter { static let brlFormatter: NumberFormatter = { let formatter = NumberFormatter() formatter.locale = Locale(identifier: "pt_BR") formatter.numberStyle = .currency formatter.minimumFractionDigits = 2 formatter.maximumFractionDigits = 2 return formatter }() // 数值转BRL货币字符串 static func formatToBRL(value: Double) -> String { brlFormatter.string(from: NSNumber(value: value)) ?? "R$ 0,00" } // BRL货币字符串转数值(去除格式符号) static func parseFromBRL(text: String) -> Double { let cleanedText = text.replacingOccurrences(of: "R$ ", with: "") .replacingOccurrences(of: ".", with: "") .replacingOccurrences(of: ",", with: ".") return Double(cleanedText) ?? 0.0 } }
2. 带光标控制的自定义TextField
SwiftUI原生TextField格式化后光标会自动跳到末尾,需通过UIViewRepresentable封装UITextField手动控制光标位置:
import SwiftUI import UIKit struct CurrencyTextField: UIViewRepresentable { @Binding var value: Double var placeholder: String func makeUIView(context: Context) -> UITextField { let textField = UITextField() textField.placeholder = placeholder textField.keyboardType = .decimalPad textField.addTarget(context.coordinator, action: #selector(Coordinator.textChanged), for: .editingChanged) textField.delegate = context.coordinator return textField } func updateUIView(_ uiView: UITextField, context: Context) { // 仅当文本不一致时更新,避免光标位置被重置 let formattedText = CurrencyFormatter.formatToBRL(value: value) if uiView.text != formattedText { let selectedRange = uiView.selectedTextRange uiView.text = formattedText uiView.selectedTextRange = selectedRange } } func makeCoordinator() -> Coordinator { Coordinator(parent: self) } class Coordinator: NSObject, UITextFieldDelegate { let parent: CurrencyTextField init(parent: CurrencyTextField) { self.parent = parent } @objc func textChanged(_ textField: UITextField) { guard let text = textField.text else { return } parent.value = CurrencyFormatter.parseFromBRL(text: text) } // 控制光标位置,避免格式化后跳到末尾 func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { let currentPosition = textField.offset(from: textField.beginningOfDocument, to: textField.selectedTextRange!.start) let newText = (textField.text as NSString?)?.replacingCharacters(in: range, with: string) ?? "" let newValue = CurrencyFormatter.parseFromBRL(text: newText) let formattedText = CurrencyFormatter.formatToBRL(value: newValue) var newPosition = currentPosition + string.count if string.isEmpty { newPosition -= 1 } // 避免光标跑到R$符号前面 if newPosition <= 3 { newPosition = 3 } textField.text = formattedText if let newRange = textField.position(from: textField.beginningOfDocument, offset: newPosition) { textField.selectedTextRange = textField.textRange(from: newRange, to: newRange) } return false } } }
3. 在View中使用自定义TextField
struct ContentView: View { @State private var amount: Double = 0.0 var body: some View { VStack(spacing: 20) { CurrencyTextField(value: $amount, placeholder: "Digite o valor") .padding() .border(.gray) Text("Valor formatado: \(CurrencyFormatter.formatToBRL(value: amount))") } .padding() } }
关键说明
- 双向绑定处理:通过自定义Binding和格式化工具,实现原始数值与格式化文本的同步更新
- 光标控制:利用UITextField代理方法手动计算并恢复光标位置,解决格式化后光标跳转问题
- 输入过滤:自动去除非数字格式符号,确保数值解析的准确性
内容的提问来源于stack exchange,提问作者Ricieri Faviero
相关产品推荐
相关产品推荐

