首页 > 解决方案 > 如何在空文本字段中更改 deleteBackward 上的 firstResponder

问题描述

共有三个文本字段。

Q1:如何将光标(更改firstResponder)移动到第一个textField,当在空的第二个中按下向后删除时。

Q2:有没有办法通过 Combine 订阅 deleteBackward() 事件?

Q3:我的代码在 iOS 14.5 上运行良好,但在 15.0 上捕获“AttributeGraph:通过属性检测到循环”。任何想法为什么以及如何解决它?

struct ContentView: View {
    
    @State var text1 = ""
    @State var text2 = ""
    @State var text3 = ""
    
    @State private var isFirstResponder: [Bool] = [true, false, false]
    
    var body: some View {
        HStack {
            CustomTextField(text: $text1, tag: 0, returnValue: .next, isFirstResponder: $isFirstResponder)
                .modifier(TextFieldStyle())
            CustomTextField(text: $text2, tag: 1, returnValue: .next, isFirstResponder: $isFirstResponder)
                .modifier(TextFieldStyle())
            CustomTextField(text: $text2, tag: 2, returnValue: .next, isFirstResponder: $isFirstResponder)
                .modifier(TextFieldStyle())
        }
            .frame(height: 40)
            .padding()
    }
}

struct TextFieldStyle: ViewModifier {
    
    func body(content: Content) -> some View {
        content
            .padding(.all, 10)
            .background(RoundedRectangle(cornerRadius: 5).fill(Color.white))
            .overlay(
                RoundedRectangle(cornerRadius: 5)
                    .stroke(lineWidth: 1)
            )
            .foregroundColor(Color.black.opacity(0.7))
    }
}

private class CustomUITextField: UITextField {
    override public func deleteBackward() {
        print("deleteBackward")
        super.deleteBackward()
        if text == "" {
            print("deleteBackward on empty")
        }
    }
}

struct CustomTextField: UIViewRepresentable {
    
    @Binding var text: String
    let tag: Int
    let returnValue: UIReturnKeyType
    @Binding var isFirstResponder: [Bool]
    
    func makeUIView(context: Context) -> UITextField {
        let textField = CustomUITextField(frame: .zero)
        
        textField.keyboardType = .numberPad
        textField.autocorrectionType = .no
        textField.returnKeyType = returnValue
        textField.tag = tag
        textField.delegate = context.coordinator
        
        return textField
    }
    
    func updateUIView(_ uiView: UITextField, context: Context) {
        if isFirstResponder[tag] {
            uiView.becomeFirstResponder()
        } else {
            uiView.resignFirstResponder()
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(text: $text, self)
    }
    
    class Coordinator: NSObject, UITextFieldDelegate {
        
        @Binding var text: String
        var parent: CustomTextField
        
        init(text: Binding<String>, _ textField: CustomTextField) {
            _text = text
            parent = textField
        }
        
        func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
            let currentText = textField.text ?? ""
            var isDeleting = string.count == 0
            
            if (currentText + string).count == 1 && !isDeleting {
                parent.isFirstResponder = [false, false, false]
                parent.isFirstResponder[min(parent.tag + 1, 2)] = true
            } else if currentText.count <= 1 && isDeleting {
                parent.isFirstResponder = [false, false, false]
                parent.isFirstResponder[max(parent.tag - 1, 0)] = true
            } else if (currentText + string).count > 3 {
                return false
            }
            
            print("textField \(currentText) -> \(string)")
            return true
        }
    }
}

标签: iosswiftcombine

解决方案


推荐阅读