3

On my login page, if I focus to UITextEdit for entering email and password, keyboard appears and login page scroll up. And I add this code for remove keyboard if I touch outside of keyboard.

extension UIViewController {
    func hideKeyboardWhenTappedAround() {
        let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
        tap.cancelsTouchesInView = false
        view.addGestureRecognizer(tap)
    }
    
    @objc func dismissKeyboard() {
        view.endEditing(true)
        UIView.animate(withDuration: 0.3, animations: {
            self.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height)
        })
    }
}

And on my viewDidLoad() function, I added it like this.

override func viewDidLoad() 
{
        super.viewDidLoad()
        //TODO
        self.hideKeyboardWhenTappedAround()
       ...
}

It works well, But If I click login button when keyboard is still opening, dismisskeyboard() function works but btnClick function doesn't work.

@IBAction func LoginBtnClick(_ sender: Any) 
{
        print("loginBtn")
        //...
}

How can I solve this problem?

halfer
  • 19,824
  • 17
  • 99
  • 186
Ryo Y.
  • 43
  • 1
  • 6

4 Answers4

3

I actually solved it a little different way (I had the same problem on my login screen). I just moved the dismissing/animating of the keyboard into an async block with a tiny delay, because it seemed like with the tap recognizer, it was picking up the touch down, but not the touch up inside the button, likely because the screen had already started to animate the dismissal of the keyboard.

@objc func dismissKeyboard() {
    DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
        self.view.endEditing(true)
        UIView.animate(withDuration: 0.3, animations: {
            self.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height)
        })
    }
}
wildcat12
  • 975
  • 6
  • 13
  • 1
    hope you get this message - that is a one in a million tip! great thinking, cheers. sending bounty :) – Fattie Jun 28 '20 at 10:21
2

I have tested it using following code:

import UIKit

extension UIViewController {
    func hideKeyboardWhenTappedAround() {
        let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
        tap.cancelsTouchesInView = false
        view.addGestureRecognizer(tap)
    }

    @objc func dismissKeyboard() {
        view.endEditing(true)
        UIView.animate(withDuration: 0.3, animations: {
            self.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height)
        })
    }
}

class ViewController: UIViewController {

    fileprivate let textField = UITextField(frame: CGRect(x: 0, y: 0, width: 400, height: 50))
    fileprivate let button = UIButton(frame: CGRect(x: 20, y: 200, width: 150, height: 50))

    override func loadView() {
        super.loadView()

        view.addSubview(textField)
        textField.backgroundColor = .lightGray
        view.addSubview(button)
        button.setTitle("Press me", for: .normal)
        button.setTitleColor(UIColor.blue, for: .normal)
        button.addTarget(self, action: #selector(a), for: .touchUpInside)

        hideKeyboardWhenTappedAround()
    }

    @objc func a() {
        print(">>>> pressed")
    }
}

The code in a gets called. Thus I assume that there is something else in your code that causes the button not to work. Perhaps you forgot to link the button to the @IBAction LoginBtnClick(_:) - check if the connection is really there.

Milan Nosáľ
  • 19,169
  • 4
  • 55
  • 90
  • Thanks for your answer. func a() was called when keyboard open? – Ryo Y. Jan 11 '18 at 16:42
  • Yes. Although when I pressed the button, the `dismissKeyboard` was called too, so not only the button action was called, but also the keyboard gets dismissed. You can try my code by simply presenting that `ViewController` somewhere. – Milan Nosáľ Jan 11 '18 at 16:54
  • Did you confirm that `@IBAction` is connected to the button in storyboards? Just as an experiment, add an `@IBOutlet` to the viewController, and then use `addTarget` as I did to link the button to the `LoginBtnClick` method programmatically. Sometimes there are glitches in storyboards, maybe it shows there is connection, but it does not really wire it up. – Milan Nosáľ Jan 11 '18 at 17:07
  • But login button works if there's no keyboard on the screen. I checked your code and it works. Very strange and I am little confused. Maybe there's something wrong on my code. – Ryo Y. Jan 11 '18 at 17:15
  • Do you do anything when the keyboard appears? Show that code too – Milan Nosáľ Jan 11 '18 at 17:16
  • 2
    I added new UIView and add gesture to it. Thanks for your time and your help. Maybe your answer is also correct. As I said, there's some problem on my code. Anyway, I solve the problem. Thank you. – Ryo Y. Jan 11 '18 at 17:24
0

Don't add the gesture to main viewController's view add a full screen subview below UIButton and add the gesture to it

Shehata Gamal
  • 98,760
  • 8
  • 65
  • 87
0

I added a button over the view of view controller (be sure that your textfield is in front of the button). Then, I implemented the UITextFieldDelegate to enable/disable the button. Something like this:

class ViewController: UIViewController, UITextFieldDelegate {
    @IBOutlet private weak var myTextfield: UITextField!
    @IBOutlet private weak var myButton: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.myTextfield.delegate = self
    }
    
    // MARK: - UITextFieldDelegate
    func textFieldDidBeginEditing(_ textField: UITextField) {
        self.myButton.isUserInteractionEnabled = true
    }
    
    func textFieldDidEndEditing(_ textField: UITextField) {
        self.myButton.isUserInteractionEnabled = false
    }
    
    // MARK: - Action functions.
    @IBAction private func onButtonAction() {
        self.myTextfield.resignFirstResponder()
    }
}
Gastón Antonio Montes
  • 2,559
  • 2
  • 12
  • 15