In my Collection ViewController I have Collection View Cell and textField in them. Every time I press on textField and keyboard shows up the view is moved up leaving white space...
Before keyboard is showed
everything is ok
After keyboard is shown
cell layout is up... and you cannot see the entire cell content.
I tried one answer solution below and I got this: black view on top
You can add observers for when the keyboard shows and hides and shift your views accordingly:
override func viewDidLoad() {
super.viewDidLoad()
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)
}
#objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
let newYOrigin = CGFloat(100) // or whatever new value you want
self.yourView.frame.origin.y = newYOrigin
}
}
#objc func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
let newYOrigin = CGFloat(0) // or whatever new value you want
self.yourView.frame.origin.y = newYOrigin
}
}
Related
I am trying to build an input screen for the iPhone in swift. The screen has a number of input fields. Most of them on the top of the screen, but three fields are at the bottom. When the user tries to edit the text on the bottom of the screen, the keyboard will pop up and it will cover the screen. I found a simple solution to move the screen up when this happens, but the result is that the screen always moves up and the fields on top of the screen move out of reach when the user tries to edit those.
for example : I want form txt1 to txt4 the view does not go up.
Is there a way to have the screen only move when the bottom fields are edited?
I have used this code I found here:
override func viewDidLoad() {
txt1.delegate = self
txt2.delegate = self
txt3.delegate = self
txt4.delegate = self
txt5.delegate = self
txt6.delegate = self
txt7.delegate = self
txt8.delegate = self
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)
}
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if view.frame.origin.y == 0 {
self.view.frame.origin.y -= keyboardSize.height
}
}
}
#objc func keyboardWillHide(notification: NSNotification) {
if view.frame.origin.y != 0 {
self.view.frame.origin.y = 0
}
}
**
I did this function to start show keyboard while I start editing on textfields:
public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool{
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}
but here it's going for all the textfield that I have and its the same problem.
how can I make it for specific textfield?
This is my view.
When I click inside the text view the keyboard was coming on top. so I added made a class and in that class, I added these functions.
var objectObserver:UIViewController?
func setKeyboardResponsiviness(observer:UIViewController){
objectObserver = observer
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}
#objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if objectObserver!.view.frame.origin.y == 0 {
objectObserver!.view.frame.origin.y -= keyboardSize.height
}
}
}
#objc func keyboardWillHide(notification: NSNotification) {
if objectObserver!.view.frame.origin.y != 0 {
objectObserver!.view.frame.origin.y = 0
}
}
After adding the code the whole screen slides up which was the intended goal but as a side effect, half of the text view is out of the screen. Any idea how I can fix this?
The simple solution is to use IQKeyboardManagerSwift.
pod 'IQKeyboardManagerSwift' // add this in your pod file.
Add the following code in didFinishLaunchingWithOptions.
IQKeyboardManager.shared.enable = true
I hope this helps.
Like so many other threads here I need to move a view up so its visible while they keyboard is visible and the correct answer seems to be this in most cases:
var isKeyboardVisible = false
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}
#objc func keyboardWillShow(notification: NSNotification) {
if !isKeyboardVisible {
if let keyboardRectValue = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
let keyboardHeight = keyboardRectValue.height
if self.view.frame.origin.y == 0 {
self.view.frame.origin.y -= keyboardHeight
}
}
isKeyboardVisible = true
}
}
#objc func keyboardWillHide(notification: NSNotification) {
if isKeyboardVisible {
if let keyboardRectValue = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
let keyboardHeight = keyboardRectValue.height
if self.view.frame.origin.y != 0 {
self.view.frame.origin.y += keyboardHeight
}
}
isKeyboardVisible = false
}
}
This however doesn't work if the user uses a custom keyboard.
keyboardHeight is 0 if a custom keyboard is used. But if the stock keyboard is used the height seems correct.
Can be reproduced if you install Google GBoard and try to get the height of the keyboard.
I'm quite certain I'm not the only one using a custom keyboard, so I guess there should be some kind of solution for this problem which is dynamic and doesn't include a long if/else list.
Addition 1: I found this answer which suggests to add an inputAccessoryView to the textfield. This makes perfect sense to me as this view will always be exactly above the keyboard. There is how ever two problems with this.
One, content is still being hidden behind the keyboard.
Two, if the textfield which is currently focused is behind the keyboard, should I add a duplicate of it to the ´ inputAccessoryView´ so whatever is written is shown in both? This seems like a lot of extra work.
What does apps like whatsapp do to solve this? There must be a universal way to adapt the content to fit above any keyboard (stock or custom) and also not disappear above the top part of the screen!?!?
So this is the cleanest way I've found.
Swift 4.2
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(keyboardShowHide), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardShowHide), name: UIResponder.keyboardWillHideNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardShowHide), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
}
#objc func keyboardShowHide(notification: NSNotification) {
if let keyboardRectValue = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
self.view.frame.size.height = UIScreen.main.bounds.height - keyboardRectValue.height
}
}
Many solutions for getting the the necessary view above the keyboard is to move all the content up and beyond the screen limit. I consider this bad practice there the will be content totally inaccessible to the user outside of the screen as long as the keyboard is visible.
As in my solution above I prefer to adjust the size of the content instead of moving it. So if the content is in a tableview or a scrollview everything will still be available while the keyboard is visible.
If you however prefer that approach just change this line:
self.view.frame.size.height = UIScreen.main.bounds.height - keyboardRectValue.height
for this:
self.view.frame.origin.y = -keyboardRectValue.height
I'm using IQKeyboardManager to keep the text fields to go up after typing with the keyboard.
I don't want to scroll to a specific view even when clicked on the text field. Below is the screenshot of the design. I want the 'header' to remain on top.
From their documentation, there is a way to keep the navigation bar remain on top.
Disable the IQKeyboardManager for your ViewController.
for that,
IQKeyboardManager.sharedManager().disableInViewControllerClass(ViewController.self)
And In that viewController write the following code. It will move your view up as per keyboard height
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
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
}
}
}
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
}
}
}
Now you want your "HEADER" view remain on TOP then,
Do like this :
**
YourViewController.view -> [headerView][contentView]
**
Put textfield in [contentView] And change [contentView].y instead of Self.view in above code.
Disable the IQKeyboardManager for your viewController:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
IQKeyboardManager.sharedManager().enable = false
NotificationCenter.default.addObserver(self, selector: #selector(Login.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(Login.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
Handle keyboard:
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y == 0{
self.table_view.frame.origin.y -= keyboardSize.height
}
}
}
func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y != 0{
self.table_view.frame.origin.y += keyboardSize.height
}
}
}
Remove observer:
override func viewWillDisappear(animated: Bool) {
IQKeyboardManager.sharedManager().enable = true
NSNotificationCenter.defaultCenter().removeObserver(self)
}
Answers from #Wolverine and #Bhavin Ramani were great: the best way to keep your custom header staying at top is to manually handle your keyboard (according to IQKeyboardSwift author's comment). If you use iOS default navigation bar, it seems it's handled for you by the library.
Here I just want to share some updates on this topic, for my future reference, as the answers are a bit old and some Swift syntax has changed. Code below is written in Xcode 13.2, targeting iOS 13+.
Firstly, you want to disable KQKeyboardManager by doing
IQKeyboardManager.shared.enable = false
Please note this lines only disables moving text field up feature, other IQKeyboard features such as resign on touch outside, auto tool bar, and etc., are not disabled by this line, this is usually what you want.
Then, you register keyboard events observer in view controller's viewDidLoad, remove observers in deinit.
override func viewDidLoad() {
super.viewDidLoad()
IQKeyboardManager.shared.enable = false
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardWillShow),
name: UIResponder.keyboardWillShowNotification,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardWillHide),
name: UIResponder.keyboardWillHideNotification,
object: nil)
}
deinit {
IQKeyboardManager.shared.enable = true
NotificationCenter.default.removeObserver(self)
}
Next, add view moving up/down methods for keyboard show/hide.
#objc private func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect {
print("keyboardSize.height", keyboardSize.height)
// using the right key here is important, because
// keyboardFrameEndUserInfoKey is an user info key
// to retrieve the keyboard’s frame at the END of its animation.
// here you move up the views you need to move up
// if you use auto layout, update the corresponding constraints
// or you update the views' frame.origin.y
// you may want to do the updates within a 0.25s animation
}
}
#objc private func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? CGRect {
// reset views to their original position on keyboard dismiss
}
}
You may also want to enable/disable auto tool bar, as it could make your keyboard height unstable.
// in viewDidLoad set to false, in deinit set back to true (if you need it)
IQKeyboardManager.shared.enableAutoToolbar = false
I tryed to use something to move up the view because when I go at the end of my form and I tap on the last textfield I can't see what I write in because the keyboard appears over the text field, so I found something but here if I tap on a textfield on the top of the view this one gonna gonna be hide by going upper, that's the code:
override func viewDidLoad() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
self.view.frame.origin.y -= keyboardSize.height
}
}
func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
self.view.frame.origin.y += keyboardSize.height
}
}
But here there is an another problem, when I tap two times one an another textfield, a black bloc appears.
So what I wan't to do is detect the textfield and put the keyboard under it so move up the textfield. And detect if the keyboard is already here do don't show a black bloc. But I haven't found solution about.
I have used this code in my project :
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
// Register Keyboard Notification
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
// Remove Keyboard notification observer
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func keyboardWillShow(notification: NSNotification)
{
var userInfo = notification.userInfo!
var keyboardFrame:CGRect = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).CGRectValue()
keyboardFrame = self.view.convertRect(keyboardFrame, fromView: nil)
var contentInset:UIEdgeInsets = self.scrollView.contentInset
contentInset.bottom = keyboardFrame.size.height - 30 //Set this value (30) according to your code as i have navigation tool bar for next and prev.
self.scrollView.contentInset = contentInset
}
func keyboardWillHide(notification: NSNotification) {
let contentInset:UIEdgeInsets = UIEdgeInsetsZero
self.scrollView.contentInset = contentInset
}
Hope It will help you!!
Add a bool property self.isKeyBoardUp
In keyboardWillShow:
If isKeyBoardUp return, else set it to true and do your stuff.
In keyboardWillHide:
If not isKeyBoardUp return, else set it to false and do your stuff.
Please check this code i am try it and works for get this one from
Move textfield when keyboard appears swift
func keyboardWasShown(notification: NSNotification) {
var info = notification.userInfo!
var keyboardFrame: CGRect = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue()
UIView.animateWithDuration(0.1, animations: { () -> Void in
self.bottomConstraint.constant = keyboardFrame.size.height + 20
})
}