Search code examples
iosswiftuitextviewcarriage-returnremoving-whitespace

Swift iOS -How to test for only Return "\n" key character being entered inside a TextView


I have a textView that tests for white space using the delegates

fileprivate let whitespace = CharacterSet.whitespaces

func textViewDidChange(_ textView: UITextView) {
        if textView.text!.trimmingCharacters(in: whitespace) == ""{
            sendButton.isEnabled = false
        }else{
            sendButton.isEnabled = true
        }
    }

func textViewDidEndEditing(_ textView: UITextView) {
        if textView.text!.trimmingCharacters(in: whitespace) == ""{
            sendButton.isEnabled = false
            sendButton.layer.backgroundColor = UIColor.lightGray.cgColor
        }else{
            sendButton.isEnabled = true
            sendButton.layer.backgroundColor = UIColor.red.cgColor
        }
    }

If the user enters all spaces the the sendButton never becomes enabled. But if the user types in 1 or more return key characters "\n" it's still recognized as a character and the sendButton does become enabled.

In the textView delegates shouldChangeTextInRange, textViewDidChange, and textViewDidEndEditing I tried adding textView.text! == "\n":

fileprivate let whitespace = CharacterSet.whitespaces

func textViewDidChange(_ textView: UITextView) {
        if textView.text!.trimmingCharacters(in: whitespace) == "" || textView.text! == "\n"{
            sendButton.isEnabled = false
        }else{
            sendButton.isEnabled = true
        }    }

func textViewDidEndEditing(_ textView: UITextView) {
        if textView.text!.trimmingCharacters(in: whitespace) == "" || textView.text! == "\n"{
            sendButton.isEnabled = false
        }else{
            sendButton.isEnabled = true
        }
    }

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        if textView.text!.trimmingCharacters(in: whitespace) == "" || textView.text! == "\n"{
            sendButton.isEnabled = false
        }else{
            sendButton.isEnabled = true
        }
        return true
    }

The button still becomes enabled.

How do I get the same response as testing for whitespace only?


Solution

  • Think about what you're doing here:

        if textView.text!.trimmingCharacters(in: whitespace) == "" || textView.text! == "\n" {
    

    Split that into pieces:

        // Part 1 - get the text from textView, and remove whitespace characters
        textView.text!.trimmingCharacters(in: whitespace)
    
        // Part 2 - get the text from textView
        textView.text!
    
        IF "Part 1" == ""
        OR
        IF "Part 2" == "\n"
    

    So, what happens if textView contains 4 spaces and a carriage return?

    Part 1 is not true, because stripping the spaces leaves you with "\n"

    Part 2 is not true, because it is "(four spaces)\n"

    That should explain why your current code is not getting what you expect.

    However, as @rmaddy suggested, this should do it:

    func textViewDidChange(_ textView: UITextView) {
        let str = textView.text.trimmingCharacters(in: .whitespacesAndNewlines)
        if str == "" {
            sendButton.isEnabled = false
        } else {
            sendButton.isEnabled = true
        }
    }