UITextView receiving text form dictation, but text is nil in textView.text property - ios

I am trying to get the text from a UITextView whose entry method was dictation. In the textViewDidChange delegate method, I have tried printing the following info:
print(textView.text)
print(myTextView.text)
print(textView.hasText)
print(textView.attributedText)
print(textView.textStorage)
all of these come back nil, except the hasText comes back as false.
The text is visible right there in the textView, but it is not being registered. I should say that when I go edit another form in the field, and then try to get the value from this text field, then the the text entered IS visible in the textView's textView.text property. But it's like it takes a few moments of editing other fields to fully "register" with the textView object.
Any idea what could be happening here?

Try this after a while
let when = DispatchTime.now() + 0.5
DispatchQueue.main.asyncAfter(deadline: when) {
print(textView.text)
print(textView.hasText)
print(textView.hasText)
print(textView.attributedText)
print(textView.textStorage)
}
Update: It`s because you are trying to print the text before setting it. It takes a little time to set your text into textView.

Related

UITextView delegate textViewDidChangeSelection is called twice

I'm trying to do some stuff when user taps Enter button, so I have implemented the following delegates for UITextView:
// Delegate is called when text is gonna change
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if (text == "\n") {
// Some behavior when user taps Enter
return true
}
// Some other code
return true
}
// Delegate is called when selection is changed
func textViewDidChangeSelection(_ textView: UITextView) {
// Some code
}
The problem is, textViewDidChangeSelection is called twice when user taps Enter.
In second call, textView.selectedRange is changed to the last character of text view. This produces a problem when user taps Enter after any line in the middle of text, because caret position is changed to end of text.
I've attached a ā€¸reproducible example here so you can check, I'm not sure if the problem is in the delegates, or in the way I attached the delegate to the view.
To re-produce the problem, do the following scenario:
Write some lines, for example:
Line 1.
Bla bla bla
line 3
Go to the end of line 1.
Tap on Enter.
The new line is there in the right position, but the caret position is changed to the end of text view.
Notes:
I've checked the following post in stackoverflow, but it doesn't
fix my problem.
The file you need to review in my ā€¸reproducible example is EditorTextView.swift
only.
OK finally, I found a solution for this weird behavior of delegation in the UITextView wrapper for SwiftUI.
So the entire issue was with setting the text again every time updateUIView was called. That is not necessary and it caused a weird feedback loop with textViewDidChange delegate method (that is where I set the selected range when the last character was \n, which is the case for a new line).
I don't know exactly why does that happen, I just think it's a bug with how UIViewRepresentables works under the hood.
So, to solve the issue, I had to move the following line from updateUIView:
uiView.text = document
And add it to makeUIView:
textView.text = document
This way, it will be bound permanently.

Setting custom text if TextField is empty

I have a textField called titleTextField in which the user can enter the title of their choice before saving a note. What I want to do is automatically fill this field with the text "Untitled" if the user has left this field blank.
I've tried several solutions, but they either do nothing or set the title always to Untitled even if the user has entered text.
This is the closest I've gotten, but it's still not working right:
if(![self.titleTextField.text isEqual:#""]){
self.titleTextField.text = #"Untitled";
}
My thought was that this would check whether the titleTextField is empty, and if so, it would populate the field with the text "Untitled." I've tried applying this in the viewDidLoad, viewWillAppear, and viewWillDisappear -- I really don't care whether the "Untitled" text is populated when the screen loads or after the user is finished with the page; I just want the end result to be an empty title field saving with the text "Untitled".
Is the coding wrong, or am I just applying it in the wrong place?
Solution as per the way you are looking for is :
if([self.titleTextField.text isEqualToString:#""]){
self.titleTextField.text = #"Untitled";
}
You can also use below :
if([self.titleTextField.text length] == 0){
self.titleTextField.text = #"Untitled";
}

textViewDidChangeSelection method thinks textView.text is empty

For various reasons, I need to access the text in a textview whenever a selection is changed, so I have implemented the delegate method textViewDidChangeSelection() to do so. For some reason though, when I try to access textView.text from within this method, sometimes it comes back as empty even when it's not. Take this bit of code for example.
func textViewDidChangeSelection(_ textView: UITextView) {
print("Called textViewDidChangeSelection")
if textView.text.isEmpty {
print("textview is empty")
} else {
print("textview is not empty")
}
}
Using this example in my code, I click on a textView that is NOT empty and sometimes it comes back with "textview is empty." It seems like it tends to happen most often immediately after re-running the simulator and clicking on any textView, but I've also seen it happen when just clicking on a textView for the first time (after having clicked on some other textView) or when I segue back to my UIView containing the textViews from some other view.
Does anyone know why this happens?
textViewDidChangeSelection(_ textView: UITextView)
is called when the text selection changes, not necessarily just when another textView is selected. In fact in the olden days the selectedRange property of the textView used to return zero (indicating an insertion) but now (according to Apple) the length of the selection range may be non-zero. When you click away from a textView your delegate method may be firing for the initial textView (the one you're leaving) which would give you the result you're seeing, if it's empty.
Try giving your textView tags and using:
func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
print("Selected \(textView.tag)")
return true
}
To see if it solves your problem. Of course tags are just a quick and dirty check and you'd probably use a more flexible textView identification in your app.

Append text to a UITextField with custom keyboard made out of buttons

I have a static numeric-keyboard made out of a bunch of buttons, I also have three UITextFields, textField1, textField2 and textField3 where I'm inputting the text using the static keyboard.
Here is the code I'm using to detect which textField is currently in focus and to input the content of the buttons. It kind of works but I don't like the fact that I have three IF statements and I'm not sure how to prevent the keyboard from appearing when a textField is tapped.
What would be the best way to implement this functionality?
#IBAction func appendKey(sender: AnyObject) {
let digit = sender.currentTitle!
if(textField1.isFirstResponder()){
textField1.text = textField1.text! + digit!
}else if(textField2.isFirstResponder()){
textField2.text = textField2.text! + digit!
}else if(textField3.isFirstResponder()){
textField3.text = textField3.text! + digit!
}
}
Thanks
If the standard keyboard is displaying then your custom keyboard isn't setup properly. Your custom keyboard should be the inputView of each UITextField. If you do that, the standard keyboard won't appear and yours will instead.
Your custom keyboard should be a separate class that handles all of it's own buttons. It appears you have everything in one view controller - all of the text fields, all of the buttons, and all of the button handling code. This is a bad approach. Create your custom keyboard class view. Put all of the code to handle and display the buttons in that custom view class. Create a single instance of this view in your view controller and assign the custom keyboard view instance to the inputView property of each text field.
In the custom keyboard class, listen for the UITextFieldTextDidBeginEditingNotification notification. This is how you keep track of the current text field. Your custom keyboard class should not have any specific reference to any text field other than track the current one. It should also ensure that the text field's inputView is itself.
In each button handler of the custom keyboard class, get the text you wish to append and then call the text field's insertText: method with the string. That's it. This will ensure the text is inserted and/or replaced based on the current selecting in the text field.

enablesReturnKeyAutomatically is not working with numeric keypad

I am implementing a message composer as like iMessages.
enablesReturnKeyAutomatically of UITextView is not working.
I have set it as textView.enablesReturnKeyAutomatically = YES;
but when I tap on numeric keypad and start typing something and sent using my send button. the textView got empty but the send button (return button of UITextView) is still enabled.
enablesReturnKeyAutomatically seems to only affect the uikeyboard, which in this case is a numeric keypad. The default numeric keypad does not have a return key, so there is nothing to enable/disable.
It sounds like you have a separate send button in the view. A workaround is inside - textView:shouldChangeTextInRange:replacementText: calculate what the new string will be with the replacementText and test if this new String is equal to #"". If so, then sendButton.enabled = NO, else sendButton.enabled = YES.
Remember to set the UITextViewDelegate and to trim the new string of white space.

Resources