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
}
}
Related
I want to move textfield up when keyword covers the textfield. Now I am facing a problem with the moving textfield up: it moves up but every time when I click one of the textfields.
How to move the textfield up only when it is covered by keyword?
Also, I have scrolling when the textfield tapped, but it does not work correctly
Here is my code:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
self.hideKeyboardWhenTappedAround()
}
#objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
if view.frame.origin.y == 0 {
self.view.frame.origin.y -= keyboardSize.height
}
}
guard let userInfo = notification.userInfo else { return }
var keyboardFrame:CGRect = (userInfo[UIResponder.keyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
keyboardFrame = self.view.convert(keyboardFrame, from: nil)
var contentInset:UIEdgeInsets = self.scrollView.contentInset
contentInset.bottom = keyboardFrame.size.height + 20
scrollView.contentInset = contentInset
}
#objc func keyboardWillHide(notification: NSNotification) {
let contentInset:UIEdgeInsets = UIEdgeInsets.zero
scrollView.contentInset = contentInset
if self.view.frame.origin.y != 0 {
self.view.frame.origin.y = 0
}
}
Simply use this cocoapods to manage all text fields
pod 'IQKeyboardManagerSwift'
Install pods in your project
Add the below line in your AppDelegate file under the didFinishLaunchingWithOptions method
IQKeyboardManager.shared.enable = true
Here is a reference link -- IQKeyboardManagerSwift
Solutions.
First way you can listen keyboardWillChangeFrameNotification
Second way you can hide keywords textField.autocorrectionType = .no
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?
I have a very small question - can we calculate the Keyboard Height with out using Notification. All the answers in stake overflow is using NSNotification but is there simpler way of calculating it?
below is the code:
NotificationCenter.default.addObserver( self, selector: #selector(keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil
#objc func keyboardWillShow(notification: ) {
if let userInfo = notification.userInfo {
if let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
let kbHeight = keyboardSize.height
}
}
}
Code to use keyboard height in textFieldDidBeginEditing:
var kbHeight: CGFloat?
#objc func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
if let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
kbHeight = keyboardSize.height
}
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField.frame.maxY > self.kbHeight!
{
self.scrollView.setContentOffset(CGPoint.init(x: 0, y: textField.frame.maxY - (self.kbHeight! + 2.0)), animated: true)
}
else{
return
}
print(textField.frame.maxY)
print(self.view.frame.height * 0.6)
print(textField.frame.maxY - self.view.frame.height * 0.6)
}
func textFieldDidEndEditing(_ textField: UITextField)
{
self.scrollView.setContentOffset(CGPoint.init(x: 0, y: 0), animated: true)
self.view.endEditing(true);
}
actually i don't have any idea for getting keyboard hight directly.. but you can save the keyboard height in variable when keyboard is present and use them where you want
Use these two Observers for keyboard and manage the keyboard hight
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)
Must be remove these observer at dinit method when pop or dismiss the view controller.
deinit {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
Selector methods for keyboard notification ...
#objc func keyboardWillShow(notification: NSNotification) {
var keyBoardHeight: CGFloat = 0
if let keyboardFrame: NSValue = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue {
let keyboardRectangle = keyboardFrame.cgRectValue
keyBoardHeight = keyboardRectangle.height
}
}
#objc func keyboardWillHide(notification: NSNotification) {
}
I have two textfields inside a subview and i am trying to move subview up when any of textfield clicked inside subview and keyboard appears. The problem is subview moves down when subview is up and i clicked on second textfield. It should hide when I touch any of the view except textfields.
The definition of my two textfield and subview;
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var loginView: UIView!
The code in viewDidLoad
NotificationCenter.default.addObserver(self, selector:#selector(keyboardWillShow), name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector:#selector(keyboardWillHide), name: .UIKeyboardWillShow, object: nil)
And two other functions which show and hide keyboard
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if loginView.frame.origin.y == 0{
let height = keyboardSize.height
self.loginView.frame.origin.y += height
}
}
}
func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if loginView.frame.origin.y != 0 {
let height = keyboardSize.height
self.loginView.frame.origin.y -= height
}
}
}
I would like to know how can I avoid subview down when I click textfield while subview is up.
Just make the y-origin of view to zero when keyboard is down.
When you tap the other textField while keyboard is open this is .
func keyboardWillHide(notification: NSNotification) {
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if loginView.frame.origin.y == 0{
let height = keyboardSize.height
self.loginView.frame.origin.y = -height
}
}
}
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if loginView.frame.origin.y != 0 {
let height = keyboardSize.height
self.loginView.frame.origin.y = 0
}
}
}
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
}
}