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

SwiftUI TextField点击Next切换时键盘反复隐藏显示问题求助

解决SwiftUI TextField切换焦点时键盘闪烁问题

你的问题出在输入框键盘类型不一致:第一个TextField用默认键盘,第二个用numbersAndPunctuation类型键盘,切换时iOS系统会销毁旧键盘并加载新键盘,导致出现闪烁。

方案1:统一键盘类型+输入过滤(最简单)

保持所有输入框使用相同的默认键盘,通过代码过滤第二个输入框的内容,只允许输入数字和标点:

struct CreditEditorScreen: View {
    
    enum FocusedField: Hashable {
        case creditor, amount, purpose
    }
    
    @State private var creditor             : String = ""
    @State private var amount               : String = ""
    @State private var purpose              : String = ""
    @State private var date                 : Date = Date()
    @FocusState private var focusedField    : FocusedField?
        
    var body: some View {
        NavigationStack {
            VStack {
                TextField("creditor", text: $creditor)
                    .focused($focusedField, equals: .creditor)
                    .submitLabel(.next)
                    .onSubmit { focusedField = .amount }
                
                // 修改这里:用默认键盘+输入过滤
                TextField("amount", text: $amount)
                    .focused($focusedField, equals: .amount)
                    .submitLabel(.next)
                    .onSubmit { focusedField = .purpose }
                    // 过滤输入,只保留数字和小数点,且限制单个小数点
                    .onChange(of: amount) { newValue in
                        let allowedCharacters = CharacterSet(charactersIn: "0123456789.")
                        let filtered = newValue.unicodeScalars.filter { allowedCharacters.contains($0) }.map(String.init).joined()
                        amount = filtered.components(separatedBy: ".").count > 2 ? String(filtered.dropLast()) : filtered
                    }
                
                TextField("purpose", text: $purpose)
                    .focused($focusedField, equals: .purpose)
                    .submitLabel(.done)
                
                DatePicker("date", selection: $date, displayedComponents: [.date])
                
                Spacer()
                
                Button("Save") { }
            }
            .onAppear { focusedField = .creditor }
            .padding()
        }
    }
}

方案2:用UIKit包装TextField(无缝切换不同键盘)

如果必须使用不同类型的键盘,可以通过UIKit的UITextField实现无缝切换,避免系统销毁重建键盘:

// 自定义UIKit包装的TextField
struct CustomUITextField: UIViewRepresentable {
    let placeholder: String
    @Binding var text: String
    let keyboardType: UIKeyboardType
    let returnKeyType: UIReturnKeyType
    var onReturn: () -> Void
    
    func makeUIView(context: Context) -> UITextField {
        let textField = UITextField()
        textField.placeholder = placeholder
        textField.keyboardType = keyboardType
        textField.returnKeyType = returnKeyType
        textField.delegate = context.coordinator
        textField.autocapitalizationType = .none
        return textField
    }
    
    func updateUIView(_ uiView: UITextField, context: Context) {
        if uiView.text != text {
            uiView.text = text
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, UITextFieldDelegate {
        let parent: CustomUITextField
        
        init(_ parent: CustomUITextField) {
            self.parent = parent
        }
        
        func textFieldShouldReturn(_ textField: UITextField) -> Bool {
            parent.onReturn()
            return true
        }
    }
}

// 视图中使用自定义TextField
struct CreditEditorScreen: View {
    @State private var creditor: String = ""
    @State private var amount: String = ""
    @State private var purpose: String = ""
    @State private var date: Date = Date()
    
    // 用tag标记输入框,方便切换焦点
    @State private var activeTag: Int = 0
    
    var body: some View {
        NavigationStack {
            VStack {
                CustomUITextField(
                    placeholder: "creditor",
                    text: $creditor,
                    keyboardType: .default,
                    returnKeyType: .next,
                    onReturn: { activeTag = 1 }
                )
                .tag(0)
                
                CustomUITextField(
                    placeholder: "amount",
                    text: $amount,
                    keyboardType: .numbersAndPunctuation,
                    returnKeyType: .next,
                    onReturn: { activeTag = 2 }
                )
                .tag(1)
                
                CustomUITextField(
                    placeholder: "purpose",
                    text: $purpose,
                    keyboardType: .default,
                    returnKeyType: .done,
                    onReturn: { activeTag = -1 }
                )
                .tag(2)
                
                DatePicker("date", selection: $date, displayedComponents: [.date])
                
                Spacer()
                
                Button("Save") { }
            }
            .onAppear {
                // 自动聚焦第一个输入框
                DispatchQueue.main.async {
                    UIApplication.shared.sendAction(#selector(UITextField.becomeFirstResponder), to: nil, from: nil, for: nil)
                }
            }
            .padding()
            // 监听activeTag变化,切换焦点
            .onChange(of: activeTag) { tag in
                if tag >= 0 {
                    DispatchQueue.main.async {
                        let keyWindow = UIApplication.shared.connectedScenes
                            .filter({$0.activationState == .foregroundActive})
                            .map({$0 as? UIWindowScene})
                            .compactMap({$0})
                            .first?.windows
                            .filter({$0.isKeyWindow}).first
                        
                        keyWindow?.viewWithTag(tag)?.becomeFirstResponder()
                    }
                } else {
                    UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
                }
            }
        }
    }
}

说明

  • 方案1适合大多数场景,实现简单,避免了键盘类型切换带来的闪烁。
  • 方案2适合必须使用不同键盘类型的场景,通过UIKit原生API直接控制焦点,实现无缝切换。

内容的提问来源于stack exchange,提问作者Abdurakhmon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 13:35:01