SwiftUI中onChange内if语句导致转换功能无响应问题
问题:SwiftUI货币转换应用聚焦判断导致转换失效
开发SwiftUI货币转换应用,界面包含两个TextField,需求是当其中一个TextField处于聚焦状态并输入内容时,自动将数值转换后同步到另一个TextField中。
当前代码在onChange回调内添加if leftTyping判断后,转换完全无响应;去掉该判断时转换功能正常。
视图代码
@State var leftAmount = "" @State var rightAmount = "" @State var showInfoScreen = false @State var showSelectCurrency = false @FocusState var leftTyping @FocusState var rightTyping @State var leftCurrency: Currency = .silverPiece @State var rightCurrency: Currency = .goldPiece var body: some View { ZStack{ Image("background") .resizable() .ignoresSafeArea() VStack{ Image("prancingpony") .resizable() .scaledToFit() .frame(height: 200) Text("Curreny Exchange") .foregroundColor(.white) .font(.largeTitle) .kerning(2) HStack{ VStack{ HStack{ Image(leftCurrency.image) .resizable() .scaledToFit() .frame(height: 33) Text(leftCurrency.name) .font(.headline) .foregroundColor(.white) } .onTapGesture { showSelectCurrency = true }.sheet(isPresented: $showSelectCurrency){ SelectCurrency(showSelectCurrency: $showSelectCurrency, topCurrency: $leftCurrency, bottomCurrency: $rightCurrency) } TextField("Amount", text: $leftAmount) .textFieldStyle(.roundedBorder) .focused($leftTyping) .onChange(of: leftAmount) { newValue in if leftTyping { rightAmount = leftCurrency.convert(leftAmount, to: rightCurrency) } } } Image(systemName: "equal") .foregroundColor(.white) .font(.largeTitle) VStack{ HStack{ Text(rightCurrency.name) .font(.headline) .foregroundColor(.white) Image(rightCurrency.image) .resizable() .scaledToFit() .frame(height: 33) } .onTapGesture { showSelectCurrency = true }.sheet(isPresented: $showSelectCurrency){ SelectCurrency(showSelectCurrency: $showSelectCurrency, topCurrency: $leftCurrency, bottomCurrency: $rightCurrency) } TextField("Amount", text: $rightAmount) .textFieldStyle(.roundedBorder) .multilineTextAlignment(.trailing) .focused($rightTyping) .onChange(of: rightAmount) { newValue in leftAmount = rightCurrency.convert(rightAmount, to: leftCurrency) } } } .padding() .background(.black.opacity(0.5)) .clipShape(Capsule()) Spacer() HStack { Spacer() Button { showInfoScreen = true } label: { Image(systemName: "info.circle.fill") .foregroundColor(.white) .font(.largeTitle) } } .padding() .sheet(isPresented: $showInfoScreen) { InfoView(showInfoScreen: $showInfoScreen) } } } }
转换函数代码
func convert(_ amountString: String, to currency: Currency) -> String { guard let doubleAmount = Double(amountString) else { return "" // 用户输入非数字时返回空字符串 } let convertedAmount = (doubleAmount / self.rawValue) * currency.rawValue return String(format: "%.2f", convertedAmount) }
问题原因
onChange(of: leftAmount)的触发时机早于FocusState变量leftTyping的状态更新:用户输入第一个字符时,leftAmount的回调先执行,但此时leftTyping还未同步为true,导致判断条件不成立,转换逻辑无法执行。
解决方案
新增一个变量记录当前活跃的输入框,通过监听FocusState的变化来更新这个变量,替代直接在onChange中判断FocusState值。
步骤1:定义枚举标记输入框
enum TextFieldSide { case left, right }
步骤2:添加状态变量并监听聚焦变化
在视图中新增:
@State var activeTextField: TextFieldSide? = nil
给左侧TextField添加聚焦监听:
TextField("Amount", text: $leftAmount) .textFieldStyle(.roundedBorder) .focused($leftTyping) .onChange(of: leftTyping) { isFocused in if isFocused { activeTextField = .left } else if activeTextField == .left { activeTextField = nil } } .onChange(of: leftAmount) { newValue in if activeTextField == .left { rightAmount = leftCurrency.convert(newValue, to: rightCurrency) } }
给右侧TextField添加聚焦监听:
TextField("Amount", text: $rightAmount) .textFieldStyle(.roundedBorder) .multilineTextAlignment(.trailing) .focused($rightTyping) .onChange(of: rightTyping) { isFocused in if isFocused { activeTextField = .right } else if activeTextField == .right { activeTextField = nil } } .onChange(of: rightAmount) { newValue in if activeTextField == .right { leftAmount = rightCurrency.convert(newValue, to: leftCurrency) } }
逻辑说明
- 通过
onChange(of: leftTyping)明确监听聚焦状态变化,确保activeTextField的更新时机正确。 - 在
leftAmount的onChange中判断activeTextField == .left,保证只有当前活跃的输入框触发转换,避免循环更新。
内容的提问来源于stack exchange,提问作者AlTheBeast
相关产品推荐
相关产品推荐

