You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 17:40:23