I have created a custom text field and I'd like to take advantage of Combine. In order to be notified whenever text changes in my text field, I currently use a custom modifier. It works well, but I want this code could inside my CustomTextField struct.
My CustomTextField struct conforms to UIViewRepresentable. Inside this struct, there is a NSObject class called Coordinator and it conforms to UITextFieldDelegate.
I'm already using other UITextField delegate methods, but couldn't find one that does exactly what I already do with my custom modifier. Some methods are close, but don't quite behave the way I want them to. Anyway, I feel it would be best to put this new custom textFieldDidChange method in the Coordinator class.
Here is my custom modifier
private let textFieldDidChange = NotificationCenter.default
.publisher(for: UITextField.textDidChangeNotification)
.map { $0.object as! UITextField}
struct CustomModifer: ViewModifier {
func body(content: Content) -> some View {
content
.tag(1)
.onReceive(textFieldDidChange) { data in
//do something
}
}
}
My CustomTextField is used in a SwiftUI view, with my custom modifier attached to it. I’m able to do things when ever there are changes to the text field. The modifier is also using Combine. It works great, but I don't want this functionality to be in the form of a modifier. I want to use it in my Coordinator class, along with my UITextFieldDelegate methods.
This is my CustomTextField
struct CustomTextField: UIViewRepresentable {
var isFirstResponder: Bool = false
@EnvironmentObject var authenticationViewModel: AuthenticationViewModel
func makeCoordinator() -> Coordinator {
return Coordinator(authenticationViewModel: self._authenticationViewModel)
}
class Coordinator: NSObject, UITextFieldDelegate {
var didBecomeFirstResponder = false
@EnvironmentObject var authenticationViewModel: AuthenticationViewModel
init(authenticationViewModel: EnvironmentObject<AuthenticationViewModel>)
{
self._authenticationViewModel = authenticationViewModel
}
// Limit the amount of characters that can be typed in the field
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let currentText = textField.text ?? ""
guard let stringRange = Range(range, in: currentText) else { return false }
let updatedText = currentText.replacingCharacters(in: stringRange, with: string)
return updatedText.count <= 14
}
/* I want to put my textFieldDidChange method right here */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
func textFieldDidEndEditing(_ textField: UITextField) {
textField.resignFirstResponder()
textField.endEditing(true)
}
}
func makeUIView(context: Context) -> UITextField {
let textField = UITextField()
textField.delegate = context.coordinator
textField.placeholder = context.coordinator.authenticationViewModel.placeholder
textField.font = .systemFont(ofSize: 33, weight: .bold)
textField.keyboardType = .numberPad
return textField
}
func updateUIView(_ uiView: UITextField, context: Context) {
let textField = uiView
textField.text = self.authenticationViewModel.text
}
}
struct CustomTextField_Previews: PreviewProvider {
static var previews: some View {
CustomTextField()
.previewLayout(.fixed(width: 270, height: 55))
.previewDisplayName("Custom Textfield")
.previewDevice(.none)
}
}
I've been watching videos about Combine and I'd like to start utilising it in a new app I'm building. I really think it's the right thing to use in this situation, but still not quite sure how to pull this off. I'd really appreciate an example.
To summarise:
I want to add a function called textFieldDidChange to my Coordinator class, and it should be triggered every time there is a change to my text field. It must utilise Combine.
Thanks in advance