升级Xcode 15.0后TextField输入异常问题求助
Xcode 15.0 TextField输入异常问题:原因与修复方案
问题诊断
你遇到的是Xcode 15.0(搭配macOS Sonoma 14.0)下SwiftUI TextField绑定跨视图传递的ObservableObject@Published值时的格式化同步bug,具体诱因包括:
- 每次视图刷新都会调用
decimals.decimalPlaces()生成新的NumberFormatter实例,导致TextField频繁重置绑定逻辑,输入时出现值的错误拼接。 - Xcode 15对Sheet跨视图的ObservableObject状态同步逻辑做了调整,Double类型的浮点精度特性放大了格式化解析错误。
- 父视图创建ViewModel并传递给Sheet时,Sheet的视图更新触发时机与父视图的状态监听形成冲突,进一步加剧了输入值的异常拼接。
修复方案
1. 用Decimal替代Double存储价格
货币类数值使用Double存在精度丢失风险,改用Decimal更安全,同时能更好适配NumberFormatter的解析逻辑。
2. 复用NumberFormatter实例
避免每次视图刷新生成新的Formatter,改为在Decimals类中初始化一次并全局复用。
3. 优化视图布局与绑定逻辑
简化Price视图的嵌套结构,确保TextField的绑定逻辑稳定。
修改后的完整代码
struct ContentView: View { @StateObject var decimals = Decimals() @StateObject var priceVM = Price.ViewModel() @State var enterSelected = false var body: some View { VStack { Button(action: { enterSelected = true }) { Text("Enter Price") } .sheet(isPresented: $enterSelected) { Price(decimals: decimals, viewModel: priceVM) } } .padding() } } struct Price: View { @ObservedObject var decimals: Decimals @ObservedObject var viewModel: Price.ViewModel @Environment(\.dismiss) var dismiss var body: some View { VStack(spacing: 20) { Text("Price = \(viewModel.price as NSNumber, formatter: decimals.priceFormatter)") .frame(width: 400, height: 200) .background(.white) TextField("Price:", value: $viewModel.price, formatter: decimals.priceFormatter) .frame(width: 95) .textFieldStyle(.roundedBorder) .padding(3) Button("Close") { dismiss() } .padding(.leading, 11) .buttonStyle(.borderless) } } } extension Price { @MainActor class ViewModel: ObservableObject { @Published var price: Decimal = 0.0 // 替换为Decimal类型 } } class Decimals: ObservableObject { // 预初始化并复用Formatter实例 let priceFormatter: NumberFormatter = { let formatter = NumberFormatter() formatter.numberStyle = .decimal formatter.minimumFractionDigits = 2 formatter.maximumFractionDigits = 11 return formatter }() }
额外说明
- 替换为Decimal后,不仅解决了输入拼接问题,还彻底避免了Double的浮点精度误差(如0.1+0.2≠0.3的经典问题)。
- 复用NumberFormatter实例后,TextField的绑定逻辑不再频繁重置,Xcode 15的状态同步bug就不会触发。
- 若因项目限制必须使用Double,至少要确保Formatter是单例/复用的,同时可尝试给TextField添加
.id(viewModel.price)强制绑定更新,但这只是临时 workaround,推荐用Decimal从根源解决问题。
内容的提问来源于stack exchange,提问作者Steve
相关产品推荐
相关产品推荐

