Strange behavior while calling UITextViewDelegate ShouldTextChangeIn - ios

I have a UITableViewCell where i have a comments UITextView to write comments. While writing down the comments i check out for #hashtags and #mentions to check their availability on the server side of the app and display them in a tableView.
The issue is that once the delegate method is called i cannot reset it to check in other if statements. For instance the " " space character "If statement is never called if i started my text by a #hashtag" so i cannot know if it is the end of a #hashtag or a #mention. And also #hashtag and " " if statements are never called if i started my text with #mention
The ranging and everything is working perfectly fine but when i type space it is not reseting to start a new #hashtag or #mention
Do i need to reset the range or what do i have to do?
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if textView == postCommentTextView {
if textView.text == "#" {
recordingHash = true
recordingMention = false
startParseHash = range.location
} else if textView.text == "#" {
recordingHash = false
recordingMention = true
startParseMention = range.location
} else if textView.text == " " {
recordingHash = false
recordingMention = false
createdHashesArray.append(createdHashString)
createdHashString = ""
}
if recordingHash != nil {
if recordingHash == true {
var value = String()
print("COUNT: \(textView.text.count)")
if startParseHash <= (textView.text.count - startParseHash) {
let createdRange = NSMakeRange(startParseHash, textView.text.count - startParseHash)
value = textView.text(in: createdRange.toTextRange(textView)!)! //as! NSString
createdHashString = "\(value)"
// print("hash:\(createdHashString)")
}
print("hash:\(createdHashString)")
if textView.text.count == 0 {
recordingHash = false
recordingMention = false
// createdHashesArray.append(createdHashString)
createdHashString = ""
}
}
}
return true
}

When you check for the character you should be using text variable not textView.text. text gives you the current character (or whole text that was pasted in) textview.text gives you the whole text

Related

how to check textfield is not empty and button enable

I want to check textfield is empty
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let text = (textField.text! as NSString).replacingCharacters(in: range, with: string)
if !text.isEmpty {
self.completeRegisterBtn.isEnabled = true
} else {
self.completeRegisterBtn.isEnabled = false
}
return true
}
This is my code but it is only check input new text.
I have already input textfields by data.
How can i check?
Furthermore not only want to check textfield but also a button(from 2 button) is selected check
Please help me. Thank you
I have attached the code how to check that textFiled is empty or not
let txtField = UITextField()
txtField.text = "testing"
if txtField.text != "" {
btn_Pause.isEnabled = true
} else {
btn_Pause.isEnabled = false
}
-> Using isEmpty function
if txtField.text?.isEmpty == true {
btn_Pause.isEnabled = false
} else {
btn_Pause.isEnabled = true
}
-> Using character count
if txtField.text?.count ?? 0 > 0 {
btn_Pause.isEnabled = true
} else {
btn_Pause.isEnabled = false
}
Answer as per your comment:
if textField.text != "" && btn.isSelected {
}
Just make function like this and call where you want to use this
func dataValidation(text: String) {
btn.isEnable = (text.isEmpty ?? false) ? false : true
}
how to use:
dataValidation(text: "test")
Try this
if let text = textfield.text, !text.isEmpty {
completeRegisterBtn.isEnabled = true
} else {
completeRegisterBtn.isEnabled = false
}

Only allow one decimal for each number in UITextView (Swift 4)

I have a UITextView into which I type a series of space-delimited numbers (for example: "12.3 30 22.7 19.23 15 8.5 11").
The UITextView may contain multiple decimal points, but I want to ensure that no individual number contains more than one decimal place.
All the solutions I have found only restrict the entire UITextView to a single decimal, not each number.
How can I do this?
You can try something like this in the UITextView delegate func shouldEndEditing..
func textViewShouldEndEditing(_ textView: UITextView) -> Bool {
guard let numbers = textView.text else {
return true
}
var hasMultipleDecimalPlaces: Bool = false
let numbersAsList = numbers.split(separator: " ")
for number in numbersAsList {
let splitAtDecimal = number.split(separator: ".")
if splitAtDecimal.count > 2 {
if splitAtDecimal[1].count > 2 {
// This is what you want to prevent so break early and return false
hasMultipleDecimalPlaces = true
break
}
}
}
return !hasMultipleDecimalPlaces
}
This will prevent the user from finishing editing the textview. It can always be used in a function like shouldReplaceCharactersIn
I think you should use this delegate textField(_:shouldChangeCharactersIn:replacementString:) and by your self check every single number and check if there was already point or not. And return false or true.
Try a regular expression in the text view delegate method:
let regex = try! NSRegularExpression(pattern: "^\\s*((-?)(\\d+\\.?|\\d*\\.\\d+))?(\\s+(-?)(\\d+\\.?|\\d*\\.\\d+))*\\s*$")
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
// Compute the value of the text view after the text is inserted.
let finalText = (textView.text as NSString).replacingCharacters(in: range, with: text) as NSString
// Check against a regular expression.
return regex.firstMatch(in: finalText as String, range: NSRange(location: 0, length: finalText.length)) != nil
}
You are going to have to experiment with the behavior that you want:
https://regex101.com
Final result:
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
// only allow valid characters
let validCharacters = " -0123456789.,|\t\n"
guard CharacterSet(charactersIn: validCharacters).isSuperset(of: CharacterSet(charactersIn: text)) else {
return false
}
let oldText = txtViewDataInput.text!
let r = Range(range, in: oldText)
let text = oldText.replacingCharacters(in: r!, with: text)
debugPrint("text = \(text)")
let whiteSpace = " ,|\t\n"
let separators = NSCharacterSet(charactersIn: whiteSpace)
let tempArray = text.components(separatedBy: separators as CharacterSet)
// do not allow more than one decimal point in a number
for number in tempArray {
let splitAtDecimal = number.filter { $0 == "." }.count
if splitAtDecimal > 1 {
return false
}
}
return true
}

How to get the Character that is before the cursor in a UITextView?

Let's say I have the following UITextView object:
var textView = UITextView()
textView.text = "Hello World!"
Now let's say I don't want to allow the user to delete the "W" character while editing it. How could I know which character is before the cursor (or selected by it)?
I'm looking for something that would work like this:
if textView.characterBeforeCursor() != "W" {
textView.deleteBackward()
}
or... (when the user selects the "W" character):
if textView.selectedTextContains("W") == false {
textView.deleteBackward()
}
What approach should I use to accomplish this?
Here's an idea, not fully tested, but seems to work... Just grab the character about to be acted upon and block backspace if its the target... Also with regard to selection of text, if the selection contains the target at all, we block new text.
import UIKit
class ViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
self.textView.delegate = self
// Do any additional setup after loading the view, typically from a nib.
}
func characterBeforeCursor() -> String? {
// get the cursor position
if let cursorRange = textView.selectedTextRange {
// get the position one character before the cursor start position
if let newPosition = textView.position(from: cursorRange.start, offset: -1) {
let range = textView.textRange(from: newPosition, to: cursorRange.start)
return textView.text(in: range!)
}
}
return nil
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if (characterBeforeCursor() == "W") {
let char = text.cString(using: String.Encoding.utf8)!
let isBackSpace = strcmp(char, "\\b")
if (isBackSpace == -92) {
return false
}
return true
}
else {
if let range = textView.selectedTextRange {
let selectedText = textView.text(in: range)
if (selectedText!.contains("W")) {
return false
}
}
return true
}
}
}
This should do it:
let forbiddenLetter = "W"
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
guard let txt = textView.text, let txtRange = Range(range, in: txt) else {
return false
}
let subString: Substring = txt[txtRange]
return !subString.contains(forbiddenLetter)
}
In the code above let txt = textView.text is just for simplicity, we could keep force-unwrapping textView.text! since the .text property is designed never returns nil for a non-nil UITextView.
By let txtRange = Range(range, in: txt) we get a variable of type Range<String.Index> instead of the vanilla NSRange that range is. By doing so we can get the Substring of txt that the textView is about to change.
Finally, the result of checking whether or not the subString contains the forbiddenLetter, is returned.
This snippet would prevent deleting W by using:
Backspace key ⌫
Deleting selection
Pasting over selection
Autocorrect (from the popup)

Keep functionality of return key but disable "\n" IOS

Update: Return Key Issue
I am using the return key in the TextView to send a message.
However, when I press the return key(which i changed to "Send") the "\n" feature is still enabled. How can i disable the newline feature without ruining my functionality of "Send".
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if (text == "\n") {
if(message.text != ""){
message.text = nil
doSomething()
}
}
return true
}
Tried to toggle Auto-enable return key still doesn't take care of my problem.
I need to access doSomething() the same way but without "\n" effecting my textView after I clear the message.
The problem is that you are returning true. That means "go ahead and enter the return character that the user typed". But this is exactly what you say you do not want to do. Return false instead (for the case where text == "\n").
Call cursorPosition() function after
message.text = nil
message.text = ""
and add the following function
func cursorPosition() {
let startPosition = textView.beginningOfDocument
textView.selectedTextRange = textView.textRange(from: startPosition, to: startPosition)
}

Search data from list when user type '#' Symbol in UITextView

Is there any way to show dropdown list when user type "#" with specific search keyword in UITextView.
For Example:
Type "#ios" and all the string which contains word "ios" filter from list and show in dropdown list.
I think you are looking for something like this.This delegate will be called whenever a user presses # key
Try this :-
func textView(_ textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
if (textView.text.appending(text) == "#") {
//trigger '#' operation
if arrayObject.contains(value) {
//Populate array and display tableview.
}
}
else if (textView.text.appending(text) == "/") {
//trigger / operation
}
//Same conditions go on
}
I achieved this by constantly checking user type text.
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
// if user type any latter from #,space and new line then clear the search string
if text == "#" || text == " " || text == "\n" {
self.searchString = ""
}
// append the type string in text view text
let resultString = textView.text.stringByAppendingString(text)
// break the line with # symbol and find the occurence
let numberOfOccurrences = resultString.componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "#")).count - 1
// store all words for checking
let stringArray = resultString.componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "#")) ?? [String]()
// checking if string array > 1 that means string contains # symbol
self.searchString = stringArray.count > 1 ? stringArray[numberOfOccurrences].containsString(" ") ? "" : stringArray[numberOfOccurrences] : ""
if self.searchString.characters.count > 0 {
self.getTopSearchFromLive(self.searchString)
} else {
self.tableView.hidden = true
}
return true
}

Resources