Search code examples
iosswiftuitableviewuitextviewuitapgesturerecognizer

how to Dismiss Keyboard if we touch other area outside UITextView in Swift


I have UITextView inside static table view, I want to remove the keyboard if the user touch other area outside the textView

usually I can this lines of code to remove the keyboard, but i don't know why, after adding placeholder in the textview, it doesn't work. I suspect because it is inside table view static

extension CheckinDetailTVC {

    // to remove keyboard if we touch other area
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.view.endEditing(false)
    }

}

I also have tried another solution in stackoverflow but no one work in me, like add gesture recognizer

override func viewDidLoad() {
    super.viewDidLoad()
    let tapGestureReconizer = UITapGestureRecognizer(target: self, action: "tap:")
    view.addGestureRecognizer(tapGestureReconizer)
}

func tap(sender: UITapGestureRecognizer) {
    view.endEditing(true)
}

or add function in TextViewDelegate, but it also doesn't work

func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
    if text == "\n" {
        textView.resignFirstResponder()
    }
    return true
}

here is the screenshot of my UI

enter image description here

here is the simplified code I Use in the view controller

class CheckinDetailTVC: UITableViewController {


    @IBOutlet weak var noteTextView: UITextView!


    override func viewDidLoad() {
        super.viewDidLoad()

        // to put placeholder for UITextView
        noteTextView.delegate = self
        noteTextView.text = "Write your note in here ..."
        noteTextView.textColor = UIColor.lightGray
        noteTextView.becomeFirstResponder()
        noteTextView.selectedTextRange = noteTextView.textRange(from: noteTextView.beginningOfDocument, to: noteTextView.beginningOfDocument)



    }



}




extension CheckinDetailTVC : UITextViewDelegate {

// add placeholder for textView

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

        let currentText:String = textView.text
        let updatedText = (currentText as NSString).replacingCharacters(in: range, with: text)

        if updatedText.isEmpty {

            textView.text = "Write your note in here ..."
            textView.textColor = UIColor.lightGray

            textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)

            return false
        }

        else if textView.textColor == UIColor.lightGray && !text.isEmpty {
            textView.text = nil
            textView.textColor = UIColor.black
        }

        return true
    }

    func textViewDidChangeSelection(_ textView: UITextView) {
        if self.view.window != nil {
            if textView.textColor == UIColor.lightGray {
                textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)
            }
        }
    }

}




extension CheckinDetailTVC {

    // usually i can use this code to remove keyboard if we touch other area
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.view.endEditing(false)
    }

}

what went wrong in here?


Solution

  • option 1

    Without blocking the UI add the cancelsTouchesInView for your gesture `

    let tapGestureReconizer = UITapGestureRecognizer(target: self, action: "tap:")
    tapGestureReconizer.cancelsTouchesInView = false
    view.addGestureRecognizer(tapGestureReconizer)
    

    option 2

    else handle the resign in multiple ways

    func tap(sender: UITapGestureRecognizer) {
     view.endEditing(true)
     // or use 
     noteTextView.resignFirstResponder()
     // or use
     view.super().endEditing(true)
     // or use
     view.keyboardDismissMode = .onDrag
    }