0

I have a ViewController with a UITextView and a "Send" bar button item in the navigation bar which submits the text in the textView. Since the UITextView does not support placeholder text like the UITextField, I am handling it on my own with the following code which resides in the UITextViewDelegate method, shouldChangeTextInRange.

Note: The following code I wrote so far enables the Send button for whitespace/newline characters too. But this is what I need help with:

How can I disable the Send button when the textView contains only whitespace or newline characters but enable it otherwise while also setting/clearing the placeholder text appropriately?

func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {

    // Combine the postTextView text and the replacement text to
    // create the updated text string
    let currentText : NSString = textView.text
    let updatedText = currentText.stringByReplacingCharactersInRange(range, withString:text)

    // If the updated textView text will be empty, disable the send button,
    // set the placeholder text and color, and set the cursor to the beginning of the text view
    if updatedText.isEmpty {
        sendBarButton.enabled = false
        textView.text = "Write something..."
        textView.textColor = UIColor.lightGrayColor()
        textView.selectedTextRange = textView.textRangeFromPosition(textView.beginningOfDocument, toPosition: textView.beginningOfDocument)
        return false
    }

    // If the textView's placeholder is showing (i.e.: the textColor is light gray for placeholder text) 
    // and the length of the replacement string is greater than 0, 
    // clear the text view and set its color to black to prepare for the user to enter text
    else if (textView.textColor == UIColor.lightGrayColor() && !(text.isEmpty)) {
        sendBarButton.enabled = true
        textView.text = nil
        textView.textColor = UIColor.blackColor()
    }

    return true
}

UPDATE: I understand the following code may be used to trim/recognize whitespace and newline characters, but not sure how to apply it here in this case: stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()).isEmpty

Thanks for your help.

DiligentDev
  • 71
  • 13

2 Answers2

2

I will do this way. I added a placeholder UILabel on UITextView which I will show or hide depending on the number of characters typed (0 or non zero).

Here is a Sample attached.

This is in Swift 3.x syntax. It will work on Xcode 8.x only.

I just made use of some more UITextView delegate methods as shown in my extension below

import UIKit

class ViewController: UIViewController {

    var placeHolderLabel:UILabel!
    @IBOutlet weak var myTextView: UITextView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        placeHolderLabel = UILabel(frame: CGRect(x:5, y:5, width:240, height:22))
        placeHolderLabel.text = "Send Placeholder"

        //Set the font same as your textView font
        placeHolderLabel.font = UIFont.systemFont(ofSize: 15.0)

        //set the placeholder color
        placeHolderLabel.textColor = UIColor.lightGray

        myTextView.addSubview(placeHolderLabel)

        //Initially disable the send button
        sendButton.isEnabled = false
    }
}

// MARK: - TextView Delegates
extension ViewController:UITextViewDelegate {

    // Will handle the case for white spaces and will keep the send button disabled
    func textViewDidChange(_ textView: UITextView) {
        if(textView.text.characters.count != 0) {
            if textView.text.characters.count > 1 {
            return
            }
            textView.text = textView.text.trimmingCharacters(in: CharacterSet.whitespaces)
            if textView.text.characters.count == 0 {
                placeHolderLabel.isHidden = false
                print("Disable Button")
                sendButton.isEnabled = false
            } else {
                placeHolderLabel.isHidden = true
                print("Enable Button")
                sendButton.isEnabled = true
            }
        }
        else {
            placeHolderLabel.isHidden = false
            print("Disable Button")
            sendButton.isEnabled = false
        }
    }

    // You can modify this, as I just made my keyboard to return whenever return key is pressed.
    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        if(text == "\n") {
            textView.resignFirstResponder()
            return false
        }
        return true
    }

    func textViewDidBeginEditing(_ textView: UITextView) {
        if(textView.text.characters.count != 0) {
            placeHolderLabel.isHidden = true
        }
        else {
            placeHolderLabel.isHidden = false
        }
    }

    func textViewDidEndEditing(_ textView: UITextView) {
        if(textView.text.characters.count != 0) {
            placeHolderLabel.isHidden = true
        }
        else {
            placeHolderLabel.isHidden = false
        }
    }
}
Rajan Maheshwari
  • 14,465
  • 6
  • 64
  • 98
  • Thanks Rajan but I believe this still enables the bar button for whitespace characters. – DiligentDev Nov 21 '16 at 04:09
  • For that we have to write some logic , just trim your characters for white spaces . I ll update the code in a moment – Rajan Maheshwari Nov 21 '16 at 04:31
  • Thanks. Yes, I can use the `stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())` method to trim the whitespace and newline characters but I need the send bar button to be disabled for these characters until any other character is entered. – DiligentDev Nov 21 '16 at 05:09
  • @DiligentDev Updated the code and sample link with your white space requirement – Rajan Maheshwari Nov 21 '16 at 05:25
  • Thanks. Although your solution didn't work for me, I was able to use part of your answer to come up with my own simple solution. See my posted answer. Thanks for your help! – DiligentDev Nov 21 '16 at 18:56
0

I was able to use part of @Rajan Maheshwari's answer to create a simple solution to my own problem (I think it can even be simplified further:

var placeholderLabel: UILabel!

func viewDidLoad() {
        placeholderLabel = UILabel(frame: CGRect(x: 5, y: 5, width: 240, height: 18))
        placeholderLabel.text = "Placeholder text..."
        placeholderLabel.textColor = UIColor.lightGrayColor()
        placeholderLabel.sizeToFit()
        postTextView.addSubview(placeholderLabel)
}

func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {

        let currentText: NSString = textView.text
        let updatedText = currentText.stringByReplacingCharactersInRange(range, withString:text)
        let trimmedText = updatedText.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())

        if !updatedText.isEmpty {
            // Contains any text: hide placeholder
            placeholderLabel.hidden = true
            if trimmedText.isEmpty {
                // Only whitespace and newline characters: disable button
                sendBarButton.enabled = false
            } else {
                // No whitespace- and newline-only characters: enable button
                sendBarButton.enabled = true
            }
        } else {
            // No text at all: show placeholder, disable button
            placeholderLabel.hidden = false
            sendBarButton.enabled = false
        }
        return true
    }

Credit: Thanks to @Rajan Maheshwari's help!

DiligentDev
  • 71
  • 13