KeyboardWillHide called while KeyboardWillShow is still animating - ios

Currently I have a view animating with the keyboard when the keyboard shows or hides. I have added a gesture recognizer so that when the user taps off the keyboard it disappears.
The issue I have run into is where if the user taps away to lower the keyboard while the keyboard is appearing, the keyboard disappears and my view is not lowered. I have actually noticed that the view moves even higher for whatever reason.
Here are my keyboard listener methods:
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
if let tabBarController = tabBarController {
responseNode.frame.origin.y -= keyboardSize.height-tabBarController.tabBar.frame.height
tableNode.view.contentInset.bottom += keyboardSize.height-tabBarController.tabBar.frame.height
}
}
}
func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
if let tabBarController = tabBarController {
responseNode.frame.origin.y += keyboardSize.height-tabBarController.tabBar.frame.height
tableNode.view.contentInset.bottom -= keyboardSize.height-tabBarController.tabBar.frame.height
}
}
}
and here is how I hide the keyboard:
extension UIViewController {
func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
view.addGestureRecognizer(tap)
}
func dismissKeyboard() {
view.endEditing(true)
}
}
Any help would be greatly appreciated!

try this
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.view.endEditing(true)
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
text.resignFirstResponder()
return true
}

Related

Offset on UIWindow causes the tab bar to show twice when swiping back

I am able to move the system keyboard above the tab bar by moving its window up when keyboardWillShowNotification is sent. However when the view controller is embedded in a navigation controller and I'm slowly swiping back, the tab bar appears twice. Also the offset resets to normal if the swipe is cancelled (obviously since kyeboardWillShow event is not triggered). Observing keyboardWillChangeFrame also doesn't apply.
Here is some sample code to show exactly what I'm doing:
import UIKit
import Foundation
class ViewController: UIViewController { }
class SecondViewController: UIViewController {
let textField = UITextField(frame: CGRect(x: 150, y: 150, width: 150, height: 150))
override func viewDidLoad() {
super.viewDidLoad()
textField.keyboardType = .decimalPad
view.addSubview(textField)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
textField.becomeFirstResponder()
}
}
extension SecondViewController {
private var keyboardOffset: CGFloat {
return -tabBarHeight
}
private var tabBarHeight: CGFloat {
return tabBarController?.tabBar.frame.height ?? 0
}
private var keyboardWindowPredicate: (UIWindow) -> Bool {
return { $0.windowLevel > UIWindow.Level.normal }
}
private var keyboardWindow: UIWindow? {
return UIApplication.shared.windows.last(where: keyboardWindowPredicate)
}
#objc private func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue, let keyboardWindow = keyboardWindow {
keyboardWindow.frame.origin.y = keyboardOffset
}
}
#objc private func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue, let keyboardWindow = keyboardWindow {
keyboardWindow.frame.origin.y = 0
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
textField.resignFirstResponder()
}
}
extension Sequence {
func last(where predicate: (Element) throws -> Bool) rethrows -> Element? {
return try reversed().first(where: predicate)
}
}
Can anyone explain why does this happen and what can I do to prevent this? I know that moving the keyboard is not something usual in iOS, but it was not my decision and I'm trying to work with it.
Maybe the reason is the following:
The docs to touchesBegan(_:with:) say:
UIKit calls this method when a new touch is detected in a view or
window. Many UIKit classes override this method and use it to handle
the corresponding touch events. The default implementation of this
method forwards the message up the responder chain. When creating your
own subclasses, call super to forward any events that you do not
handle yourself. For example, [super touchesBegan:touches
withEvent:event]; If you override this method without calling super (a
common use pattern), you must also override the other methods for
handling touch events, even if your implementations do nothing.
So the 1st thing I would do is to override, as requested by the docs,
the other methods for handling touch requests, even if your
implementations do nothing

Moving UITextFields when keyboard shows

I am trying to move UITextFields when the Keyboard shows. Now I've seen videos and read articles on how to do it. I haven't seen one that uses the textfield itself, rather they use the bottom constraint of the textfield. Here is a video of what my code does, below is my code.
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var nameTF: UITextField!
#IBOutlet weak var emailTF: UITextField!
var selectedTextField: UITextField?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
createKeyboardNotification()
}
func createKeyboardNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(respondToKeyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(respondToKeyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
#objc func respondToKeyboardWillShow(notification: Notification) {
adjustHeightForTextFields(isKeyboardHidden: false, notification: notification, textField: selectedTextField)
}
#objc func respondToKeyboardWillHide(notification: Notification) {
adjustHeightForTextFields(isKeyboardHidden: true, notification: notification, textField: selectedTextField)
}
func adjustHeightForTextFields(isKeyboardHidden: Bool, notification: Notification, textField: UITextField?) {
guard let userInfo = notification.userInfo else { return }
let keyboardFrameRect = userInfo[UIKeyboardFrameEndUserInfoKey] as! CGRect
if let textField = textField {
let textFieldYPosition = textField.frame.origin.y
if view.frame.maxY - textFieldYPosition > keyboardFrameRect.height {
UIView.animate(withDuration: 0.25) {
textField.frame.origin.y = (self.view.frame.maxY - textField.frame.size.height - keyboardFrameRect.height - 8)
}
}
else {
UIView.animate(withDuration: 0.25) {
let difference = textFieldYPosition - keyboardFrameRect.height
textField.frame.origin.y = difference + 16 + self.view.safeAreaInsets.bottom - 8
}
}
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
func textFieldDidBeginEditing(_ textField: UITextField) {
switch textField {
case nameTF:
print("NAME")
selectedTextField = nameTF
break
case emailTF:
print("EMAIL")
selectedTextField = emailTF
break
default:
break
}
}
}
If you have seen the video, I've come across some strange things. First when you tap on the textfield it works like its suppose to, but when you start typing the textfield just disappears. I didn't come across this when I was using the textfield bottom constraint. Now the second part is when the keyboard is already shown the textfield doesn't animate correctly, until you click it twice.
Now I am not using a scrollview, but would like to push the whole content or would I need to use a scrollview. If you take a look at this video, you can better understand what I mean by wanting to push the content.
Would really appreciate any help provided, Thanks. :)
You need to move the view up only when the textField on the bottom becomes active.
//Create a global variable to use as our keyboardHeight
var keyboardHeight: CGFloat = 0.0
override func viewDidLoad() {
super.viewDidLoad()
//Set the delegate only for the emailTF
emailTF.delegate = self
//Set up an observer. This will help us calculate keyboard height dynamically, depending on the iPhone the app runs on.
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
}
#objc private func keyboardWillShow(notification: NSNotification) {
if let keyboardRectValue = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
keyboardHeight = keyboardRectValue.height
}
}
Then move the view up and down when the textField becomes active/inactive.
func textFieldDidBeginEditing(_ textField: UITextField) {
print("MOVE VIEW UP")
if self.view.frame.origin.y == 0{
self.view.frame.origin.y -= keyboardHeight
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
print("MOVE VIEW DOWN")
if self.view.frame.origin.y != 0{
self.view.frame.origin.y += keyboardHeight
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
print("MOVE VIEW DOWN")
if self.view.frame.origin.y != 0{
self.view.frame.origin.y += keyboardHeight
}
return true
}
This should definitely work. Good luck!
I agree that it's not clear always. We do this in our app and we used a scroll view. We embed the entire page in the scroll view. Then we move the bottom of the scroll view up. Scrollviews are easy to implement.
#objc func keyboardWillShow(notification: NSNotification) {
// Only deal with this if the window is active and visible.
if !self.isViewLoaded || self.view.window == nil {
return
}
if let userInfo = notification.userInfo {
let keyboardFrame = userInfo[UIKeyboardFrameEndUserInfoKey] as! CGRect
scrollView.frame = CGRect(x: view.frame.origin.x,y: view.frame.origin.y,width: view.frame.width, height: view.frame.height - keyboardFrame.height - 64)
}
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard)))
}
And when the keyboard disappears:
#objc func keyboardWillHide(notification: NSNotification) {
scrollView.frame = scrollViewOriginalFrame
}

Dismiss keyboard in iOS

I looked at and tried multiple solutions for Swift 3, Xcode 8 but couldn't get any to work. I've tried:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
and also setting a text field input as first responder:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
pressureInput.resignFirstResponder()
}
I don't know if something from Xcode 8 to Xcode 9 that cause these methods to not work, or if I messed elsewhere. I have 9 text fields and they've all set delegate to self. Their tags are incremented to move on to the next text field on pressing return. Don't think that would affect it. Sorry, new at this! The code runs fine with either of those attempted functions, but they keyboard stays. I would just like to dismiss keyboard when touched outside of any text field.
first of all write this extension in any swift file
extension UIViewController {
func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
view.addGestureRecognizer(tap)
}
func dismissKeyboard() {
view.endEditing(true)
}
}
Than in viewDidLoad of that View only call in any view controller there are textFields.
self.hideKeyboardWhenTappedAround()
Swift 4, 5. I always use hide keyboard when tapped around and return button.
override func viewDidLoad() {
super.viewDidLoad()
hideKeyboardWhenTappedAround()
emailTextField.delegate = self // your UITextfield
passwordTextField.delegate = self // your UITextfield
}
// Hide Keyboard
extension EmailAutorization: UITextFieldDelegate {
// Return button tapped
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
// Around tapped
func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(EmailAutorization.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
}
#objc func dismissKeyboard() {
view.endEditing(true)
}
}
Here is Solution of Dismiss Keyboard and Move View Up on Keyboard Open : Swift 5
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(taped))
view.addGestureRecognizer(tap)
NotificationCenter.default.addObserver(self, selector: #selector(KeyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(KeyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}
//This Method Will Hide The Keyboard
#objc func taped(){
self.view.endEditing(true)
}
#objc func KeyboardWillShow(sender: NSNotification){
let keyboardSize : CGSize = ((sender.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size)!
if self.view.frame.origin.y == 0{
self.view.frame.origin.y -= keyboardSize.height
}
}
#objc func KeyboardWillHide(sender : NSNotification){
let keyboardSize : CGSize = ((sender.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size)!
if self.view.frame.origin.y != 0{
self.view.frame.origin.y += keyboardSize.height
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
}
Did you tried to debug the program to see if the code stops in the function at all(with break point)? Usually this code should work...Check if those textFields are in the super view or in a child view and if they are maybe you should call self.childView.endEditing(true).
If you really work with multiple textFields maybe you should try IQKeyboardManager library. I use it in all my projects. You can find it here: https://github.com/hackiftekhar/IQKeyboardManager. Simple to use and with good support. Just install it trough cocoa pods, put IQKeyboardManager.sharedManager().enable = true in the AppDelegate and you're ready to go. :)
Are you sure that touchesBegan is being called? If you're sure, try adding self.resignFirstResponder() to your touchesBegan function. This tells your view controller that it's no longer the first responder and should dismiss the keyboard.
If not, what you'll want to do is create a UITapGestureRecognizer, add it to your view, and wire it to a function that calls self.resignFirstResponder().

Dismiss UIPopoverPresentationController with any gesture, not just tap

So I have a simple UIPopoverPresentationController that displays some content.
User can dismiss it by tapping anywhere on the screen (default popover behaviour).
I want the popover to be dismissed if the user does any kind of tap or gesture on the screen. Preferably drag gesture.
Any idea if this is possible? And how?
try using touchesBegan:withEvent method
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = touches.first {
if touch.view == self.view {
self.dismiss()
} else {
return
}
}
}
VC is the view presented in the popover.
in the presentViewController:animated:completion: block
[self presentViewController:vc animated:YES completion:^{
UIView *v1 = vc.view.superview.superview.superview;
for (UIView* vx in v1.subviews) {
Class dimmingViewClass = NSClassFromString(#"UIDimmingView");
if ([vx isKindOfClass:[dimmingViewClass class]])
{
UIPanGestureRecognizer* pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(closePopoverOnSwipe)];
[vx addGestureRecognizer:pan];
}
}
}];
you have a UIDimmingView that holds the tap gesture that will close. just add to it. I am using the Class dimmingViewClass = NSClassFromString(#"UIDimmingView"); to avoid making direct use of undocumented APIs. I have not tried yet to send this hack to apple, but will try next week. I hope it will pass. But I tested this and it did call my selector.
I resolved this problem using custom view:
typealias Handler = (() -> Void)?
final class InteractionView: UIView {
var dismissHandler: Handler = nil
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
return self
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.dismissHandler?()
}
}
In the viewDidAppear configure this view and add to popover containerView:
fileprivate func configureInteractionView() {
let interactionView = InteractionView(frame: self.view.bounds)
self.popoverPresentationController?.containerView?.addSubview(interactionView)
interactionView.backgroundColor = .clear
interactionView.isUserInteractionEnabled = true
interactionView.dismissHandler = { [weak self] in
self?.hide()
}
}
fileprivate func hide() {
self.dismiss(animated: true, completion: nil)
}
my solution for this problem.
for example if you create a class UIViewController named MyPopoverViewController to present PopViewController.
then in the viewDidLoad() or viewWillAppear(_ animated:) Method add two GestureRecognizer as follows:
protocal MyPopoverControllerDelegate {
func shouldDismissPopover()
}
class MyPopoverViewController : UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// back trace to root view, if it is a UIWindows, add PanGestureRecognizer
// and LongPressGestureRecognizer, to dismiss this PopoverViewController
for c in sequence(first: self.view, next: { $0.superview}) {
if let w = c as? UIWindow {
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(dismissPopover(gesture:)))
w.addGestureRecognizer(panGestureRecognizer)
let longTapGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(dismissPopover(gesture:)))
w.addGestureRecognizer(longTapGestureRecognizer)
}
}
#objc private func dismissPopover(gesture: UIGestureRecognizer) {
delegate?.shouldDismissPopover()
}
}
then in your main ViewController, which this PopOverViewController presents, implements the Method of the Protocol.
extension YourMainViewController: MyPopoverControllerDelegate {
func shouldDismissPopover() {
self.presentedViewController?.dismiss(animated: true, completion: nil)
}
}

UITextField keyboard not dismissing

I have a UIViewController with a UITextField in it and I'm trying to dismiss the keyboard when I click away or the view is dismissed. However, when I call resignFirstResponder(), the keyboard still doesn't dismiss and I'm not quite sure why. Here's my code:
class MessageViewController: UIViewController, UITextFieldDelegate {
var messageTextField : UITextField = UITextField()
override func viewDidLoad() {
...
messageTextField.frame = CGRectMake(10, UIScreen.mainScreen().bounds.size.height-50, UIScreen.mainScreen().bounds.size.width-80, 40)
messageTextField.delegate = self
messageTextField.borderStyle = UITextBorderStyle.Line
messageTextField.becomeFirstResponder()
self.view.addSubview(messageTextField)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)
...
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
self.view.endEditing(true)
println("Touched")
}
func keyboardWillShow(notification: NSNotification) {
var keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey]as? NSValue)?.CGRectValue()
let messageFrame = messageTextField.frame
let newY = messageFrame.origin.y - keyboardSize!.height
messageTextField.frame = CGRectMake(messageFrame.origin.x, newY, messageFrame.size.width, messageFrame.size.height)
}
func keyboardWillHide(notification: NSNotification) {
var keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey]as? NSValue)?.CGRectValue()
let messageFrame = messageTextField.frame
let newY = messageFrame.origin.y - keyboardSize!.height
messageTextField.frame = CGRectMake(messageFrame.origin.x, newY, messageFrame.size.width, messageFrame.size.height)
}
}
Does anyone know why the keyboard isn't dismissing? I added the UITextField to the view programmatically as opposed to using storyboard. Does that make a difference?
class ViewController: UIViewController,UITextFieldDelegate {
confirm protocols
messageTextField.delegate=self
set delegate
func textFieldShouldReturn(textField: UITextField!) -> Bool
{
messageTextField.resignFirstResponder()
return true;
}
Use this code..
The following is how I did for that problem. I hope this method solve your problem.
textfield
#IBOutlet var userID : UITextField!
function.
func textFieldShouldReturn(textField: UITextField!)-> Bool
{ userID.resignFirstResponder( )
return true
}
In your desired function, you need to write this syntax.
#IBAction func login(sender: AnyObject)
{
userID.resignFirstResponder( )
}
This is in ViewDidLoad or ViewDidAppear
override func viewDidLoad( )
{
userID.delegate = self;
}
I cannot comment on the previous user. That's why I write this one.
I hope this gives you idea.
func textFieldShouldReturn(textField: UITextField) -> Bool {
self.view.endEditing(true)
//textField.resignFirstResponder()
return false
}
check delegate add into your viewDidLoad()
self.yourTextField.delegate = self;
how-to-dismiss-uitextfields-keyboard-in-your-swift-app
This might helps you :)
#vijeesh's answer will probably work, and the logic is almost correct, but it is technically wrong if you ever use more than one UITextField. textField is the UITextField parameter that is passed when textFieldShouldReturn is called. The problem is, you're just declaring:
textField.resignFirstResponder()
Your program doesn't know what UITextField you're referring to. Even though you may only have one textField in your program, and you know that it's your messageTextField, the program still doesn't know that. It just sees "textField". So you have have to tell it what to do for each UITextField in your program. Even if you have just one. This should work:
func textFieldShouldReturn(textField: UITextField) -> Bool {
if textField == messageTextField {
messageTextField.resignFirstResponder()
}
return true
}
very simple.
first you add Tap Gesture
var tap = UITapGestureRecognizer(target: self, action: "handle:")
view.addGestureRecognizer(tap)
in function handle
func handle(tap: UITapGestureRecognizer){
view.endEditing(true)
}
so you can dismiss keyboard when you click outside UITextField

Resources