Does keyboard textfield depends from textfield constraint? - swift

I'm having a problem with keyboard textfield. I'm working on swift 4 in iOS 9.2
I have a login screen which does have a lot of constraints (to be adaptable for every screen). In my login screen, I have a username and password textfield.
The problem is that when I launch the application on my IPad, the keyboard appears in center of the associated textField. Moreover, the observer won't work : When I use debugger, I never get inside the observers
But when I launch on simulator, everything works perfectly.
#My object life cycle
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
}
# My textfield delegates and observers
extension LoginViewController {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
#objc func keyboardWillShow(_ notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y == 0{
self.view.frame.origin.y -= keyboardSize.height
}
}
}
#objc func keyboardWillHide(_ notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y != 0{
self.view.frame.origin.y += keyboardSize.height
}
}
}
}
And for my textfield storyboard configuration, here is it
So what didn't I understood ?
Is the keyboard also dependants to textfield constraints ?
Why the observers aren't working on iPad but works in simulation ?
how can I move the keyboard programatically ?

Make sure your iPad/iPhone have the keyboard dock..
https://discussions.apple.com/thread/5430957

You're not inheriting from UITextFieldDelegate in your extension. Also, make sure you set your textfields as self delegate in viewDidLoad method.
#IBOutlet weak var yourViewName: UIView!
override func viewDidLoad() {
loginTextField.delegate = self
passwordTextField.delegate = self
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector:#selector(LoginViewController.keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.addObserver(self, selector:#selector(LoginViewController.keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardDidHide, object: nil)
}
extension LoginViewController : UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
#objc func keyboardWillShow(notification: NSNotification) {
UIView.animate(withDuration: 0.3) {
self.usernameField.frame.origin.y -= 43
self.usernameLine.frame.origin.y -= 43
self.passwordField.frame.origin.y -= 43
self.passwordLine.frame.origin.y -= 43
self.loginBtn.frame.origin.y -= 65
}
}
#objc func keyboardWillHide(notification: NSNotification) {
UIView.animate(withDuration: 0.3) {
self.usernameField.frame.origin.y += 33
self.usernameLine.frame.origin.y += 33
self.passwordField.frame.origin.y += 33
self.passwordLine.frame.origin.y += 33
self.loginBtn.frame.origin.y += 55
}
}

easies way , you first take one scroll view and add top , bottom , leading , trailing contains of all 4 with value 0 and than take one view and add 4 constrain for view to top,bottom,leading,traling to scrollview and put your textfield inside that view and add constrains as per your design concept relative to view which you added in this scrollview ,
now , whenever your keyboard will open your view will move toward upside and textfield and keyboard both will be look properly

Related

Subviews does not follow constraints and dismissing keyboard leaves a black space at the bottom of the view

Whenever my root view controller(Navigation Controller) pushes to my 2nd VC (let's call it VC2), all of VC2's subviews: buttons, labels, and text fields get shifted down even though I've set constraints on it. Also, whenever I dismiss the keyboard after editing the text field, the view goes back to its intended place where the constraints set it to be and leaves a black space at the bottom of the view. Here is my code for VC2:
override func viewDidLoad() {
super.viewDidLoad()
view.layer.backgroundColor = UIColor(named: const.white)?.cgColor
nameField.delegate = self
emailField.delegate = self
passField.delegate = self
setUpTitles()
setUpTextFields()
setUpButton()
addPaddingAndBorder(to: nameField)
addPaddingAndBorder(to: emailField)
addPaddingAndBorder(to: passField)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChangeView(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardIsDone(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChangeView(notification:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
let tap = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard(_:)))
self.view.addGestureRecognizer(tap)
}
deinit {
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
}
#objc func keyboardWillChangeView(notification: Notification){
view.frame.origin.y = -150 //This is temporary
}
#objc func keyboardIsDone(notification: Notification){
view.frame.origin.y = 0
}
#objc func dismissKeyboard(_: UITapGestureRecognizer){ //Tapping outside the keyboard
nameField.resignFirstResponder()
emailField.resignFirstResponder()
passField.resignFirstResponder()
}
//Executed when hitting return key
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
nameField.resignFirstResponder()
emailField.resignFirstResponder()
passField.resignFirstResponder()
view.frame.origin.y = 0
return true
}
Before:
After:
When you use responders you should make your first textfield to be a first responder, like that:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
firstTextField.becomeFirstResponder()
}
This is my answer in different issue

iOS keyboard hides a UITextField

When I press on a UITextField that is on the lower part of the screen, it is hidden by the keyboard.
What I wanted to do is moving up the view, with the standard iOS animation, reaching the UITextField that in which I am inserting some text.
I am developing the app in Swift 5 (Xcode 10.2)
The result that I have reached is that now I can move the view (a little earlier than desired) but the view moves every time I press on a UITextField, not only the one that will be hided by the keyboard.
class ViewController: UIViewController {
deinit {
NotificationCenter.default.removeObserver(self)
}
override func viewDidLoad() {
super.viewDidLoad()
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)
}
#objc func keyboardWillShow(notification: NSNotification) {
guard let userInfo = notification.userInfo else {
return
}
guard let keyboardSize = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {
return
}
let keyboardFrame = keyboardSize.cgRectValue
if self.view.frame.origin.y == 0 {
self.view.frame.origin.y -= keyboardFrame.height
}
}
#objc func keyboardWillHide(notification: NSNotification) {
guard let userInfo = notification.userInfo else {
return
}
guard let keyboardSize = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {
return
}
let keyboardFrame = keyboardSize.cgRectValue
if self.view.frame.origin.y != 0 {
self.view.frame.origin.y += keyboardFrame.height
}
}
}
The result that I want to obtain is that if the user presses on a UITextField that will be hided by the keyboard, then, a little bit after the keyboard appeared, the view is moved up until the user can see the UITextField that has pressed.
I've searched a long for a solution to this problem but all others that I've seen seems outdated or not doing what I'm trying to achieve.
you can try by taking scrollview :
import UIKit
class ViewController: UIViewController {
#IBOutlet var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
NotificationCenter.default.addObserver(self,selector:#selector(self.keyboardWillShow),name:UIResponder.keyboardDidShowNotification, object: nil)
NotificationCenter.default.addObserver(self,selector: #selector(self.keyboardWillHide),name:UIResponder.keyboardDidHideNotification, object: nil)
}
#objc func keyboardWillShow(notification: Notification) {
guard let userInfo = notification.userInfo,
let frame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
else{
return
}
let contentInset = UIEdgeInsets(top: 0, left: 0, bottom: frame.height, right: 0)
scrollView.contentInset = contentInset
}
#objc func keyboardWillHide(notification: Notification)
{
scrollView.contentInset = UIEdgeInsets.zero
}
}

Keyboard height observer gives inacurate height

I'm using the observer below to determine my keyboard height.
I then use this keyboardHeight to adjust the bottom constraint for a UIView (image attached):
#IBOutlet weak var postViewBottomContraint: NSLayoutConstraint!
override func viewWillAppear(_ animated: Bool) {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: .UIKeyboardWillShow, object: nil)
}
and the method:
#objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
keyboardHeight = keyboardSize.height
print("Keyboard Height is:",keyboardHeight)
}
}
, and here is where I change the height:
func startEditing() {
UIView.animate(withDuration: 0.5) {
self.postViewBottomContraint.constant = self.keyboardHeight
print ("Bottom constraint is:",self.postViewBottomContraint.constant.description)
self.postTextView.textColor = UIColor.lightGray
self.view.layoutIfNeeded()
}
}
It adds a space between the keyboard and the UIView even though the keyboardHeight and the bottomConstraint are identical (in my case it is 253.0), is it adding something extra that is not visible or is there something else going on?

Keyboard not being shown correctly

I have got the following functions that make the keyboard not to cover the TextView, but the keyboard is not showing correctly. Instead, there appears a kind of all black "keyboard" with no keyboard keys.
func textViewDidBeginEditing(_ textView: UITextView) {
moveTextView(textView, moveDistance: -250, up: true)
}
func textViewDidEndEditing(_ textView: UITextView) {
moveTextView(textView, moveDistance: -250, up: false)
}
func textViewShouldReturn(_ textView: UITextView) -> Bool {
textView.resignFirstResponder()
return true
}
func moveTextView(_ textView: UITextView, moveDistance: Int, up: Bool) {
let moveDuration = 0.3
let movement: CGFloat = CGFloat(up ? moveDistance : -moveDistance)
UIView.beginAnimations("animateTextView", context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(moveDuration)
self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
UIView.commitAnimations()
}
Do somebody have an idea of why and how to fix it?
Thank you for your time.
Try to do it another way. Add UITextViewDelegate to your viewController. Add smth like this in viewDidLoad():
self.yourTextView1.delegate = self
self.yourTextView2.delegate = self
//For scrolling the view if keyboard on
NotificationCenter.default.addObserver(self, selector: #selector(YourViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(YourViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
And add this to your ViewController:
var keyBoardAlreadyShowed = false //using this to not let app to scroll view
//if we tapped UITextField and then another UITextField
func keyboardWillShow(notification: NSNotification) {
if !keyBoardAlreadyShowed {
self.view.frame.origin.y -= 50 // we will scroll on it
keyBoardAlreadyShowed = true
}
}
func keyboardWillHide(notification: NSNotification) {
self.view.frame.origin.y += 50
keyBoardAlreadyShowed = false
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
Hope it helps
Try this:
iOS Simulator > Restore Content and Settings.
Clean the Project, and restart Xcode.

Move button when keyboard appears swift

In a UIViewController I have several text fields and a button is on the bottom of the UIViewController.
For the button, I have set a bottom constraint with a constant of 0.
Then I made an outlet from the bottom constraint to the UIViewController.
When I run my code, the button does not move upwards. I have seen suggestions on stackoverflow that I should add UIScrollView, but that means, I would have to delete all the objects on the UIViewController, put the UIScrollView and then put my objects on the UIVIewController again.
#IBOutlet weak var bottomConstraint: NSLayoutConstraint!
// When tapping outside of the keyboard, close the keyboard down
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.view.endEditing(true)
}
// Stop Editing on Return Key Tap. textField parameter refers to any textfield within the view
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
// When keyboard is about to show assign the height of the keyboard to bottomConstraint.constant of our button so that it will move up
func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
if let keyboardSize: CGRect = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
bottomConstraint.constant = keyboardSize.size.height
view.setNeedsLayout()
}
}
}
// When keyboard is hidden, move the button to the bottom of the view
func keyboardWillHide(notification: NSNotification) {
bottomConstraint.constant = 0.0
view.setNeedsLayout()
}
The typical way to address this would be to move the keyboard with code like this:
in ViewController class:
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
if view.frame.origin.y == 0{
let height = keyboardSize.height
self.view.frame.origin.y += height
}
}
}
func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
if view.frame.origin.y != 0 {
let height = keyboardSize.height
self.view.frame.origin.y -= height
}
}
}
in ViewDidLoad method:
NotificationCenter.default.addObserver(self, selector: Selector("keyboardWillShow:"), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: Selector("keyboardWillHide:"), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
Please Read This:
The way you are trying to solve your problem is not allowed. In the code above, if you change view to your button variable name, the button will shoot up and then fall back down. This is because Auto Layout and Programmatic layout do not work together, it is one or the other. The way you fix this is by programmatically creating that button (with CGRect), then using the code above to move only that button on keyboard press. (Do that by changing view to your button variable name.
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if view.frame.origin.y == 0{
let height = keyboardSize.height
self.yourBtn.frame.origin.y += height
}
}
}
func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if view.frame.origin.y != 0 {
let height = keyboardSize.height
self.yourBtn.frame.origin.y -= height
}
}
}
To programmatically create the button you would use code similar to this:
myButton.frame = CGRect(...)
Complimentary to Ryan's answer above, this can be done all with auto-layout and no need for frames and CGRect.
Swift 5
In your view, constrain your button as you normally would but add a reference to the constraint for modification when the keyboard hides/shows:
var bottomButtonConstraint = NSLayoutConstraint()
bottomButtonConstraint = yourButton.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -12)
bottomButtonConstraint.isActive = true
In your ViewController's viewDidLoad():
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)
Also in your ViewController:
#objc private func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
self.yourCustomView.bottomButtonConstraint.constant -= keyboardSize.height
}
}
#objc private func keyboardWillHide(notification: NSNotification) {
self.yourCustomView.bottomButtonConstraint.constant = -12
}
You need add(viewDidLoad) observers to call your functions:
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWillShow), name: UIKeyboardDidShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWillHide), name: UIKeyboardDidHideNotification, object: nil)
Consider using this pod: https://cocoapods.org/pods/IQKeyboardManager
In AppDelegate.swift, just import IQKeyboardManagerSwift framework and enable IQKeyboardManager.
import IQKeyboardManagerSwift
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
IQKeyboardManager.shared.enable = true
return true
}
}