swift - 如何移动到 SwiftUI 中的下一个 TextField?

标签 swift textfield swiftui first-responder

使用 Swift5.1.2, iOS13.2, Xcode-11.2,

在 Stackview 中有几个 TextField,我想在用户在第一个 TextField 中键入 x 数量的字符后立即移动到下一个 TextField。

使用 this link ,我可以识别 TextField 条目何时达到 x 字符数。但是,我不知道如何让 firstResponder 跳转到我的 StackView 中的第二个 TextField。

SwiftUI 有解决方案吗?

最佳答案

我正在使用 UITextFieldUIViewRepresentable 来实现这一点。
定义每个文本字段的 tag 并声明一个 bool 值列表,该列表具有相同数量的可用文本字段以关注返回键 fieldFocus ,这将跟踪基于当前索引/标签的下一个要关注的文本字段。
用法:

import SwiftUI

struct Sample: View {
    @State var firstName: String = ""
    @State var lastName: String = ""
    
    @State var fieldFocus = [false, false]
    
    var body: some View {
        VStack {
            KitTextField (
                label: "First name",
                text: $firstName,
                focusable: $fieldFocus,
                returnKeyType: .next,
                tag: 0
            )
            .padding()
            .frame(height: 48)
            
            KitTextField (
                label: "Last name",
                text: $lastName,
                focusable: $fieldFocus,
                returnKeyType: .done,
                tag: 1
            )
            .padding()
            .frame(height: 48)
        }
    }
}
UITextFieldUIViewRepresentable 中:
import SwiftUI

struct KitTextField: UIViewRepresentable {
    let label: String
    @Binding var text: String
    
    var focusable: Binding<[Bool]>? = nil
    var isSecureTextEntry: Binding<Bool>? = nil
    
    var returnKeyType: UIReturnKeyType = .default
    var autocapitalizationType: UITextAutocapitalizationType = .none
    var keyboardType: UIKeyboardType = .default
    var textContentType: UITextContentType? = nil
    
    var tag: Int? = nil
    var inputAccessoryView: UIToolbar? = nil
    
    var onCommit: (() -> Void)? = nil
    
    func makeUIView(context: Context) -> UITextField {
        let textField = UITextField(frame: .zero)
        textField.delegate = context.coordinator
        textField.placeholder = label
        
        textField.returnKeyType = returnKeyType
        textField.autocapitalizationType = autocapitalizationType
        textField.keyboardType = keyboardType
        textField.isSecureTextEntry = isSecureTextEntry?.wrappedValue ?? false
        textField.textContentType = textContentType
        textField.textAlignment = .left
        
        if let tag = tag {
            textField.tag = tag
        }
        
        textField.inputAccessoryView = inputAccessoryView
        textField.addTarget(context.coordinator, action: #selector(Coordinator.textFieldDidChange(_:)), for: .editingChanged)
        
        textField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
        
        return textField
    }
    
    func updateUIView(_ uiView: UITextField, context: Context) {
        uiView.text = text
        uiView.isSecureTextEntry = isSecureTextEntry?.wrappedValue ?? false
        
        if let focusable = focusable?.wrappedValue {
            var resignResponder = true
            
            for (index, focused) in focusable.enumerated() {
                if uiView.tag == index && focused {
                    uiView.becomeFirstResponder()
                    resignResponder = false
                    break
                }
            }
            
            if resignResponder {
                uiView.resignFirstResponder()
            }
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    final class Coordinator: NSObject, UITextFieldDelegate {
        let control: KitTextField
        
        init(_ control: KitTextField) {
            self.control = control
        }
        
        func textFieldDidBeginEditing(_ textField: UITextField) {
            guard var focusable = control.focusable?.wrappedValue else { return }
            
            for i in 0...(focusable.count - 1) {
                focusable[i] = (textField.tag == i)
            }
            
            control.focusable?.wrappedValue = focusable
        }
        
        func textFieldShouldReturn(_ textField: UITextField) -> Bool {
            guard var focusable = control.focusable?.wrappedValue else {
                textField.resignFirstResponder()
                return true
            }
            
            for i in 0...(focusable.count - 1) {
                focusable[i] = (textField.tag + 1 == i)
            }
            
            control.focusable?.wrappedValue = focusable
            
            if textField.tag == focusable.count - 1 {
                textField.resignFirstResponder()
            }
            
            return true
        }
        
        func textFieldDidEndEditing(_ textField: UITextField) {
            control.onCommit?()
        }
        
        @objc func textFieldDidChange(_ textField: UITextField) {
            control.text = textField.text ?? ""
        }
    }
}
enter image description here

关于swift - 如何移动到 SwiftUI 中的下一个 TextField?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58673159/

相关文章:

java - 如何在不更改 javafx 边框的情况下更改 TextField 的背景颜色?

swiftui - @state 变量更改后 View 不更新

swift - 任务完成后重定向

ios - 在 Swift 中导入扩展文件

ios - 场景大小不适合屏幕 SpriteKit

button - TextField 为空时如何禁用按钮?

带有对象参数的特定 UITextfield 的 Swift 2 addObserver

swift - SwiftUI 中 NavigationView 导航栏的自定义后退按钮

ios - 带有 pageToken 的 Google Place 搜索 API 返回相同的记录

swift - 单元格之间的空间 UICollectionViewLayout