I am trying to handle UIResponder
for an existing subclass of UITextView
in SwiftUI. I have been able to use the Coordinator pattern to handle UITextViewDelegate
but I'm having trouble with UIResponder
.
In the past (with UIKit), what I would have done is use NotificationCenter
to add an observer for UIResponder.keyboardWillShowNotification
in a subclass of UIViewController
.
In SwiftUI, I'm not sure where to put it. I've done a simple thing which is to re-use the Coordinator class in makeUIView
, as in:
let nc = NotificationCenter.default
nc.addObserver(context.coordinator, selector: #selector(Coordinator.keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: editorTextView)
But the keyboardWillShow
method never gets called. Am I doing this wrong?
I would recommend using a Combine Publisher so you don't need to mess with selectors, but this should work either way. The observer/selector pattern is commented out in this example, but if you uncomment it both the observer and the publisher should respond when the keyboard appears.
import Combine
import SwiftUI
import UIKit
struct MyTextView: UIViewRepresentable {
// Pass in the binding to the string from the SwiftUI view
var text: Binding<String>
init(text: Binding<String>) {
self.text = text
}
func makeUIView(context: Context) -> UITextField {
let tf = UITextField()
tf.delegate = context.coordinator
tf.text = context.coordinator.text.wrappedValue // Access the wrapped value in the binding
return tf
}
func updateUIView(_ uiView: UITextField, context: Context) {
//
}
func makeCoordinator() -> MyTextViewDelegate {
let delegate = MyTextViewDelegate(text: text)
return delegate
}
class MyTextViewDelegate: NSObject, UITextFieldDelegate {
// let nc = NotificationCenter.default
var text: Binding<String>
// You can use a Combine Publisher rather than dealing with selectors
var subscriber: AnyCancellable?
init(text: Binding<String>) {
self.text = text
super.init()
subscriber = NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)
.sink() { [weak self] note in
print(self?.text.wrappedValue ?? "nil")
print("Publisher called -> " + note.description)
}
// nc.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
}
// @objc func keyboardWillShow(notification: Notification) {
// print("Selector called -> " + notification.description)
// }
// Value should update in SwiftUI when return key is pressed to show that the data flows
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.text.wrappedValue = textField.text ?? ""
print(textField.text!)
return true
}
}
}