Ignore space after predictive text selection in UITextView - ios

I found something similar to my problem checkout here but it does not work for me: link
Thid UITextView delegate method calls twice If we tap on predictive text
1st call insert text 2nd call insert space after if
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool
I found one hack:
if text.count > 1 { return false }
But this logic failed when user tap on single character predictive text
I am doing some customizations in this method e.g. mentions support so after doing some work I update attributedText of TextView on mainThread so before setting attributedText to the textView this method get call for space in predictive text selection case and at that time previous text is not setup yet so it will insert just space.
Two things comes in my mind, somehow handle the call of this method that it should not call twice until text is set to textView
Second somehow ignore the space after predictive text.
if text == " " { return false }
We also can not do this because after that user will not be able to insert spaces.
Predictive texts:

Related

Swift TextView detect previous line on backspace

Problem:
I need to detect when backspace is tapped and goes back to the previous line.
How can we detect a backspace that brings us back to a previous line on a textview, NOT a new line but the previous line.
For example: We can detect a new line by the given code
if (text == "\n"){
print("new line")
}
We can detect a backspace.
if (range.length == 1 && text.isEmpty){
print("backspace pressed on keyboard")
}
Pseudo code: How can we detect a backspace that brings us back to a previous line on a textview, NOT a new line but the previous line.
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if (range.length == 1 && text.isEmpty){
print("backspace pressed on keyboard")
if (backspace brought textview cursor back to its previous line){
}
}
Inputs on Keyboard:
return pressed -> bring us to new line
backspace pressed -> returns us to prev line
Given your overly simplistic examples you can check to see if the current text in the text view at the changed range is equal to a newline character.
if (textView.text as NSString).substring(with: range) == "\n" {
Keep in mind that text in a text view can wrap without ever entering a newline character. That may or may not be important for your use case. And this also means that the text can "unwrap" without deleting a newline character.
You seem to be assuming that the cursor is at the end of the text. Not sure how you want to handle the user deleting somewhere in the middle. Or what about a user selecting a bunch of text and deleting?
Also note that your check for a backspace isn't really a check for a backspace. That condition can also be met if the user selects one character and then selects the Cut menu.

iOS: when user select QuickType keyboard handle selected UITextField

In my project when user select specific UITextField (that UITextField supposed to get user telephone number), the QuickType Keyboard show user telephone number. I want when user select his/her telephone number I can change that (remove "+" in telephone number) and show the result in that UITextField. how can I do that?
UPDATE:
I tried shouldChangeCharactersIn (UITextFieldDelegate function) to handle that, but replacementString return space (" ") and if I just return true (doing nothing inside that function) to that nothing will show inside UITextField.
You were on the right track with shouldChangeCharactersIn, but in case with QuickType keyboard it gets called two times instead of one.
First call is made to clean the current string, even when it's empty: range is (0, 0) in this case. Not much sence, but if you type something, select whole text, and paste phone number from quick help, this first change will have range of the full string to clear it.
And to modify input string, you need to update text field text and return false, because you don't need system to update text anymore.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.starts(with: "+") {
let modifierString = String(string.dropFirst())
.trimmingCharacters(in: .whitespacesAndNewlines)
textField.text = ((textField.text ?? "") as NSString)
.replacingCharacters(in: range, with: modifierString)
return false
} else {
return true
}
}
Note that it'll also ignore press of "+" made by user, if you don't want that you probably need to add more logic to that if, like
if string.count > 1 && string.starts(with: "+") {
This will allow user to press "+" but remove it from any pasted content.
If your objective is to remove any extra characters and keep only numbers, you could in the first place restrict the user to inputting only numbers by selecting the "Number Pad" keyboard type in the attributes inspector of the text field in the storyboard.
Or if you'd like to do it with code:
textField.keyboardType = .numberPad
Sorry if this is not what you are looking for, I just thought maybe you overcomplicated the problem and resorted to filtering the text to numbers, when there is an Apple-provided type of keyboard exactly for collecting phone numbers that you could use instead.

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.

Detecting paste UITextView in textViewDidChange

I am creating a UITextView for use as a rudimentary IDE. When the user pastes text (code) I am modifying it so that it fits inline (correctly indented) with the other text. However, I am also coloring the text (attributedText) using the textViewDidChange method. As a result when I paste text, it is first inserted then adjusted inline. The process looks a bit weird especially for a large chunck of text. I was wondering if there is a way for me to catch that the text is being pasted in textViewDidChange to avoid running attribute changes until the text is modified. Note that coloring attributes in the of the textView shouldChangeTextIn method is not possible as I am implementing some custom behavior through the interaction of the two methods and adding attributes needs to be done at the textViewDidChange stage.
EXAMPLE SETUP
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if (text == UIPasteboard.general.string) {
// MODIFY PASTABLE TEXT HERE
// INSERT MODIFIED TEXT
textView.replace(textRange, withText: newText)
// PREVENT OLD TEXT FROM BEING INSERTED
return false
}
}
func textViewDidChange(_ textView: UITextView) {
// MANAGE ATTRIBUTES
}
WEIRD BEHAVIOR

Narrowing Down UITextField on BackSpace while typing

Using Storyboard, I setup a UITextField and two static UILabels. I setup the constraints so the static labels spread out as user types in the TextField.
[Label1] [TextField] [Label2]
[Label1] [TextFieldIsGettingFilled] [Label2]
Everything is fine until now. The TextField is getting wider as user types in. However, if the user uses backspace (deletes character), TextField doesn't get narrower. So it becomes like:
[Label1] [TextField ] [Label2]
What is the way of detecting backspace and narrow TextFields' width accordingly while user is still typing?
EDIT
I made an example and this works (increases and decreases according to text):
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
textField.invalidateIntrinsicContentSize()
return true
}
Source: Resize a UITextField while typing (by using Autolayout)

Resources