swiftswiftuiuitextfielduitextfielddelegate

Respond to specific key press in UITextField (UIViewRepresentable)


I would like to respond to keyboard presses in a UITextField wrapped as UIViewRepresentable. This is the code for the wrapper view:

import SwiftUI
import UIKit

struct ContentView: View {
    @State var text: String = "Hello."
    
    var body: some View {
        WrappedUITextField(text: $text)
    }
}

struct WrappedUITextField: UIViewRepresentable {
    @Binding var text: String
    
    func makeCoordinator() -> Coordinator {
        Coordinator($text)
    }
    
    func makeUIView(context: Context) -> UITextField {
        let textField = UITextField()
        textField.delegate = context.coordinator
        textField.text = text
        textField.autocapitalizationType = .none
        return textField
    }
    
    func updateUIView(_ uiView: UITextField, context: Context) {
        if text != context.coordinator.currentText { uiView.text = text }
    }
}

class Coordinator: NSObject, UITextFieldDelegate {
    
    var text: Binding<String>
    var currentText: String
    
    init(_ text: Binding<String>) {
        self.text = text
        currentText = text.wrappedValue
    }
    
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        
        guard let oldText = textField.text,
              let textRange = Range(range, in: oldText) else {
            return false
        }
        
        self.currentText = oldText.replacingCharacters(in: textRange, with: string)
        print("oldText = \"\(oldText)\", new text = \"\(currentText)\"")
        
        self.text.wrappedValue = currentText
        return true
    }
    
    func textFieldDidChangeSelection(_ textField: UITextField) {
        print("textFieldDidChangeSelection")
    }
}

Specifically, I want to trigger some action when pressing left/right arrow key when the cursor is at the left/rightmost edge, e.g., "|abc", where "|" is the cursor position, and the user presses left arrow.

None of the UITextFieldDelegate methods will work here because pressing left/right arrow key when the cursor is already on the left/rightmost edge (respectively) neither changes the text contents (e.g., textFieldDidBeginEditing() won't be called) nor cursor position (e.g., textFieldDidChangeSelection() won't be called).

How do I respond to left/right arrow keys in this case? Something like a pressesBegan() method would be nice here, but I couldn't figure out how to integrate that with UIViewRepresentable (e.g., adding override func pressesBegan(...) to Coordinator doesn't work).

Thanks


Solution

  • Just create your class inherited UITextField and override handler there

    func makeUIView(context: Context) -> UITextField {
        let textField = MyTextField()                   // << here !!
        textField.delegate = context.coordinator
        textField.text = text
        textField.autocapitalizationType = .none
        return textField
    }
    
    class MyTextField: UITextField {
       override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
           // handle here requried events
    
           // call super if not handled above
           super.pressesBegan(presses, with: event)
       }
    }