I was first having problems with textfields on the bottom of the screen because the keyboard would cover them. Now that I fixed that problem I have a new one. When I try to enter text in a textfield that is at the top of the screen, the screen rises and does not let me see what I'm typing.
I think what I would ideally like to do is change how much the keyboard pushes the screen up. Below is the code I used for the initial change.
I just started learning to develop two weeks ago so I'm still getting familiar with all the syntax and functions.
var kbHeight: CGFloat!
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(animated:Bool) {
super.viewWillAppear(animated)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
if let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
kbHeight = keyboardSize.height
self.animateTextField(true)
}
}
}
func keyboardWillHide(notification: NSNotification) {
self.animateTextField(false)
}
func animateTextField(up: Bool) {
var movement = (up ? -kbHeight : kbHeight)
UIView.animateWithDuration(0.3, animations: {
self.view.frame = CGRectOffset(self.view.frame, 0, movement)
})
}
It's a suggestion
Instead of setting View's frame. put the view inside a UIScrollView.
then inside your keyboardWillShow and keyboardWillHide methods adjust the scrollview frame accordingly.
and set the scrollview's content offset using the "scrollRectToVisible" inside the the textfield delegate method.
- (void) textFieldDidBeginEditing:(UITextField *)textField
{
CGRect rectText = textField.frame;
self.scrollVIew scrollRectToVisible:rectText animated:TRUE];
}
Related
Although I've searched, I'm confused about how best to approach this.
I have a tableView where the bottom cell is an input to the list, in the same way apple reminders work. When there are too many items on the list, the keyboard covers the list and I can't see what I'm typing.
My thought it I need to change the physical size of the table view and ensure it is scrolled to the bottom when the keyboard shows.
Some have said keyboard observers but the majority of code I've found for this is out of date and errors when put into Xcode.
NSNotificationCenter Swift 3.0 on keyboard show and hide
This is the best I can find but I'm also hearing about contraints, using a UITableViewController instead of embedding a UITableView and so on ...
This is the code I have so far:
NotificationCenter.default.addObserver(self, selector: #selector(EntryViewController.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(EntryViewController.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
#objc func keyboardWillShow(notification: Notification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
print("notification: Keyboard will show")
if self.view.frame.origin.y == 0{
self.view.frame.origin.y -= keyboardSize.height
}
}
}
#objc func keyboardWillHide(notification: Notification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y != 0 {
self.view.frame.origin.y += keyboardSize.height
}
}
}
This moves the whole view up I think, which means safeareas (such as navigation bar and so on) have the TableView underneath them. Do I make the navigationview non-transparent in this approach?
One solution (which I sometimes use) is simply change the content offset of the tableView when the keyboard appears/disappears. I believe this would work in your instance as opposed to varying the tableView's constraints as you mentioned your UIViewController is a UITableViewController. Please see the below code for my suggestion, hope this helps!
Handle Notifications:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(EntryViewController.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(EntryViewController.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self)
}
Actions:
#objc func keyboardWillShow(notification: Notification) {
if let keyboardHeight = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height {
print("Notification: Keyboard will show")
tableView.setBottomInset(to: keyboardHeight)
}
}
#objc func keyboardWillHide(notification: Notification) {
print("Notification: Keyboard will hide")
tableView.setBottomInset(to: 0.0)
}
Extensions:
extension UITableView {
func setBottomInset(to value: CGFloat) {
let edgeInset = UIEdgeInsets(top: 0, left: 0, bottom: value, right: 0)
self.contentInset = edgeInset
self.scrollIndicatorInsets = edgeInset
}
}
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've been trying to set up my keyboard since a few days ago, but I can't manage to do it. I just want the imageView to move up when I am writing in the bottom text field. In fact, it's working, but it also moves up when I try to write at the topTextField. I don't want that, because when that happens, I can't see the textField at the top, and I can't see what I'm writing.
I'll include my screenshots and my code.
In this image, I pressed the topTextField to write something, but as you can see, the topTextField is lost. I mean the view moves up when I press the topTextField and I don't want that. What I want is so that when I press the topTextField the keyboard should appear but the view should be at the same place.
And in the last one I pressed the textFieldBottom, and as you can see, it works. The view moves up so I can see what I'm writing inside the textFieldBottom.
Here is my code:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.subscribeToKeyboardNotifications()
self.subscribeToKeyboardNotificationsDown()
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.unsubscribeToKeyBoardNotifications()
self.unsubscribeToKeyBoardNotificationsDown()
}
func subscribeToKeyboardNotifications() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.keyboardWillShow(_:)) , name: UIKeyboardWillShowNotification, object: nil)
}
func unsubscribeToKeyBoardNotifications() {
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
}
func keyboardWillShow(notification: NSNotification) {
view.frame.origin.y -= getKeyboardHeight(notification)
}
func subscribeToKeyboardNotificationsDown() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.keyboardWillHide(_:)), name: UIKeyboardWillHideNotification, object: nil)
}
func unsubscribeToKeyBoardNotificationsDown() {
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}
func keyboardWillHide(notification: NSNotification) {
view.frame.origin.y += getKeyboardHeight(notification)
}
func getKeyboardHeight(notification:NSNotification) -> CGFloat {
let userInfo = notification.userInfo
let keyboardSize = userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue
return keyboardSize.CGRectValue().height
}
// do one thing maintain one globle varible to save current textfield which is on editing mode based on that varible u put condition to move imageview
following example code will help you.
var currentTextField: UITextField
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
currentTextField = textField
return true
}
func keyboardWillShow(notification: NSNotification) {
if currentTextField == textFieldBottom {
view.frame.origin.y -= getKeyboardHeight(notification)
}
}
When topTextField becomes first responder, you're changing view.frame.origin, this is moving your topTextField up. Instead of changing view frame, you can set constraints for bottomTextField and you can change constraints for bottomTextField programmatically in keyboardWillShow method. This way your view frame will not change and topTextField will not move up.
Hi I am using this code to move my view when a textView is selected, this is to make sure my texView is visible for when the keyboard pops up.
override func viewDidLoad() {
super.viewDidLoad() NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: self.view.window)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: self.view.window)
}
func keyboardWillHide(sender: NSNotification) {
let userInfo: [NSObject : AnyObject] = sender.userInfo!
let keyboardSize: CGSize = userInfo[UIKeyboardFrameBeginUserInfoKey]!.CGRectValue.size
self.view.frame.origin.y += keyboardSize.height
}
func keyboardWillShow(sender: NSNotification) {
let userInfo: [NSObject : AnyObject] = sender.userInfo!
let keyboardSize: CGSize = userInfo[UIKeyboardFrameBeginUserInfoKey]!.CGRectValue.size
let offset: CGSize = userInfo[UIKeyboardFrameEndUserInfoKey]!.CGRectValue.size
if keyboardSize.height == offset.height {
if self.view.frame.origin.y == 0 {
UIView.animateWithDuration(0.1, animations: { () -> Void in
self.view.frame.origin.y -= keyboardSize.height
})
}
} else {
UIView.animateWithDuration(0.1, animations: { () -> Void in
self.view.frame.origin.y += keyboardSize.height - offset.height
})
}
print(self.view.frame.origin.y)
}
override func viewWillDisappear(animated: Bool) {
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: self.view.window)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: self.view.window)
}
How can i only move the view if the bottom textView is selected? Because currently if you select the uppermost textView it moves half of it off screen.
I appreciate any help, thanks in advance.
In the notification callbacks check for bottomTextView.isFirstResponder() and move the view only if its true. Otherwise don't move the view.
Make your class a UITextViewDelegate like so:
class ViewController: UIViewController, UITextViewDelegate {
Then in viewDidLoad set only the bottom textView to be controlled by the delegate:
bottomTextView.delegate = self
Then you can use these functions, changing the values to suit your needs:
func textViewDidBeginEditing(textView: UITextView) {
self.view.frame.origin.y -= 150
}
func textViewDidEndEditing(textView: UITextView) {
self.view.frame.origin.y += 150
}
Rather than only moving the keyboard for a single text field, I suggest a more flexible approach.
What I do is to keep track of which text field is being selected and do some calculations to move the view controller's content view up just enough to expose the text field that is becoming active.
To do that you need to set up your view controller to be the text fields' delegates.
I have a development blog post that explains this in detail:
Shifting views to make room for the keyboard
The code in that post is in Objective-C, but the concepts are identical. I'm not sure I have the same code in Swift that I can share (The only code I've found is in a project I did for a client.)
That blog post references a project called RandomBlobs on Github.
I would suggest using a delegate. For example, you can set you bottom textView's delegate to self (add UITextViewDelegate first of course) and implement the
textViewDidBeginEditing(_:) method. When that's triggered, you will know that the user started editing this particular view.
I have a container UIView that contains a UICollectionView and a UIView, both of which have UITextFields in them. I used NSNotificationCenter to move the container view when the keyboard pops up.
However, it only seems to work for the UICollectionView but not the sub UIView, as shown in the screenshots:
works fine for the text fields in UICollectionView:
but doesn't work for the text field in the UIView:
EDIT: related code:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
if let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
kbHeight = keyboardSize.height
self.animateDurationView(true)
}
}
}
func keyboardWillHide(notification: NSNotification) {
self.animateDurationView(false)
}
func animateDurationView(up: Bool) {
var movement = (up ? -kbHeight : kbHeight)
UIView.animateWithDuration(0.3, animations: {
self.durationView.frame = CGRectOffset(self.durationView.frame, 0, movement)
})
}