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

升级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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 09:27:49