7

Having a problem with my keyboard dismissal. Every time I swipe down on the key board I have this blank Black background that I would like to get rid of. I dont understand why its there. This is hooked up to my tableView.

My keyBoard

What it looks like when I swipe Down

I have the keyBoard on the tableView set as Dismiss Interactively

//                      KeyBoard (move text box above keyboard)




    //KeyBoard - exit when the view is touched
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.view.endEditing(true)
    }



    // Start Editing The Text Field
    func textFieldDidBeginEditing(_ messageTextField: UITextField) {
        moveTextField(messageTextField, moveDistance: -215, up: true)
    }

    // Finish Editing The Text Field
    func textFieldDidEndEditing(_ messageTextField: UITextField) {
        moveTextField(messageTextField, moveDistance: -215, up: false)
    }

    // Hide the keyboard when the return key pressed
    func textFieldShouldReturn(_ messageTextField: UITextField) -> Bool {
        messageTextField.resignFirstResponder()
        return true
    }

    // Move the text field in a pretty animation!
    func moveTextField(_ messageTextField: UITextField, moveDistance: Int, up: Bool) {
        let moveDuration = 0.3
        let movement: CGFloat = CGFloat(up ? moveDistance : -moveDistance)

        UIView.beginAnimations("animateTextField", context: nil)
        UIView.setAnimationBeginsFromCurrentState(true)
        UIView.setAnimationDuration(moveDuration)
        self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
        UIView.commitAnimations()
    }
codechicksrock
  • 291
  • 5
  • 14
  • 4
    You have to listen to keyboard event to move view together with keyboard, not using textfield delegate – Tj3n Mar 09 '17 at 08:33
  • You are giving -215 in both cases, which doesn't make sense. – El Tomato Mar 09 '17 at 08:58
  • 1
    The black view is probably your UIWindow. The keyboard finishes hidding before the View moves down animation ends (endEditing is called before moveTextField) which is why the black view/UIWindow gets displayed which is behind your UIView. Post your entire code for further help or probably change your approach to keyboard events as highlighted by Tj3n. – SHN Feb 04 '19 at 11:50
  • 1
    Simply Use IQ keyboardManager the best drop in Available – Nishant Pathania Feb 05 '19 at 05:42
  • See [this](https://stackoverflow.com/q/1126726/7698092) popular answer. It suggests to listen to keyboard events and adjust frame of view accordingly. Or just use IQKeyboardManager. Its a simple solution – Awais Fayyaz Feb 07 '19 at 06:37

4 Answers4

0

Add your view in inputAccessoryView of UITextField like

yourtextField.inputAccessoryView = yourView

And setup your tableview like this

yourTableView.keyboardDismissMode = .Interactive

enter image description here

jignesh Vadadoriya
  • 3,244
  • 3
  • 18
  • 29
0

The black view is the view behind your ViewController. The view gets visible as the keyboard gets hidden before the moveTextField animation finishes thereby making the black view visible.

Dismiss keyboard in the animation block itself so that Keyboard dismisses with the screen. And call the moveTextField from touchesBegan. You will need to get an reference of your UITextField (tf) in the below code. Also, don't forget to handle Return key.

//KeyBoard - exit when the view is touched
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    if tf.isEditing {
    moveTextField(tf, moveDistance: -215, up: false)
    }

    //        self.view.endEditing(true)
}



// Start Editing The Text Field
func textFieldDidBeginEditing(_ messageTextField: UITextField) {
    moveTextField(messageTextField, moveDistance: -215, up: true)
}

// Finish Editing The Text Field
func textFieldDidEndEditing(_ messageTextField: UITextField) {
    //        moveTextField(tf, moveDistance: -215, up: false)
}

// Hide the keyboard when the return key pressed
func textFieldShouldReturn(_ messageTextField: UITextField) -> Bool {
    //        messageTextField.resignFirstResponder()
    if tf.isEditing {
        moveTextField(tf, moveDistance: -215, up: false)
    }
    return true
}

// Move the text field in a pretty animation!
func moveTextField(_ messageTextField: UITextField, moveDistance: Int, up: Bool) {
    let moveDuration = 0.3
    let movement: CGFloat = CGFloat(up ? moveDistance : -moveDistance)

    UIView.beginAnimations("animateTextField", context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)
    UIView.setAnimationDuration(moveDuration)
    if !up {
        self.view.endEditing(true)
    }
    self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
    UIView.commitAnimations()
}
SHN
  • 785
  • 7
  • 23
  • I tried above solution with text view but it doesn't fix the black background issue as view animation is delayed after keyboard hides. On a side notes shouldn't we be handling the keyboard in keyboard notification instead of text field delegates? Is it proper way to do it? – Raymond Jun 11 '20 at 18:16
0

Better solution

If you have seen native Messaging app of ios you dismiss keyboard with swipe gesture interactively. with that your problem also will be solved as we are not adding textview / textfield and button (send) to the UIView as subview it will be added as accessory view of view controller

Step 1:

Goto Storybaord select collection view and on property list change Keyboard dismiss mode to Dismiss interactively

enter image description here

and also make your collection view to fill the entire screen (don't add space for your textField and send button )

Step 2:

In your view controller add following properties on top

var viewAcc: UIView?
var sendButton: UIButton!
var inputTextField: UITextField!

override var inputAccessoryView: UIView? {
    return viewAcc
}

override var canBecomeFirstResponder: Bool {
    return true
}

In ViewDidLoad Method add following code for init view and add textField and send button

    viewAcc = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 44))
    viewAcc?.backgroundColor = UIColor.white
    inputTextField = UITextField (frame: CGRect(x:8, y:0, width:UIScreen.main.bounds.width, height: 44 ))
    inputTextField.inputAccessoryView = nil
    inputTextField.delegate = self as? UITextFieldDelegate
    inputTextField.placeholder = "Enter message..."
    viewAcc?.backgroundColor = .white
    viewAcc?.addSubview(inputTextField);

    let topBorderView = UIView()
    topBorderView.backgroundColor = UIColor(white: 0.5, alpha: 0.5)
    viewAcc?.addSubview(topBorderView)
    viewAcc?.addConstraintsWithFormat(format: "H:|[v0]|", views: topBorderView)
    viewAcc?.addConstraintsWithFormat(format: "V:|[v0(0.5)]", views: topBorderView)

    sendButton = UIButton(type: .system)
    sendButton.isEnabled = true
    sendButton.titleLabel?.font = UIFont.systemFont(ofSize: 16)
    sendButton.setTitle("Send", for: .normal)
    sendButton.contentEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
    sendButton.addTarget(self, action: #selector(handleSend), for: .touchUpInside)
    viewAcc?.addSubview(sendButton)

    inputTextField.translatesAutoresizingMaskIntoConstraints = false
    sendButton.translatesAutoresizingMaskIntoConstraints = false
    viewAcc?.addConstraint(NSLayoutConstraint(item: inputTextField, attribute: .left, relatedBy: .equal, toItem: viewAcc, attribute: .left, multiplier: 1, constant: 8))
    viewAcc?.addConstraint(NSLayoutConstraint(item: inputTextField, attribute: .top, relatedBy: .equal, toItem: viewAcc, attribute: .top, multiplier: 1, constant: 7.5))
    viewAcc?.addConstraint(NSLayoutConstraint(item: inputTextField, attribute: .right, relatedBy: .equal, toItem: sendButton, attribute: .left, multiplier: 1, constant: -2))
    viewAcc?.addConstraint(NSLayoutConstraint(item: inputTextField, attribute: .bottom, relatedBy: .equal, toItem: viewAcc, attribute: .bottom, multiplier: 1, constant: -8))
    viewAcc?.addConstraint(NSLayoutConstraint(item: sendButton, attribute: .right, relatedBy: .equal, toItem: viewAcc, attribute: .right, multiplier: 1, constant: 0))
    viewAcc?.addConstraint(NSLayoutConstraint(item: sendButton, attribute: .bottom, relatedBy: .equal, toItem: viewAcc, attribute: .bottom, multiplier: 1, constant: -4.5))

Now run the app you can see the textField and button at bottom and you can also see your full collection view. and you can dismiss keyboard with just hold and swipe down

Hope it is helpful

Prashant Tukadiya
  • 15,838
  • 4
  • 62
  • 98
-2

I have faced similar issue some time ago and found the following way.

First of all add a bottom constraint for your view and connect it with IBOutlet in VC class:

@IBOutlet weak var bottomConstraint: NSLayoutConstraint!

Then add notifications to your viewWillAppear overriden function. I believe that gesture recognizer is already added by you. Anyway I shared my gesture recognizer functions in the bottom of this answer.

override func viewWillAppear(_ animated: Bool) {
    //self.addGestureRecognizer()
    // To move view when keyboard appears/hides
    NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillChangeFrame(notification:)), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
}

And the main thing is the function below. I added it as an extension. The idea is to change the constraint with animation when a keyboard moves down. You will see that I have used a hardcoded value for iPhone 4s (I had an issue with this solution on iPhone 4s and iOS 9). So you can remove it if you do not support 4s model.

extension CheckoutViewController {
    // Function to scroll view when keyboard appears/disappears
    @objc func keyboardWillChangeFrame (notification: NSNotification) {
        if let userInfo = notification.userInfo {
            let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
            let duration:TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 1
            let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
            let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIViewAnimationOptions.curveEaseInOut.rawValue
            let animationCurve:UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
            if (endFrame?.origin.y)! >= UIScreen.main.bounds.size.height {
                self.bottomConstraint?.constant = 0.0
            } else {
                // Keyboard is opened
                var constraintValue = endFrame?.size.height ?? 0
                if UIDevice.current.modelName != "iPhone 4s" {
                    constraintValue = constraintValue - 35
                }
                self.bottomConstraint?.constant = constraintValue
            }
            UIView.animate(withDuration: duration,
                           delay: TimeInterval(0),
                           options: animationCurve,
                           animations: { self.view.layoutIfNeeded() },
                           completion: nil)
        }
    }
}

Unfortunately I have no access to XCode at the moment, so used by code as an example. I hope it will help you to fix your issue.

Gesture recognizer extension for UIViewController that I used:

extension UIViewController {
    func addGestureRecognizer() {
        // Gesture recognizer To dismiss keyboard
        let singleTap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
        singleTap.cancelsTouchesInView = false
        singleTap.numberOfTapsRequired = 1
        self.view.addGestureRecognizer(singleTap)
    }

    @objc func handleTap(_ recognizer: UITapGestureRecognizer) {
        self.view.endEditing(false)
    }
}
DJ-Glock
  • 1,277
  • 2
  • 12
  • 39