Swift TextView detect previous line on backspace - ios

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.

Related

Ignore space after predictive text selection in UITextView

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:

How to check if textfield has more than one letter in Swift?

I'm working on a text field in Swift and what I'm about to implement is that disabling the navigation button unless there is 0 character in the text field and adding the delete button on the text field if there is more than 1 letter on the text field, like an iOS default calendar app.
The bar button item "Add" is disabled when there is no letter in the title text field.
Enable the "Add" button and show the delete button when there is 1+ letter in the Title text field.
I took a look at the text field delegate methods and I think I can implement the one I'm trying to by using shouldChangeCharactersIn method (?)
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// check if there is a letter in the text field
// if there is, enable the Add button and show delete icon
// if there is not, disable the add button and hide the delete icon
return true
}
I was wondering if there are other methods to implement this functionality? I think whenever type / delete a letter on the text field, the function above will be called everytime, so I was wondering if there are other ways to implement this stuff easily.
func textFieldDidChangeSelection(_ textField: UITextField) {
validateTextField(text:textField.tex)
}
your method shouldChangeCharactersIn is not working properly sometimes
so can use this method
make a function that take text and disable and enable nav bar and delete button like
func validateTextField(text:String) {
if text.count > 1 {
textField.clearButtonMode = .whileEditing
} else if text.count <= 1 {
textField.clearButtonMode = .never
} else if text.count < 1 {
navigationItem.leftBarButtonItem.tintColor = .lightGray
}
You can use storyboard approach for particular textfield text count.
1.Ctrl + Click on the textfield in interface builder.
Drag from EditingChanged to inside your view controller class in the assistant view.
3.Name your function ("textFieldEditingChanged" for example) and click connect.

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.

Enable Return Key when Pasting into a UISearchController's UISearchBar

I'm running into a similar problem to the one described in a bug report to Apple.
Basically, when pasting text into a UISearchBar (part of UISearchController), the Return key is not enabled on the keyboard. (It gets enabled when typing characters though).
Essentially, enablesReturnKeyAutomatically is ignored, as this property should be true by default.
Steps to Reproduce:
Let UISearchBar of a UISearchController become first responder.
Paste text into UISearchBar
Press Search button on keyboard
Expected Results:
Search button is enabled.
Actual Results:
Search button stays disabled.
Question
Even though this seems to be a bug, is there a workaround for this particular problem ? Especially that some apps like Twitter or Product Hunt got around it somehow.
The following will do it. Note that if pasting into a search bar that already has text, the search button will already be enabled, so a special-case is only needed when the current search text is empty.
func searchBar(searchBar: UISearchBar, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
if text.characters.count > 0 && range.length == 0 && range.location == 0 {
dispatch_async(dispatch_get_main_queue()) {
searchBar.resignFirstResponder()
searchBar.becomeFirstResponder()
}
}
return true
}

Resources