Emojis breaking my code in UItextView Swift 4 - ios

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if range.length + range.location > commentView.text!.count{
return false
}
let newLength = (commentView.text?.count)! + text.count - range.length
let i = charCount - newLength
if i < 30 {
charCountLabel.textColor = UIColor.red
} else {
charCountLabel.textColor = UIColor(r: 79, g: 79, b: 79)
}
charCountLabel.text = "\(i)"
return newLength < charCount
}
The above code is a character counter for a UITextView, yet when I enter a single emoji into the UITextView the editing stops, why is that?? and how would I integrate a fix
CommentView : UItextView
charCount : Int
charCountLabel : UIlabel
sc of the debugger
upon stepping though the thread I get this when I try to send another character :
further in thread
EDIT
upon going through the debugger I have found that the second emoji or any char is causing the "I" var to be some super long number same with the "newLength" ... any one got any ideas?

I tried running your code in a test project and hit several issues. I assumed you initialized 'charCount' with 0 to begin, but this results in 'i' being -1 when you type the first character, which then returns false for every character after that.
If you're simply trying to implement a text length counter there are easier ways to do it. The two methods below populate the proper character count in the counter label when adding/deleting regular text and emoji characters.
First method I'd try is implementing the textView delegate func textViewDidChange(_ textView: UITextView). This will update the label count after every character you type. You could also set your text color here if you want.
func textViewDidChange(_ textView: UITextView) {
// only want to update character count label for commentView
guard textView == commentView, let string = textView.text else {
return
}
// update counter label text with the current text count of the textview
charCountLabel.text = "\(string.count)"
}
The second method is to use the textView delegate you were using. Here's some code I got working in a test project. There are probably better ways than this but this will get you going.
#IBOutlet weak var commentView: UITextView!
#IBOutlet weak var charCountLabel: UILabel!
let minCount = 30
let maxCount = 120
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
// only want to update character count label for commentView
guard textView == commentView, let string = textView.text else {
return true
}
// get current text + new character being entered
var newStr = string+text
// check if this is a backspace
let isDeleting = (range.length > 0) && text.isEmpty
if isDeleting == true {
// trim last character
// you may want to drop based on the range.length, however you'll need
// to determine if the character is an emoji and adjust the number of characters
// as range.length returns a length > 1 for emojis
newStr = String(newStr.dropLast())
}
// set text color based on whether we're over the min count
charCountLabel.textColor = newStr.count < minCount ? .red : .blue
// set the character count in the counter label
charCountLabel.text = "\(newStr.count)"
// if we're less than the max count allowed, return true
return newStr.count < maxCount
}

Related

How do I get the line height of UITextField

I have a few questions that I can't find the answers online:
1.How do I get the line height of UITextField?
2.How do I get UITextField to only enter a maximum of 3 lines of text?
3.How do I get UITextField to only enter a maximum of 25 characters per line?
and thanks for answer!
As the comment says, you should use UITextView, not UITextField.
UITextField handles only a single line.
Use UITextView to process multiple lines of text.
I'm going to give you an opinion on number 3.
First, there is something that limits the length of the textview.
Auto-layout to a length of 25 characters to fit the font size you want.
The second is that when the text reaches 25 characters, it adds a line-breaking symbol.
extension mainVC: UITextViewDelegate {
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if let char = text.cString(using: String.Encoding.utf8) {
let isBackSpace = strcmp(char, "\\b")
if isBackSpace != -92 {
if textView.text.count == 25 {
textView.text += "\n"
}
}
}
return true
}
}
If what you entered is not backspace, it will be executed. If it's 25 characters, you can add the line symbol '\n' to the text in textView and change it.

How to add text bubble styles when I separate the email address by comma as show in the screen shot attached

Hi I'm new to iOS app development. I am implemented an UITextview to add multiple address separated by comma I need to apply a style bubble when user separates the email by pressing comma or space or done button (in the keyboard.) reference screen shot from android
I am executing an email array same as below
func executeEmailArray(){
self.emailString = self.emailTF.text ?? ""
self.emailArray = emailString.components(separatedBy: ",")
print("emailArray \(self.emailArray)")
}
I formatted the uiTextView as below by using an extension
extension NewShoppingListVC: UITextViewDelegate {
func textViewDidChangeSelection(_ textView: UITextView) {
// Moves cursor to start when tapped on textView with placeholder
if emailTF.text == placeholder {
emailTF.selectedRange = start
}
}
func textViewDidChange(_ textView: UITextView) {
// Manages state of text when changed
if emailTF.text.isEmpty {
emailTF.text = placeholder
emailTF.textColor = .lightGray
} else if emailTF.text != placeholder {
emailTF.textColor = .black
}
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
// Called when you're trying to enter a character (to replace the placeholder)
if emailTF.text == placeholder {
emailTF.text = ""
}
return true
}
}
Please add some codes to explain me how to apply the textbubbles as in the image

How to check if text is selected and cross button is pressed in iOS?

I have a textview. There is label above textview which indicates that textview text is mandatory or not. So if textview is empty label will be visible and as soon as user write any character label is hidden. Now I want to detect if user has selected some text and pressed cross button of keyboard. I used below code it works thought but it is conflicting with other scenario.
So if there any alternate for below code?
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
print(text.length + (textView.text?.length)!)
if textView.tag == 22 || textView.tag == 23 || textView.tag == 24 || textView.tag == 25 {
let indexPath = IndexPath(row: textView.tag, section: 0)
let cell = tableView.cellForRow(at: indexPath) as! WhyTableViewCell
if text == "" && range.length > 1 {
cell.lblValidation.isHidden = false
}
}
return true
}
textDidChange....
Isn’t the test simply
Label.isHidden = !text.isEmpty()
The label is based solely on the amount of text in the text field ? If the text field is empty you want to display the mandatory label. It doesn’t matter if the field was empty to start, a single backspace of one character, the ‘x’ click or a selection and delete, you only display the label when the value of the text field is empty.

UITextField - uneditable prefixed characters both in beginning & end

I have a UITextField, and I want to give it uneditable characters. For example I have "Hello...", and I want the user to write in between o and . such as "Hello userTypedStuff..."
I used this approach, however with that, user can longpress the beginning and for example go to the position inside Hello. Thus, user can mess with the prefixed characters.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let protectedRange = NSMakeRange(0, 5)
let intersection = NSIntersectionRange(protectedRange, range)
if intersection.length > 0 {
return false
}
if range.location == 12 {
return true
}
if range.location + range.length > 12 {
return false
}
return true
}
How can I setup something like 'uneditable first 5 characters and last 3 characters'?
Edit: The UITextField's text-aligned center and is forming above a UILabel. However, if I give textField the width of label, textField won't get larger on width. Thus, I gave its width view's width. Is there a way I can create UILabel on both ends having Hello and ... separately, and have textField (with text-aligned center) in the middle which automatically stretches width?
Edit 2:
If I have my textview with two static UILabels on both sides, that can be a workaround. However, this time the UITextField won't push UILabels to sides as user starts to type in. The TextField center textAligned. If I give it a constant width while creating it (programatically with CGRectMake) , it won't stretch its width as user types in. Thus, I gave it the width of view. Is there a way to auto-stretch the TextField's width as user types in that pushes the UILabels on the sides?
"Hello" [textfield] "..."
uilabel uilabel
class SpecialTextField: UITextField {
// MARK: - UITextField Observing
override internal func willMoveToSuperview(newSuperview: UIView!) {
if newSuperview != nil {
text = "Hello..."
addTarget(self, action: #selector(SpecialTextField.didChangeText), forControlEvents: .EditingChanged)
}
}
override internal func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
return false
}
private var newVal: String = "Hello..."
func didChangeText() {
var userDidEditDefaultValue = true
if (text?.characters.count > 7) { // Count of "Hello..."
if text?.rangeOfString("Hello") != nil {
if text?.rangeOfString("...") != nil {
userDidEditDefaultValue = false
}
}
}
if userDidEditDefaultValue {
text = newVal
}
newVal = text ?? "Hello..."
}
}
This will not allow user to change default value of "Hello..." OR "Hello userEntry..."
You can use delegate method to insert text in the middle.
Hope it helped.

If i wanted to change the color of a specific word entered in textview, How would i do that?

From what i've read i would need to use rangeOfString to search a textview entry but am unsure about how to go about that. Is it possible to change the color of a text entered in textview in real time, for example if someone wrote "blue," could i change the word to blue the moment they typed it. If so how would i go about that? I'm very new to coding and even newer to swift.
You will have to use attributed text for your text view and then use the textView(textView: UITextView, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool method, which will be triggered whenever the text in the text view's text changes. Apply your own logic in there as to what range the colored text will fall into and how that will happen...
Make sure your controller conforms to the UITextViewDelegate protocol and make the textView's delegate your controller.
Demonstration:
class ViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
self.textView.delegate = self // important! Otherwise the textView will not know where to call the delegate functions!
}
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
// first make sure that the text field is the one we want to actually change
if textView == self.textView{
let nsString = textView.text as NSString // we explicitly cast the Swift string to NSString so that we can use rangeOfString for example
let stringLength = textView.text.characters.count
// Arbitrarily check if the string in the text field is not empty
// Apply your own logic for when to update the string
if stringLength > 0{
let text = NSMutableAttributedString(string: textView.text)
// Currently the range is assumed to be the whole text (the range covers the whole string)
// You'll have to apply your own logic here
text.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: NSMakeRange(0, stringLength))
textView.attributedText = text
}
}
return true
}
}
For example, instead of using the above to color the whole text
text.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: NSMakeRange(0, stringLength))
Color the first occurence of "hello" in red:
text.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: nsString.rangeOfString("hello"))
Note that I explicitly cast the textView's text to NSString so that we can use the range functions such as (rangeOfString())
Changes were made to swift in which count no longer seems to work with String. I made a slight change to the answer given by the_critic (https://stackoverflow.com/users/1066899/the-critic). Thank you to all who helped
class ViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
self.textView.delegate = self // important! Otherwise the textView will not know where to call the delegate functions!
}
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
// first make sure that the text field is the one we want to actually change
if textView == self.textView{
let nsString = textView.text as NSString // we explicitly cast the Swift string to NSString so that we can use rangeOfString for example
let stringLength = textView.text.characters.count
// Arbitrarily check if the string in the text field is not
empty
// Apply your own logic for when to update the string
if stringLength > 0{
let text = NSMutableAttributedString(string: textView.text)
// Currently the range is assumed to be the whole text (the range covers the whole string)
// You'll have to apply your own logic here
text.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: NSMakeRange(0, stringLength))
textView.attributedText = text
}
}
return true
}
}

Resources