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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 21:14:50