341

I have an app that has a text field on the lower half of the view. This means that when I go to type in the text field the keyboard covers the textfield.

How would I go about moving the view upwards while typing so I can see what i'm typing and then moving it back down to its original place when the keyboard disappears?

I've looked everywhere but all the solutions appear to be in Obj-C which I can't quite convert just yet.

Any help would be greatly appreciated.

Dan Beaulieu
  • 19,406
  • 19
  • 101
  • 135
Alex Catchpole
  • 7,156
  • 6
  • 20
  • 29
  • The best way to do this is to place your content inside a _UIScrollView_, then adjust the scroll view's _contentInset_ property by the height of the keyboard when it's shown. Absolutely do not assume the keyboard height--use the value from the "keyboard will show" notification. – nielsbot Oct 02 '15 at 16:47
  • 2
    In fact, the Apple docs tell you how to do this, under "Managing the Keyboard": https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html – nielsbot Oct 02 '15 at 16:48
  • 14
    I think all answers below does not take into consideration one case: what if you have multiple textfields and some of them are located at the top of the screen? Anytime user taps that textfield, it goes up beyond the screen, I'm pretty sure the correct answer should detect whether `it is actually needed to scroll view up when keyboard appears` – theDC Aug 18 '16 at 22:06
  • This answer is able to detect whether it is actually needed to scroll the view up when the keyboard appears by checking if the textfield currently being edited occupies the same space as the keyboard: https://stackoverflow.com/a/28813720/6749410 – HirdayGupta May 29 '18 at 08:51

42 Answers42

823

Here is a solution, without handling the switch from one textField to another:

override func viewDidLoad() {
        super.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?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue() {
        self.view.frame.origin.y -= keyboardSize.height
    }            
}

func keyboardWillHide(notification: NSNotification) {
    self.view.frame.origin.y = 0
}

To solve this, replace the two functions keyboardWillShow/Hide with these:

func keyboardWillShow(notification: NSNotification) {        
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue() {
        if view.frame.origin.y == 0 {
            self.view.frame.origin.y -= keyboardSize.height
        }
    }        
}

func keyboardWillHide(notification: NSNotification) {
    if view.frame.origin.y != 0 {
        self.view.frame.origin.y = 0
    }
}

Swift 3.0:

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)    
}

@objc func keyboardWillShow(notification: NSNotification) {        
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        if self.view.frame.origin.y == 0 {
            self.view.frame.origin.y -= keyboardSize.height
        }
    }        
}

@objc func keyboardWillHide(notification: NSNotification) {
    if self.view.frame.origin.y != 0 {
        self.view.frame.origin.y = 0
    }
}
    

Swift 4.0:

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)    
}

@objc func keyboardWillShow(notification: NSNotification) {        
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        if self.view.frame.origin.y == 0 {
            self.view.frame.origin.y -= keyboardSize.height
        }
    }        
}

@objc func keyboardWillHide(notification: NSNotification) {
    if self.view.frame.origin.y != 0 {
        self.view.frame.origin.y = 0
    }
}

Swift 4.2:

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)
}

@objc func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        if self.view.frame.origin.y == 0 {
            self.view.frame.origin.y -= keyboardSize.height
        }
    }
}

@objc func keyboardWillHide(notification: NSNotification) {
    if self.view.frame.origin.y != 0 {
        self.view.frame.origin.y = 0
    }
}
pkamb
  • 33,281
  • 23
  • 160
  • 191
Boris
  • 11,373
  • 2
  • 33
  • 35
  • 64
    If the user touches another text field while the keyboard is present the view will be pushed further up which causes a black area (the size of the keyboard) - we need to fix that by having a variable that tracks if the keyboard is present or not. e.g if keyboardPresent == true then dont move the view origin etc etc – jonprasetyo Dec 16 '15 at 15:09
  • This is a great solution except in one case. I am having issues when transitioning from a regular keyboard to a number pad. I am getting a black screen. – ML. Feb 21 '16 at 04:39
  • 4
    @Matthew Lin use a boolean so the functions keyboardWillShow and hide only works one time – iluvatar_GR Mar 08 '16 at 14:42
  • Any idea why all the UI elements disappear when I use becomeFirstResponder() with this? The elements move up with the keyboard and then disappear after the animation stops. – Stephen Horvath May 02 '16 at 10:18
  • 1
    @Boris This is only a solution if the user has a perfectly simple plain keyboard. If the user unmerges the keyboard into two sections, it screws up the view. – bearacuda13 Aug 04 '16 at 16:53
  • 1
    Works great for the stock keyboard, but doesn't pick up the height of custom keyboards. – yesthisisjoe Dec 02 '16 at 23:24
  • 12
    Just one suggestion, so that you don't have to debug a lot like I did. If you have multiple uitextfields on the same screen, keyboard-size may vary (it doesn't show suggestions for some inputs based on your settings), so it is advisable to set self.view.frame.origin.y = 0 , everytime you dismiss the keyboard. For example, it would show sugeestions for your email textfield, so keyboard size would increase, and it won't show suggestions for password field, so keyboard size would decrease. – Vandan Patel Dec 21 '16 at 20:55
  • 2
    How you deal with multiple textFields? E.G if you want to do it only on one textField? – Tarvo Mäesepp Jan 28 '17 at 16:13
  • 1
    Shouldn't i remove this observers on view disappears func? – Virendra Singh Rathore Mar 30 '17 at 07:49
  • The code doesn't work well for custom keyboards. My keyboard's height does not compute properly on first run. Is there a fix for the code to take in consideration the height of custom keyboards? – ajayb Jul 18 '17 at 01:01
  • This works good but can you get it to move depending on where the textfield is if you have multiple ones instead of moving one to fit them all – Will Mays Sep 06 '17 at 13:34
  • 1
    this is buggy, it moves the view only once, better solution here: https://stackoverflow.com/a/46366394/4995771 – Masih Nov 03 '17 at 17:30
  • Why just work one time? when keyboard dismiss and for second time tap on textfield keyboard not move up. I checked it and I find out for second time keyboard.height size in KyboardWillShow method is zero(0). why??? – reza_khalafi Nov 24 '17 at 10:05
  • 40
    You need to use `UIKeyboardFrameEndUserInfoKey` rather than `UIKeyboardFrameBeginUserInfoKey` when obtaining the keyboard size. I am not sure why at the moment, but the former will yield more consistent results. – jshapy8 Nov 27 '17 at 05:46
  • 4
    Please replace `UIKeyboardFrameBeginUserInfoKey` with `UIKeyboardFrameEndUserInfoKey`. First one gives start frame of the keyboard, which comes zero sometimes, while second one gives the end frame of the keyboard. – Arnab Mar 02 '18 at 12:28
  • It's better to use UIKeyboardFrameEndUserInfoKey for new third parties keyboards – Saeed Ir Aug 30 '18 at 23:47
  • 2
    This code is again broken with notch based devices :( – jeet.chanchawat Oct 12 '18 at 07:10
  • 2
    The only modification i made was to change the code in "keyboardWillHide" where the origin is assigined to this self.view.frame.origin.y = 0 – Sarthak Mishra Oct 27 '18 at 02:01
  • 1
    this sulotion has allot of issues, ive tried it in my app, and it's doing allot of problems especialy once u move between one textfield to another – Chief Madog Nov 06 '18 at 12:56
  • 1
    I had to use `UIResponder.keyboardFrameEndUserInfoKey` instead of `UIKeyboardFrameBeginUserInfoKey` as it was not recognized in Swift 4.2 – YoanGJ Nov 10 '18 at 13:50
  • On Notch based devices (iPhone X, XS, XS Max, XR), this code will not work. I would highly suggest using @SarthakMishra's advice and setting self.view.frame.origin.y = 0. This then matches the check in the method above – Matthew Knippen Dec 08 '18 at 20:37
  • I tried this exact solution and found that 6 memory leaks result when the user taps to view the keyboard **for the first time** after launching app. Memory is **not** leaked thereafter. I get 6 leaks, and the backtrace mentions UIKeyboardPredictonView as one of the leaked objections. I found this using Apple's Profiler, and I observe the 6 leaks using both UIKeyboardFrameBeginUserInfoKey and UIKeyboardFrameEndUserInfoKey. I also did not use a flag as @johnprasetyo suggests. – Joshua Wolff Jan 02 '19 at 22:57
  • 2
    Why does this have to be so hard? All I want to do is shift my app up when the keyboard appears. There are a million different answers on stackoverflow and all of them seem to have edge cases that don't work correctly. – ChrisO Mar 06 '19 at 11:40
  • 2
    What if we also have some text fields in the upper half of the view I know this solution works only if textfields are in the lower half of the view I wonder what if we have a list of text fields from up to bottom in a view – Samira Mar 18 '19 at 20:27
  • 1
    THIS WILL NOT WORK. What if the user uses split mode or undock? What about the free floating keyboard comming out with ios 13? Don't do it. – Warpzit Jun 11 '19 at 11:56
  • The proper solution, Thanks!! Is there any specific way to catch each tap on text fields and push up the screen in different sizes? – KZoNE May 21 '20 at 09:24
  • Is there an update for swift 5, or will the code under Swift 4.2 work properly? – T.E. Dec 14 '20 at 20:15
  • In my case initial self.view.frame.origin.y wasn't 0. – NDQuattro May 26 '21 at 17:25
  • **Top safe area.** Don't forget to add this or remove each time. Each device has it's own. – J A S K I E R Jun 06 '21 at 21:45
112

Easiest way that doesn't even require any code:

  1. Download KeyboardLayoutConstraint.swift and add (drag & drop) the file into your project, if you're not using the Spring animation framework already.
  2. In your storyboard, create a bottom constraint for the View or Textfield, select the constraint (double-click it) and in the Identity Inspector, change its class from NSLayoutConstraint to KeyboardLayoutConstraint.
  3. Done!

The object will auto-move up with the keyboard, in sync.

gammachill
  • 1,466
  • 1
  • 13
  • 14
  • 4
    To select the bottom constraint you can also go to Size Inspector, then double-click on the constraint in the list - http://www.raywenderlich.com/wp-content/uploads/2015/09/043_constraints-232x500.png – gammachill May 07 '16 at 09:46
  • 3
    This worked perfect for me. it's literally a 2 step process. 1. Add the KeyboardLayoutConstraint.swift, 2. In storyboard, create a bottom constrain for the view or text field. NOTE: I deleted my constraints and added just 1 constraint to bottom of the view or textfield and change its class from NSLayoutConstraint to KeyboardLayoutConstraint. Then any views/textfields etc. above I just connected constraints from that item to the item with a single KeyboardLayoutConstraint and the result was all items in view moved UP/DOWN when Key Board Appears/Disappears – Brian Bird Dec 29 '16 at 18:58
  • 4
    This is the best solution, the provided code does not hardcode any values such as the length or curve of the animation, or the size of the keyboard. It is also easy to understand. – miguelSantirso Sep 25 '17 at 14:17
  • 6
    This is working for me, but I get an extra 50px of space between the top of the keyboard and the bottom of my scrollView. I'm wondering if it's due to the Safe Area bottom constraint I'm using. Anyone run into this? – Clifton Labrum Jul 09 '18 at 20:00
  • If you have centered views, set priority 750 in Y axis and than add a KeyboardLayoutConstraint subclassed bottom constraint. – Ely Dantas Dec 11 '18 at 00:31
  • 1
    This was an awesome answer. Very cool design as well. One suggestion: If your textviews/textfields are on table view cells you might notice the views that have this constraint will jump awkwardly every time the user clicks enter and goes to the next text field. You can wrap the animations in `DispatchQueue.main.async {}` to fix it. Good work! Thumbs up! – ScottyBlades Jan 01 '19 at 03:46
  • Awesome answer, but I have a special case where I have a table view with a cell containing the text area, so it's pushing the table view upward but not the whole cell – btrballin Jun 02 '19 at 07:20
  • @CliftonLabrum I've got the same problem and because of this one of my buttons has its height decreased automatically (decribed [here](https://stackoverflow.com/q/56833750/2016165)). :/ Did you manage to find a solution for this? – Neph Jul 01 '19 at 10:22
  • I have found one issue the observer doesnt get removed, the deinit in the class never gets called... i tried to add the class programatically – anoop4real Sep 24 '19 at 08:53
  • This was the best answer. None of the other solutions worked for me except this one! – Ethan Brimhall May 30 '20 at 02:43
  • @CliftonLabrum replace updateContraint function with ```swift func updateConstant() { let safeAreaBottom = UIApplication.appDelegate.window?.safeAreaInsets.bottom ?? 0 let height: CGFloat if keyboardVisibleHeight > 0 { height = keyboardVisibleHeight - safeAreaBottom } else { height = 0 } self.constant = offset + height } ``` also, I have convenience accessor for app delegate which gave my window if you are on ios 13 make sure you get the window object from you scenedelegate – Amit Samant Jan 05 '21 at 06:50
  • Note that for this to work properly, the **first item** of the constraint has to be the reference point in the view (e.g. Superview.Bottom) and the **second item** has to be the bottom of the view or textfield or whatever. If you have it the other way around then the view/textfield moves down instead of up when the keyboard appears. – Kian Dec 05 '21 at 01:34
72

One of the popular answers on this thread uses the following code:

func keyboardWillShow(sender: NSNotification) {
    self.view.frame.origin.y -= 150
}
func keyboardWillHide(sender: NSNotification) {
    self.view.frame.origin.y += 150
}

There's an obvious problem with offsetting your view by a static amount. It'll look nice on one device but will look bad on any other size configuration. You'll need to get the keyboards height and use that as your offset value.

Here's a solution that works on all devices and handles the edge-case where the user hides the predictive text field while typing.

Solution

Important to note below, we're passing self.view.window in as our object parameter. This will provide us with data from our Keyboard, such as its height!

@IBOutlet weak var messageField: UITextField!

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
}

We'll make it look nice on all devices and handle the case where the user adds or removes the predictive text field.

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 {
        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
        })
    }
}

Remove Observers

Don't forget to remove your observers before you leave the view to prevent unnecessary messages from being transmitted.

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)
}

Update based on question from comments:

If you have two or more text-fields, you can check to see if your view.frame.origin.y is at zero.

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)
}
Dan Beaulieu
  • 19,406
  • 19
  • 101
  • 135
  • 1
    when dealing with multiple text fields, the view keeps moving up and does not come back down – Mugunthan Balakrishnan Oct 13 '15 at 09:07
  • You're going to have to change your conditions to account for the text fields – Dan Beaulieu Oct 13 '15 at 11:05
  • thanks for the response, i found the answer i was looking for at this thread on stack overflow http://stackoverflow.com/questions/1126726/how-to-make-a-uitextfield-move-up-when-keyboard-is-present/26561723#26561723 – Mugunthan Balakrishnan Oct 14 '15 at 05:43
  • @MugunthanBalakrishnan thanks for bringing this up, I've added a solution. – Dan Beaulieu Oct 17 '15 at 22:14
  • Hi guys, there is a bug. The observers are not removed from the view after being called in viewWillDisappear. Replace this line "NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: self.view.window)" with "NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)" then observer is removed – rudald Mar 20 '18 at 00:57
  • if anyone having trouble with the frame going down when changing textFields, you may disable autocorrection ! – Achraf Sep 17 '18 at 18:33
49

Not an advertisement or promotion or spam, just a good solution. I know that this question has nearly 30 answers and I'm so shocked that no one even mentioned once about this beautiful GitHub project that does it all for you and even better. All the answers just move the view upwards. I just solved all my problems with this IQKeyboardManager. It has 13000+ stars.
Just add this in your podfile if you are using swift

pod 'IQKeyboardManagerSwift'

and then inside your AppDelegate.swift do import IQKeyboardManagerSwift

import IQKeyboardManagerSwift

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

      IQKeyboardManager.shared.enable = true // just add this line

      return true
    }
}

Add the line IQKeyboardManager.shared.enable = true to enable it
This solution is a must if you are going for production.

weegee
  • 3,256
  • 2
  • 18
  • 32
  • This is really good, but the latest version doesn't work for me, I have used 6.2.1, and import as `import IQKeyboardManager` and used `IQKeyboardManager.shared().isEnabled = true` in AppDelegate – Dhanu K Aug 01 '19 at 11:15
  • 5
    And this works awesomely when using multiple edit texts, This saved my time – Dhanu K Aug 01 '19 at 11:16
  • 4
    Can't thank enough for pointing out to this wonderful library. This library is finally THE FINAL ANSWER to all keyboard related nonsense which Apple has never provided a solution for. Now I'll be using it for all my projects, new and old, and save time and headache which this keyboard appearing, disappearing or not disappearing, or how to hide it, and why it is overlapping, issues have been causing me since the day I am programming for iPhones. – zeeshan Aug 04 '19 at 20:47
  • 1
    @DhanuK, Just found this library and works perfectly and it's effortless. The app delegate code has been updated to IQKeyboardManager.shared.enable = true – adougies May 19 '20 at 21:59
  • 4
    This answer needs to be higher, much higher. – J.C Jun 23 '20 at 16:31
  • I am already using this but when the keyboard collapse the view doest move back to its original position... `IQKeyboardManager.shared.layoutIfNeededOnUpdate = true` adding this line also, resolve my issue ... – Wahab Khan Jadon Jul 04 '21 at 09:59
  • This should be the accepted answer! It's the best one at all!! – oskarko Dec 06 '21 at 14:38
  • this is pure genius – Mohamad Bachir Sidani Dec 07 '21 at 22:52
31

I improved one of the answers a bit to make it work with different keyboards & different textviews/fields on one page:

Add observers:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange(notification:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)

    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}

func keyboardWillHide() {
    self.view.frame.origin.y = 0
}

func keyboardWillChange(notification: NSNotification) {

    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        if YOURTEXTVIEW.isFirstResponder {
            self.view.frame.origin.y = -keyboardSize.height
        }
    }
}

Remove observers:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
}
anasaitali
  • 1,504
  • 21
  • 30
scipianne
  • 310
  • 4
  • 6
  • 3
    this solution works better than accepted answer. Accepted answer shows the keyboard only once which to me is a bug :) – Masih Nov 03 '17 at 17:28
  • This works for Xcode 10.1 and iOS 12. The accepted answer is not valid any longer. – zeeshan Nov 15 '18 at 17:04
  • This is an excellent answer, the only thing I would add is keeping track of the bottom safe area in the newer devices (X, XS, etc) so that it accounts for that. – Munib Feb 16 '19 at 17:33
  • @Munib See https://stackoverflow.com/a/54993623/1485230 Other issues include the view not animating, and keyboard height change not being followed. – Antzi Mar 05 '19 at 00:28
  • what if my textField is top of the view..? i mean if there is textfield with origin Y = 0..?? then textField is going up and i cant see it – Saifan Nadaf Jun 27 '19 at 11:36
  • Work on XCode 12.2 and Swift 5, nice! – xhinoda Mar 01 '21 at 00:52
29

Add this to your viewcontroller. Works like a charm. Just adjust the values.

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(sender: NSNotification) {
    self.view.frame.origin.y -= 150
}
@objc func keyboardWillHide(sender: NSNotification) {
    self.view.frame.origin.y += 150
}
Emre Ozdil
  • 401
  • 6
  • 12
user3677173
  • 2,559
  • 2
  • 17
  • 16
  • This works for me. However it is a little jerky. How can I get this to move up smoothley? also is there a way to only apply it to one of the textfields as it currently does this for all. :( – DannieCoderBoi Feb 06 '15 at 01:56
  • You can use UIView.animateWithDuration(1.0, animations: { self.view.frame.origin.y -= 150 }, completion: nil) – user3677173 Feb 06 '15 at 19:44
  • 3
    It might not work with "Auto-Layout" so consider deactivating it if so. – Teodor Ciuraru Mar 03 '15 at 22:40
  • 1
    It causes some funky behavior with autolayout @Josh, you are mistaken – Mike Jun 06 '15 at 20:22
  • If you are using auto layout, make sure to call `self.view.setTranslatesAutoresizingMaskIntoConstraints(true)` before you move in up and `self.view.setTranslatesAutoresizingMaskIntoConstraints(false)` before you move it back – Mike Jun 06 '15 at 21:12
  • @Mike It works here and I'm happy, obviously, I am not mistaken. Funky things are the daily water and bread of xCode. I am no iOS expert, so I cannot debate more than what my eyes see. For the record, I am using Xcode 6.3 and autolayout. and building to iOS 8.3 – Josh Jun 08 '15 at 08:49
  • Its not working for me. The screen goes black after moving from one UITextField to other. – V_J Jun 17 '15 at 07:06
  • It depends on a layout. Constraints can mess up with frames. – user3677173 Jun 18 '15 at 07:14
  • 9
    Don't do this! You cannot assume the keyboard is a certain size. – nielsbot Oct 02 '15 at 16:39
  • 2
    Should use keyboardSize. What happens when you have accessory views and different keyboard heights on devices? Detached keyboard? – xtrinch Jul 01 '16 at 22:53
27

Swift 5.0:

After 4-5 hours of fight i came with a simple extension of UIViewController with simple code which works like charm

*View should not move when TextField is above the keyboard

*No need to set constant value to NSLayoutConstraint

*No third party library required

*No animation code required

*Works on tableview as well

*This works on Auto layout / auto resize

extension UIViewController {
    func addKeyboardObserver() {
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardNotifications(notification:)),
                                               name: UIResponder.keyboardWillChangeFrameNotification,
                                               object: nil)
    }

    func removeKeyboardObserver(){
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
    }

    // This method will notify when keyboard appears/ dissapears
    @objc func keyboardNotifications(notification: NSNotification) {

        var txtFieldY : CGFloat = 0.0  //Using this we will calculate the selected textFields Y Position
        let spaceBetweenTxtFieldAndKeyboard : CGFloat = 5.0 //Specify the space between textfield and keyboard


        var frame = CGRect(x: 0, y: 0, width: 0, height: 0)
        if let activeTextField = UIResponder.currentFirst() as? UITextField ?? UIResponder.currentFirst() as? UITextView {
            // Here we will get accurate frame of textField which is selected if there are multiple textfields
            frame = self.view.convert(activeTextField.frame, from:activeTextField.superview)
            txtFieldY = frame.origin.y + frame.size.height
        }

        if let userInfo = notification.userInfo {
            // here we will get frame of keyBoard (i.e. x, y, width, height)
            let keyBoardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
            let keyBoardFrameY = keyBoardFrame!.origin.y
            let keyBoardFrameHeight = keyBoardFrame!.size.height

            var viewOriginY: CGFloat = 0.0
            //Check keyboards Y position and according to that move view up and down
            if keyBoardFrameY >= UIScreen.main.bounds.size.height {
                viewOriginY = 0.0
            } else {
                // if textfields y is greater than keyboards y then only move View to up
                if txtFieldY >= keyBoardFrameY {

                    viewOriginY = (txtFieldY - keyBoardFrameY) + spaceBetweenTxtFieldAndKeyboard

                    //This condition is just to check viewOriginY should not be greator than keyboard height
                    // if its more than keyboard height then there will be black space on the top of keyboard.
                    if viewOriginY > keyBoardFrameHeight { viewOriginY = keyBoardFrameHeight }
                }
            }

            //set the Y position of view
            self.view.frame.origin.y = -viewOriginY
        }
    }
}

Add this Extension Of UIResponder to get which TextField is selected

extension UIResponder {

    static weak var responder: UIResponder?

    static func currentFirst() -> UIResponder? {
        responder = nil
        UIApplication.shared.sendAction(#selector(trap), to: nil, from: nil, for: nil)
        return responder
    }

    @objc private func trap() {
        UIResponder.responder = self
    }
}

Then use of this in your any ViewController

   override func viewWillAppear(_ animated: Bool) {
        self.addKeyboardObserver()
    }

    override func viewWillDisappear(_ animated: Bool) {
        self.removeKeyboardObserver()
    }

  • Register this Notification in func viewWillAppear(_ animated: Bool)

  • Deregister this Notification in func viewWillDisappear(_ animated:Bool)

    Download Demo Here

Saifan Nadaf
  • 1,715
  • 1
  • 17
  • 28
  • This looked like the best solution, however there are a couple of bugs. 1, the textfield moves up but when I start typing in it it jumps up a little more. 2, In landscape when typing the textField sometimes jumps to the left. – Darren Jan 16 '20 at 16:36
  • @Darren i am trying to figure out these bugs but i haven't found, can you please tell where you got these bugs i mean for which version/device...?? – Saifan Nadaf May 20 '20 at 10:42
  • Works really great, but when I use it in a UIView that is contained in a UIViewController nested in UITabBarController the UIView moves up and never returns down. Maybe i'm missing something. Any comment or help is welcome. – Yisus Oct 13 '21 at 05:19
  • @Yisus can you please share a demo, so that i can check easily ? – Saifan Nadaf Oct 13 '21 at 09:39
  • @SaifanNadaf after a few days I solved my problem. I think that the problem comes from a weird implementation of NavigationBar in my legacy project. I set the 'isTranslucent' property to false. It was moving up my view away from my bottom. Thanks any way. – Yisus Oct 18 '21 at 21:25
  • @SaifanNadaf, If I want to add toolbar above textfield/textview then how can I achieve that in your solution ? – Birju Dec 20 '22 at 05:06
  • @Birju you can reduce `keyBoardFrameY` with your toolBar height and add your toolBar – Saifan Nadaf Dec 20 '22 at 07:03
  • @SaifanNadaf i am not talking about frame and position. I am asking you about how can i add toolbar above keyboard. Because in your solution activetextfield has read only property for inputAccessoryView. So, in that case we can't add toolbar as a input in activetextfield. – Birju Dec 20 '22 at 10:42
14

I see all answers are moving the view itself by the value of the keyboard height. Well, I have an elaborate answer, which could be useful if you are using constraints i.e autolayout, that moves a view by changing its constraint value (bottom or top constraints for example) by a predefined value or you can use keyboard size value.

In this example, I use bottom constraint from the textfield to Bottom Layout View with initial value of 175.

@IBOutlet weak var bottomConstraint: NSLayoutConstraint!

override func viewDidLoad() {
    super.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) {
    //To retrieve keyboard size, uncomment following line
    //let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue()
    bottomConstraint.constant = 260
    UIView.animateWithDuration(0.3) {
        self.view.layoutIfNeeded()
    }
}

func keyboardWillHide(notification: NSNotification) {
    //To retrieve keyboard size, uncomment following line
    //let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue()
    bottomConstraint.constant = 175
    UIView.animateWithDuration(0.3) {
        self.view.layoutIfNeeded()
    }
}
Amr Hossam
  • 2,313
  • 1
  • 22
  • 23
  • Hi sir, could you please tell me why this doesn't work when placed in a view that also contains a TableView? It works fine in the same scenario when it contains a CollectionView. – elarcoiris Oct 18 '18 at 12:57
13

For Black Screen Error ( Swift 4 & 4.2 ) .

I fixed the black screen problem. In the verified solution The keyboard height changes after tapping and this is causing black screen.

Have to use UIKeyboardFrameEndUserInfoKey instead of UIKeyboardFrameBeginUserInfoKey

var isKeyboardAppear = false

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 !isKeyboardAppear {
        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
            if self.view.frame.origin.y == 0{
                self.view.frame.origin.y -= keyboardSize.height
            }
        }
        isKeyboardAppear = true
    }
}

@objc func keyboardWillHide(notification: NSNotification) {
    if isKeyboardAppear {
        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
            if self.view.frame.origin.y != 0{
                self.view.frame.origin.y += keyboardSize.height
            }
        }
         isKeyboardAppear = false
    }
}
Ali Ihsan URAL
  • 1,894
  • 1
  • 20
  • 43
  • Won't work if there is a tabbar. You have to calculate tabbar height, otherwise there will be a black screen gap between keyboard and view. – Ankur Lahiry Sep 03 '18 at 06:09
  • 2
    This does not fix the black area where the keyboard was on the iPhone X and newer. And every time the keyboard appears and disappears the main view keeps sliding down. – Edison Mar 16 '19 at 21:29
9

There have been som changes to how we define the KeyboardWillHideNotification.

This solution works with Swift 4.2:

NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)


@objc func keyboardWillShow(_ notification:Notification) {
    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        self.view.frame.origin.y -= keyboardSize.height
    }
}

@objc func keyboardWillHide(_ notification:Notification) {
    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        self.view.frame.origin.y += keyboardSize.height
    }
}
Ivan Le Hjelmeland
  • 1,065
  • 11
  • 26
  • 3
    what if my textField is top of the view..? i mean if there is textfield with origin Y = 0..?? then textField is going up and i cant see it – Saifan Nadaf Jun 27 '19 at 11:34
8

So none of the other answers seems to get it right.

The Good Behaviored Keyboard on iOS should:

  • Resize automatically when the keyboard change sizes (YES IT CAN)
  • Animate at the same speed as the keyboard
  • Animate using the same curve as the keyboard
  • Respect safe areas if relevant.
  • Works on iPad/Undocked mode too

My code use a NSLayoutConstraint declared as an @IBOutlet

@IBOutlet private var bottomLayoutConstraint: NSLayoutConstraint!

You could also use transforms, view offsets, .... I think it's easier with the constraint tho. It works by setting a constraint to the bottom, you might need to alter the code if your constant is not 0/Not to the bottom.

Here is the code:

// In ViewDidLoad
    NotificationCenter.default.addObserver(self, selector: #selector(?MyViewController.keyboardDidChange), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)


@objc func keyboardDidChange(notification: Notification) {
    let userInfo = notification.userInfo! as [AnyHashable: Any]
    let endFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
    let animationDuration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber
    let animationCurve = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as! NSNumber
    bottomLayoutConstraint.constant = view.frame.height - endFrame.origin.y - view.safeAreaInsets.bottom // If your constraint is not defined as a safeArea constraint you might want to skip the last part.
    // Prevents iPad undocked keyboard.
    guard endFrame.height != 0, view.frame.height == endFrame.height + endFrame.origin.y else {
        bottomLayoutConstraint.constant = 0
        return
    }
    UIView.setAnimationCurve(UIView.AnimationCurve(rawValue: animationCurve.intValue)!)
    UIView.animate(withDuration: animationDuration.doubleValue) {
        self.view.layoutIfNeeded()
        // Do additional tasks such as scrolling in a UICollectionView
    }
}
Antzi
  • 12,831
  • 7
  • 48
  • 74
  • best answer for this problem of iOS. i've been trying to find something like this for 15mins and here it is! – Mr. T. May 16 '22 at 14:13
7

The validated answer doesn't take in account the textfield position and has some bug (double displacement, never come back the primary position, displacement even if the texfield is on top of the view...)

The idea is :

  • to get the focus TextField absolute Y position
  • to get the keyboard height
  • to get the ScreenHeight
  • Then calculate the distance between keyboard position and textfield (if < 0 -> move up the view)
  • to use UIView.transform instead of UIView.frame.origin.y -= .., cause it's easier to come back to original position with UIView.transform = .identity

then we will be able to move the view only if necessary and of the specific displacement in oder to have the focused texField just over the keyboard

Here is the code :

Swift 4

class ViewController: UIViewController, UITextFieldDelegate {

var textFieldRealYPosition: CGFloat = 0.0

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.addObserver(self, selector: #selector(VehiculeViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(VehiculeViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)

  // Delegate all textfields

}


@objc func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        let distanceBetweenTextfielAndKeyboard = self.view.frame.height - textFieldRealYPosition - keyboardSize.height
        if distanceBetweenTextfielAndKeyboard < 0 {
            UIView.animate(withDuration: 0.4) {
                self.view.transform = CGAffineTransform(translationX: 0.0, y: distanceBetweenTextfielAndKeyboard)
            }
        }
    }
}


@objc func keyboardWillHide(notification: NSNotification) {
    UIView.animate(withDuration: 0.4) {
        self.view.transform = .identity
    }
}


func textFieldDidBeginEditing(_ textField: UITextField) {
  textFieldRealYPosition = textField.frame.origin.y + textField.frame.height
  //take in account all superviews from textfield and potential contentOffset if you are using tableview to calculate the real position
}

}

Quentin N
  • 641
  • 7
  • 5
  • Very good! (In viewDidLoad you have "VehiculeViewController" instead of just "ViewController"). – rene Nov 06 '18 at 14:17
  • A much more complete and useful answer. Thank you! I suggest that the keyboard check is called as follows because it will give a consistent size of the keyboard.........if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size – Ben May 21 '19 at 23:28
  • A far better, elegant and practical solution of all. – Kozmotronik Apr 19 '23 at 14:18
6

I noticed that the other answers involved cutting some of the top from the view. If you want to simply resize the view without cutting any content, just try this method :)

func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        self.view.setTranslatesAutoresizingMaskIntoConstraints(true)
        self.view.frame = CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.height - keyboardSize.height)
    }
}

func keyboardWillHide(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        self.collectionView.setTranslatesAutoresizingMaskIntoConstraints(false)
        self.view.frame = CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.height + keyboardSize.height)
    }
}
Grant Park
  • 1,004
  • 1
  • 9
  • 29
6

For Swift 3, I made a UIViewController subclass since I needed constant behavior in all View Controllers.

class SomeClassVC: UIViewController {

  //MARK: - Lifecycle
  override func viewDidLoad() {
    super.viewDidLoad()

    addKeyboardObservers()
  }

  override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    removeKeyboardObservers()
  }

  //MARK: - Overrides
  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    view.endEditing(true)
  }

  //MARK: - Help
  func addKeyboardObservers() {
    NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
  }

  func removeKeyboardObservers() {
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: self.view.window)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: self.view.window)
  }

  func keyboardWillShow(notification: NSNotification) {
    let keyboardHeight = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.height
    UIView.animate(withDuration: 0.1, animations: { () -> Void in
      self.view.window?.frame.origin.y = -1 * keyboardHeight!
      self.view.layoutIfNeeded()
    })
  }

  func keyboardWillHide(notification: NSNotification) {
    UIView.animate(withDuration: 0.1, animations: { () -> Void in
      self.view.window?.frame.origin.y = 0
      self.view.layoutIfNeeded()
    })
  }

  func resignTextFieldFirstResponders() {
    for textField in self.view.subviews where textField is UITextField {
      textField.resignFirstResponder()
    }
  }

  func resignAllFirstResponders() {
      view.endEditing(true)
  }
}
Pavle Mijatovic
  • 773
  • 10
  • 6
  • Inspired by Pavle's solution, I upgraded it to automatically raise the keyboard by a certain percentage of the remaining available space and also find the focused field recursively for proper layout. Grab it here: https://gist.github.com/noordawod/24d32b2ce8363627ea73d7e5991009a0 – Noor Dawod Aug 09 '17 at 08:45
  • My tab bar is also moving up with window! :( – Ahmadreza Dec 21 '17 at 15:29
5

My two cents for beginners: in above samples someone changes coordinates, other uses "autoresizing mask", and other constraints:

As Apple says, do not mix these 3 types of logic. If You have constraints in Storyboard, do not try to change x/y. It definitively not work.

ingconti
  • 10,876
  • 3
  • 61
  • 48
4

its 100% Perfect Answer For all Guy's Update Tableview Height when open Keyboard

For Swift4.2

   override func viewDidLoad() {
      super.viewDidLoad()
      NotificationCenter.default.addObserver(self, selector: #selector(RecipeVC.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)

      NotificationCenter.default.addObserver(self, selector: #selector(RecipeVC.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
   }

   @objc func keyboardWillShow(notification: NSNotification) {
    if ((notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue) != nil {

        var userInfo = notification.userInfo!
        var keyboardFrame:CGRect = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
        keyboardFrame = self.view.convert(keyboardFrame, from: nil)

        var contentInset:UIEdgeInsets = self.tbl.contentInset
          contentInset.bottom = keyboardFrame.size.height
          self.tbl.contentInset = contentInset
    }
}

   @objc func keyboardWillHide(notification: NSNotification) {
    if ((notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue) != nil {
        let contentInset:UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
        self.tbl.contentInset = contentInset
    }
}

Swift3.2

    override func viewDidLoad() {
          super.viewDidLoad()

           NotificationCenter.default.addObserver(self, selector: #selector(RecipeVC.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)

           NotificationCenter.default.addObserver(self, selector: #selector(RecipeVC.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
    }
    func keyboardWillShow(notification: NSNotification) {
         if ((notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue) != nil {
         //self.view.frame.origin.y -= keyboardSize.height
         var userInfo = notification.userInfo!
         var keyboardFrame:CGRect = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
          keyboardFrame = self.view.convert(keyboardFrame, from: nil)

          var contentInset:UIEdgeInsets = self.tbl.contentInset
          contentInset.bottom = keyboardFrame.size.height
          self.tbl.contentInset = contentInset

       }
    }

    func keyboardWillHide(notification: NSNotification) {
         if ((notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue) != nil {
         let contentInset:UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
         self.tbl.contentInset = contentInset
         }
    }
Maulik Patel
  • 2,045
  • 17
  • 24
  • This is the best answer. tbl should be tableView and I added some padding: contentInset.bottom = keyboardFrame.size.height + 10 – iphaaw Aug 29 '19 at 11:10
  • Amazing answer! Works perfect for me with textView! Thank you! – Dian Aug 24 '22 at 19:23
4

Similar to @Boris answer, but in Swift 5:

override func 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)
}

@IBAction func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        if self.view.frame.origin.y == 0 {
            self.view.frame.origin.y -= keyboardSize.height
        }
    }
}

@IBAction func keyboardWillHide(notification: NSNotification) {
    if self.view.frame.origin.y != 0 {
        self.view.frame.origin.y = 0
    }
}
Jerry Chong
  • 7,954
  • 4
  • 45
  • 40
  • Don't use .keyboardFrameBeginUserInfoKey; it can give two different heights depending on the settings. It is better to use keyboardFrameEndUserInfoKey, which delivers the final height. – Thel Jun 23 '23 at 11:18
3

For Swift 3

func textFieldDidBeginEditing(_ textField: UITextField) { // became first responder

    //move textfields up
    let myScreenRect: CGRect = UIScreen.main.bounds
    let keyboardHeight : CGFloat = 216

    UIView.beginAnimations( "animateView", context: nil)
    var movementDuration:TimeInterval = 0.35
    var needToMove: CGFloat = 0

    var frame : CGRect = self.view.frame
    if (textField.frame.origin.y + textField.frame.size.height + UIApplication.shared.statusBarFrame.size.height > (myScreenRect.size.height - keyboardHeight - 30)) {
        needToMove = (textField.frame.origin.y + textField.frame.size.height + UIApplication.shared.statusBarFrame.size.height) - (myScreenRect.size.height - keyboardHeight - 30);
    }

    frame.origin.y = -needToMove
    self.view.frame = frame
    UIView.commitAnimations()
}

func textFieldDidEndEditing(_ textField: UITextField) {
    //move textfields back down
    UIView.beginAnimations( "animateView", context: nil)
    var movementDuration:TimeInterval = 0.35
    var frame : CGRect = self.view.frame
    frame.origin.y = 0
    self.view.frame = frame
    UIView.commitAnimations()
}
Celil Bozkurt
  • 1,693
  • 16
  • 18
2

Swift 4:

I was having an issue with the most accepted answer, in which hiding the keyboard did not return the view all the way to the bottom of the page (only partially). This worked for me (+updated for Swift 4).

override func viewDidLoad() {
    super.viewDidLoad()
    self.hideKeyboardWhenTappedAround()
    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 {
        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 = 0
        }
    }
}
Rbar
  • 3,740
  • 9
  • 39
  • 69
  • what if my textField is top of the view..? i mean if there is textfield with origin Y = 0..?? then textField is going up and i cant see it – Saifan Nadaf Jun 27 '19 at 11:31
1

Updated for Swift 3...

As others have said, you need to add notification observers in your controller's viewDidLoad() method, like so:

NotificationCenter.default.addObserver(forName: .UIKeyboardWillShow, object: nil, queue: nil)
    { notification in
    self.keyboardWillShow(notification)
    }

NotificationCenter.default.addObserver(forName: .UIKeyboardWillHide, object: nil, queue: nil)
    { notification in
    self.keyboardWillHide(notification)
    }

NotificationCenter.default.addObserver(forName: .UIKeyboardDidShow, object: nil, queue: nil)
    { _ in
    self.enableUserInteraction()
    }

NotificationCenter.default.addObserver(forName: .UIKeyboardDidHide, object: nil, queue: nil)
    { _ in
    self.enableUserInteraction()
    }

Remember to remove your observers where appropriate (I do it in the viewWillDisappear() method)

NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardDidShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardDidHide, object: nil)

Then, implement your show and hide methods - notice the line that tells the app to ignore interaction events (beginIgnoringInteractionEvents). This is important since without it, the user could tap on a field or even a scrollview and cause the shift to occur a second time, resulting in a terrible UI glitch. Ignoring interaction events prior to the keyboard showing and hiding will prevent this:

func keyboardWillShow(notification: Notification)
    {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
        {
        UIApplication.shared.beginIgnoringInteractionEvents()
        self.view.frame.origin.y -= keyboardSize.height
        // add this line if you are shifting a scrollView, as in a chat application
        self.timelineCollectionView.contentInset.top += keyboardSize.height
        }
    }

func keyboardWillHide(notification: Notification)
    {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
        {
        UIApplication.shared.beginIgnoringInteractionEvents()
        self.view.frame.origin.y += keyboardSize.height
        // add this line if you are shifting a scrollView, as in a chat application
        self.timelineCollectionView.contentInset.top -= keyboardSize.height
        }
    }

Lastly, re-enable user interactions (remember, this method fires after the keyboard didShow or didHide):

func enableUserInteraction()
    {
    UIApplication.shared.endIgnoringInteractionEvents()
    }
Gene Loparco
  • 2,157
  • 23
  • 23
1

Here is my solution (actually this code is for the case when you have few textfields in your view, this works also for the case when you have one textfield)

class MyViewController: UIViewController, UITextFieldDelegate {

@IBOutlet weak var firstTextField: UITextField!
@IBOutlet weak var secondTextField: UITextField!

var activeTextField: UITextField!
var viewWasMoved: Bool = false


override func viewDidLoad() {
    super.viewDidLoad()
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(PrintViewController.keyboardWillShow(_:)), name: UIKeyboardWillShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(PrintViewController.keyboardWillHide(_:)), name: UIKeyboardWillHideNotification, object: nil)
}

override func viewDidDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

func textFieldDidBeginEditing(textField: UITextField) {
    self.activeTextField = textField
}

func textFieldDidEndEditing(textField: UITextField) {
    self.activeTextField = nil
}

func textFieldShouldReturn(textField: UITextField) -> Bool {
    textField.resignFirstResponder()
    return true
}


func keyboardWillShow(notification: NSNotification) {

    let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue()

    var aRect: CGRect = self.view.frame
    aRect.size.height -= keyboardSize!.height

    let activeTextFieldRect: CGRect? = activeTextField?.frame
    let activeTextFieldOrigin: CGPoint? = activeTextFieldRect?.origin

    if (!CGRectContainsPoint(aRect, activeTextFieldOrigin!)) {
        self.viewWasMoved = true
        self.view.frame.origin.y -= keyboardSize!.height
    } else {
        self.viewWasMoved = false
    }
}

func keyboardWillHide(notification: NSNotification) {
    if (self.viewWasMoved) {
        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
            self.view.frame.origin.y += keyboardSize.height
        }
    }
}
Vah.Sah
  • 522
  • 1
  • 6
  • 21
1

If you have 2 or more text fields on the same VC, and the user taps on one of them and then taps on the other one, without calling the function keyboardWillHide, the view is going upwards one more time, which is not necessary, because you'll have the keyboard, a blank space which has the height of the keyboard, and then the view, using the code in the answer I edited:

override func viewDidLoad() {       
    super.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
    }
}

To solve this, replace the two functions "KeyboardWillShow/Hide" to these:

func keyboardWillShow(notification: NSNotification) {
 if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
    if 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 view.frame.origin.y != 0 {
            self.view.frame.origin.y += keyboardSize.height
        }
    }
}
Chris Stillwell
  • 10,266
  • 10
  • 67
  • 77
Mr. Xcoder
  • 4,719
  • 5
  • 26
  • 44
  • what if my textField is top of the view..? i mean if there is textfield with origin Y = 0..?? then textField is going up and i cant see it – Saifan Nadaf Jun 27 '19 at 11:30
1

Swift 3 code

var activeField: UITextField?

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.addObserver(self, selector: #selector(ProfileViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(ProfileViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}

func textFieldDidBeginEditing(_ textField: UITextField){
    activeField = textField
}

func textFieldDidEndEditing(_ textField: UITextField){
    activeField = nil
}

func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        if (self.activeField?.frame.origin.y)! >= keyboardSize.height {
            self.view.frame.origin.y = keyboardSize.height - (self.activeField?.frame.origin.y)!
        } else {
            self.view.frame.origin.y = 0
        }
    }
}

func keyboardWillHide(notification: NSNotification) {
    self.view.frame.origin.y = 0
}
FelixSFD
  • 6,052
  • 10
  • 43
  • 117
1

@Boris's solution is VERY good but the view can sometimes be corrupted.

For the perfect alignment, use the below code

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)}

Functions:

@objc func keyboardWillShow(notification: NSNotification) {        
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
    if self.view.frame.origin.y == 0{
        self.view.frame.origin.y -= keyboardSize.height
    }
}}    

And,

@objc func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
    if self.view.frame.origin.y != 0{
        self.view.frame.origin.y = 0 
    }
} }
Eray Alparslan
  • 806
  • 7
  • 12
1

This implementation (Swift 4) will give you the behaviour that comes closest to the default behaviour of Android. Ie: moves the view up when the active textfield is under the keyboard and moves accordingly when the user switches to another view without closing the keyboard. Remember to call setTextFieldDelegates().

public class DelegateProxy: NSObject, UITextFieldDelegate {

  private static let instance = DelegateProxies()

  weak var activeTextField: UITextField?
  var offset: CGFloat = 0
  weak var vc: UIViewController?
  var keyboardHeight: CGFloat = 0

  public static func getDelegate(root: UIViewController) -> DelegateProxies {
    instance.vc = root
    return instance
  }

  public static func getDelegate() -> DelegateProxies {
    return instance
  }

  public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    textField.resignFirstResponder()
    return true
  }


  public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {

    self.activeTextField = textField

    let globalPointY: CGFloat = (textField.superview?.convert(textField.frame.origin, to: nil).y ?? CGFloat(0)) + textField.frame.height


    offset = UIScreen.main.bounds.height - globalPointY

    if keyboardHeight > 0 {
        vc?.moveViewUp(offsetFromKeyboard: keyboardHeight - offset)
    }

    return true
  }
}


extension UIViewController {

  private func setTextFieldDelegates(parentView: UIView) {
    for child in parentView.subviews {
        setTextFieldDelegates(parentView: child)
        (child as? UITextField)?.delegate = DelegateProxies.getDelegate(root: self)
    }
  }

  func registerAutoResizeOnKeyboardAppear(){
    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)
  }

  func unregisterAutoResizeOnKeyboard(){
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
  }

  @objc func keyboardWillShow(notification: NSNotification) {

    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        let offsetFromKeyboard = keyboardSize.height - DelegateProxies.getDelegate().offset
        DelegateProxies.getDelegate().keyboardHeight = keyboardSize.height

        moveViewUp(offsetFromKeyboard: offsetFromKeyboard)
    }
  }

  func moveViewUp(offsetFromKeyboard: CGFloat){
    print("offset from keyboard: \(offsetFromKeyboard)")

    let moveOffset = offsetFromKeyboard + 8

    if offsetFromKeyboard > 0 {
        self.view.frame.origin.y = -moveOffset
    }

    if offsetFromKeyboard < 0 && view.frame.origin.y < 0 {
        self.view.frame.origin.y += -moveOffset
        if self.view.frame.origin.y > 0{
            self.view.frame.origin.y = 0
        }
    }
  }

  @objc func keyboardWillHide(notification: NSNotification) {
    self.view.frame.origin.y = 0
  }
}
ravindu1024
  • 1,496
  • 1
  • 13
  • 30
0

this video tutorial is the best. 7 mins long and it'll just make so much sense. Such a simple solution for when you have multiple text fields and want the scroll view to move "x" amount of pixels when that specific textfield is tapped.

https://youtu.be/VuiPGJOEBH4

Just these steps:

-Place all your textfields within a scrollview that is constrained to the edges of the view.

-Connect all the textfields and scroll view as delegates to the view controller.

-Connect all textfields and scroll view with an IBOutlet.

class ViewController: UIViewController, UITextFieldDelegate {

-Add UITextFieldDelegate protocol to your class

@IBOutlet var stateAddress: UITextField!
@IBOutlet var zipAddress: UITextField!
@IBOutlet var phoneNumber: UITextField!
@IBOutlet var vetEmailAddress: UITextField!    
@IBOutlet weak var scrollView: UIScrollView!

-Add UITextFieldDelegate methods to your swift file:

func textFieldShouldReturn(textField: UITextField) -> Bool {

    textField.resignFirstResponder()
    return true
}


func textFieldDidBeginEditing(textField: UITextField) {

    if (textField == self.stateAddress) {
        scrollView.setContentOffset(CGPointMake(0, 25), animated: true)
    }
    else if (textField == self.zipAddress) {
        scrollView.setContentOffset(CGPointMake(0, 57), animated: true)
    }
    else if (textField == self.phoneNumber) {
        scrollView.setContentOffset(CGPointMake(0, 112), animated: true)
    }
    else if (textField == self.vetEmailAddress) {
        scrollView.setContentOffset(CGPointMake(0, 142), animated: true)
    }
}

func textFieldDidEndEditing(textField: UITextField) {

    scrollView.setContentOffset(CGPointMake(0, 0), animated: true)
}

The first method just activates the return button on the keyboard to dismiss the keyboard. The second is when you tap into whatever specific textfield then setting the y offset of how far your scrollview scrolls (mine is based off of the y location on my view controllers 25,57,112,142). The last says when you tap away from the keyboard the scrollview goes back to original location.

I made my view pixel perfect this way!

bme003
  • 121
  • 10
0

This feature shud have come built in Ios, however we need to do externally.
Insert the below code
* To move view when textField is under keyboard,
* Not to move view when textField is above keyboard
* To move View based on the height of the keyboard when needed.
This works and tested in all cases.

import UIKit

class NamVcc: UIViewController, UITextFieldDelegate
{
    @IBOutlet weak var NamTxtBoxVid: UITextField!

    var VydTxtBoxVar: UITextField!
    var ChkKeyPadDspVar: Bool = false
    var KeyPadHytVal: CGFloat!

    override func viewDidLoad()
    {
        super.viewDidLoad()

        NamTxtBoxVid.delegate = self
    }

    override func viewWillAppear(animated: Bool)
    {
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: #selector(TdoWenKeyPadVyd(_:)),
            name:UIKeyboardWillShowNotification,
            object: nil);
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: #selector(TdoWenKeyPadHyd(_:)),
            name:UIKeyboardWillHideNotification,
            object: nil);
    }

    func textFieldDidBeginEditing(TxtBoxPsgVar: UITextField)
    {
        self.VydTxtBoxVar = TxtBoxPsgVar
    }

    func textFieldDidEndEditing(TxtBoxPsgVar: UITextField)
    {
        self.VydTxtBoxVar = nil
    }

    func textFieldShouldReturn(TxtBoxPsgVar: UITextField) -> Bool
    {
        self.VydTxtBoxVar.resignFirstResponder()
        return true
    }

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
    {
        view.endEditing(true)
        super.touchesBegan(touches, withEvent: event)
    }

    func TdoWenKeyPadVyd(NfnPsgVar: NSNotification)
    {
        if(!self.ChkKeyPadDspVar)
        {
            self.KeyPadHytVal = (NfnPsgVar.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue().height

            var NonKeyPadAraVar: CGRect = self.view.frame
            NonKeyPadAraVar.size.height -= self.KeyPadHytVal

            let VydTxtBoxCenVal: CGPoint? = VydTxtBoxVar?.frame.origin

            if (!CGRectContainsPoint(NonKeyPadAraVar, VydTxtBoxCenVal!))
            {
                self.ChkKeyPadDspVar = true
                UIView.animateWithDuration(1.0,
                    animations:
                    { self.view.frame.origin.y -= (self.KeyPadHytVal)},
                    completion: nil)
            }
            else
            {
                self.ChkKeyPadDspVar = false
            }
        }

    }

    func TdoWenKeyPadHyd(NfnPsgVar: NSNotification)
    {
        if (self.ChkKeyPadDspVar)
        {
            self.ChkKeyPadDspVar = false
            UIView.animateWithDuration(1.0,
                animations:
                { self.view.frame.origin.y += (self.KeyPadHytVal)},
                completion: nil)
        }
    }

    override func viewDidDisappear(animated: Bool)
    {
        super.viewWillDisappear(animated)
        NSNotificationCenter.defaultCenter().removeObserver(self)
        view.endEditing(true)
        ChkKeyPadDspVar = false
    }
}

|::| Sometimes View wil be down, In that case use height +/- 150 :

    NonKeyPadAraVar.size.height -= self.KeyPadHytVal + 150

    { self.view.frame.origin.y -= self.KeyPadHytVal  - 150},
                    completion: nil)

    { self.view.frame.origin.y += self.KeyPadHytVal  - 150},
                completion: nil)
Sujay U N
  • 4,974
  • 11
  • 52
  • 88
0
func keyboardWillShow(notification: NSNotification) {

    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        self.view.frame.origin.y = self.view.frame.height - (self.view.frame.height + keyboardSize.height)
    }

}

func keyboardWillHide(notification: NSNotification) {
        self.view.frame.origin.y = 0
}

it must be more stable

Hadevs Play
  • 71
  • 10
0
 override func viewWillAppear(animated: Bool)
 {
 super.viewWillAppear(animated)

 NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
 NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)

 }

 // MARK: - keyboard
 func keyboardWillShow(notification: NSNotification) 
{

if let userInfo = notification.userInfo {
if let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
let contentInsets = self.tblView.contentInset as UIEdgeInsets
self.tblView.contentInset = UIEdgeInsets(top: contentInsets.top, left: contentInsets.left, bottom: keyboardSize.height, right:contentInsets.right)
                    // ...
                } else {
                    // no UIKeyboardFrameBeginUserInfoKey entry in userInfo
                }
            } else {
                // no userInfo dictionary in notification
            }
        }

func keyboardWillHide(notification: NSNotification) 
{
let contentInsets = self.tblView.contentInset as UIEdgeInsets
self.tblView.contentInset = UIEdgeInsets(top: contentInsets.top, left: contentInsets.left, bottom: 0, right:contentInsets.right)
 }
Himali Shah
  • 181
  • 1
  • 8
0

Use following code for view Up on UITextField Clicked

func textFieldDidBeginEditing(textField: UITextField) {
    ViewUpanimateMoving(true, upValue: 100)
}
func textFieldDidEndEditing(textField: UITextField) {
    ViewUpanimateMoving(false, upValue: 100)
}
func ViewUpanimateMoving (up:Bool, upValue :CGFloat){
    var durationMovement:NSTimeInterval = 0.3
    var movement:CGFloat = ( up ? -upValue : upValue)
    UIView.beginAnimations( "animateView", context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)
    UIView.setAnimationDuration(durationMovement)
    self.view.frame = CGRectOffset(self.view.frame, 0,  movement)
    UIView.commitAnimations()
}
Jugal K Balara
  • 917
  • 5
  • 15
0

I made a cocoapod to simplify the matter:

https://github.com/xtrinch/KeyboardLayoutHelper

How to use it:

Make an auto layout bottom constraint, give it a class of KeyboardLayoutConstraint in module KeyboardLayoutHelper and the pod will do the work necessary to increase it to accomodate appearing and disappearing keyboard. See example project on examples how to use it (I made two: textFields inside a scrollView, and vertically centered textFields with two basic views - login & register).

The bottom layout constraint can be of the container view, the textField itself, anything, you name it.

xtrinch
  • 2,232
  • 1
  • 24
  • 46
0
func registerForKeyboardNotifications()
    {
        //Keyboard
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWasShown), name: UIKeyboardDidShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWillBeHidden), name: UIKeyboardDidHideNotification, object: nil)


    }
    func deregisterFromKeyboardNotifications(){

        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)

    }
    func keyboardWasShown(notification: NSNotification){

        let userInfo: NSDictionary = notification.userInfo!
        let keyboardInfoFrame = userInfo.objectForKey(UIKeyboardFrameEndUserInfoKey)?.CGRectValue()

        let windowFrame:CGRect = (UIApplication.sharedApplication().keyWindow!.convertRect(self.view.frame, fromView:self.view))

        let keyboardFrame = CGRectIntersection(windowFrame, keyboardInfoFrame!)

        let coveredFrame = UIApplication.sharedApplication().keyWindow!.convertRect(keyboardFrame, toView:self.view)

        let contentInsets = UIEdgeInsetsMake(0, 0, (coveredFrame.size.height), 0.0)
        self.scrollViewInAddCase .contentInset = contentInsets;
        self.scrollViewInAddCase.scrollIndicatorInsets = contentInsets;
        self.scrollViewInAddCase.contentSize = CGSizeMake((self.scrollViewInAddCase.contentSize.width), (self.scrollViewInAddCase.contentSize.height))

    }
    /**
     this method will fire when keyboard was hidden

     - parameter notification: contains keyboard details
     */
    func keyboardWillBeHidden (notification: NSNotification) {

        self.scrollViewInAddCase.contentInset = UIEdgeInsetsZero
        self.scrollViewInAddCase.scrollIndicatorInsets = UIEdgeInsetsZero

    }
Kamalkumar.E
  • 254
  • 2
  • 11
  • Use above code to move the textfield above the keyboard in swift 2.2 it will work’s fine . i hope it will help some one – Kamalkumar.E Aug 16 '16 at 13:20
0

swift 3.0 insert in viewDidLoad(), this->

{

view.addSubview(Your_messageInputConteinerView)

    view.addConstraintWithFormat(format: "H:|[v0]|", views:Your_messageInputConteinerView)

    view.addConstraintWithFormat(format: "V:[v0(48)]", views:Your_messageInputConteinerView)

NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)

NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification(notification:)), name: .UIKeyboardWillHide, object: nil)

bottomConstraint = NSLayoutConstraint(item: Your_messageInputConteinerView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0)

view.addConstraint(bottomConstraint!)

}

func handleKeyboardNotification(notification:Notification){

if let userInfo = notification.userInfo {

    if let keyBoardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue{

        print(keyBoardFrame)

        if bottomConstraint?.constant != CGFloat(0) {
                bottomConstraint?.constant = 0
                return
            }
     bottomConstraint?.constant = -keyBoardFrame.height
                           or
        self.view.frame.origin.y = -keyBoardFrame.height
    }
}

}

Anton Russia
  • 197
  • 1
  • 2
0

i've read answers and solved my problem by this lines of code:

class ViewController: UIViewController, UITextFieldDelegate {

  @IBOutlet weak var titleField: UITextField!
  @IBOutlet weak var priceField: UITextField!
  @IBOutlet weak var detailsField: UTtextField!

  override func viewDidLoad() {
   super.viewDidLoad()
// Do not to forget to set the delegate otherwise the textFieldShouldReturn(_:)
// won't work and the keyboard will never be hidden.
   priceField.delegate = self
   titleField.delegate = self
   detailsField.delegate = self

   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)
  }

   func textFieldShouldReturn(_ textField: UITextField) -> Bool {
      self.view.endEditing(true)
      return false
   }

 func keyboardWillShow(notification: NSNotification) {
   var translation:CGFloat = 0
   if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
     if detailsField.isEditing{
       translation = CGFloat(-keyboardSize.height)
     }else if priceField.isEditing{
       translation = CGFloat(-keyboardSize.height / 3.8)
     }
   }
    UIView.animate(withDuration: 0.2) {
   self.view.transform = CGAffineTransform(translationX: 0, y: translation)
    }
  }


 func keyboardWillHide(notification: NSNotification) {
    UIView.animate(withDuration: 0.2) {
     self.view.transform = CGAffineTransform(translationX: 0, y: 0)
    } 
  }
}

I have a few UITextFields and want the view to move up differently depending on which textField is tapped.

Fedya Grab
  • 36
  • 6
0

if you are like me who has tried all the above solutions and still your problem is not solved, I have a got a great solution for you that works like a charm. First I want clarify few things about some of solutions mentioned above.

  1. In my case IQkeyboardmanager was working only when there is no auto layout applied on the elements, if it is applied then IQkeyboard manager will not work the way we think.
  2. Same thing with upward movement of self.view.
  3. i have wriiten a objective c header with a swift support for pushing UITexfield upward when user clicks on it, solving the problem of keyboard covering the UITextfield : https://github.com/coolvasanth/smart_keyboard.
  4. One who has An intermediate or higher level in iOS app development can easily understand the repository and implement it. All the best
Vasanth
  • 560
  • 1
  • 5
  • 25
0
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
            self.view.frame.origin.y = 0
        }
    }
}
Puji Wahono
  • 338
  • 5
  • 10
0

Simpelst Method In Swift 4 enter image description here

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var myTextField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: .UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        myTextField.resignFirstResponder()
    }

    func keyboardWillShow(notification: NSNotification) {
        // let duration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey]
        // print("duration",duration)
        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
            let keyboardHeight : Int = Int(keyboardSize.height)
            print("keyboardWillShow",keyboardHeight)
            if let height = UserDefaults.standard.value(forKey: "keyboardHeight") as? (Int) {
                moveTextField(myTextField, moveDistance: -height as Int, moveDuration: 0.43, up: true)
            }else{
                UserDefaults.standard.set(keyboardHeight, forKey: "keyboardHeight")
                moveTextField(myTextField, moveDistance: -keyboardHeight, moveDuration: 0.43, up: true)
            }
        }
    }

    func keyboardWillHide(notification: NSNotification){
        if let height = UserDefaults.standard.value(forKey: "keyboardHeight") as? (Int) {
            moveTextField(myTextField, moveDistance: -height as Int, moveDuration: 0.25, up: false)
        }
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }

    func moveTextField(_ textField: UITextField, moveDistance: Int, moveDuration: Double, up: Bool) {
        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()
    }

}

You Can Also Move Up And Down Only UITextFiled Not Whole Screen(UIView).
With Using This Method.

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange), name: .UIKeyboardWillChangeFrame, object: nil)

And

   @objc func keyboardWillChange(notification: NSNotification) {

        let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
        let curve = notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt
        let curFrame = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
        let targetFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
        let deltaY = targetFrame.origin.y - curFrame.origin.y

        UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: {
            self.textField.frame.origin.y+=deltaY

        },completion: nil)
    }
ZAFAR007
  • 3,049
  • 1
  • 34
  • 45
0

I've need to move a UIView in swift 4 when keyboard opens and closes. and all of the answers couldn't help me. because height of keyboard changes when emojis open. so my code is :

@objc func keyboardWillShow(sender: NSNotification) {

    if let keyboardFrame: NSValue = sender.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue {

        let keyboardRectangle = keyboardFrame.cgRectValue
        let keyboardHeight = keyboardRectangle.height

        if(self.oldHeight == keyboardHeight){
            self.sendingView.frame.origin.y -= keyboardHeight
            self.oldHeight = keyboardHeight
        }
        else{
            self.sendingView.frame.origin.y += self.oldHeight
            self.sendingView.frame.origin.y -= keyboardHeight
            self.oldHeight = keyboardHeight
        }
    }
}

@objc func keyboardWillHide(sender: NSNotification) {

    if let keyboardFrame: NSValue = sender.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue {

        let keyboardRectangle = keyboardFrame.cgRectValue
        let keyboardHeight = keyboardRectangle.height

        self.sendingView.frame.origin.y += keyboardHeight

    }
}

and in 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);

self.oldHeight = CGFloat() and defines as field at the top of class.

MHSaffari
  • 858
  • 1
  • 16
  • 39
0

Swift 4.1

 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)    
}

@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   //can adjust as keyboardSize.height-(any number 30 or 40)
        }
    }        
}

@objc func keyboardWillHide(notification: NSNotification) {
    if self.view.frame.origin.y != 0 {
        self.view.frame.origin.y = 0
    }
}
0

Moving origin of the view is a naive approach.

Here is the better solution: Adjust the height of the view when the keyboard appears.

let viewHeight = self.view.frame.height

    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)
    }

    @objc func keyboardWillShow(notification: NSNotification) {
        if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
            if self.view.frame.height == self.viewHeight{
                self.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height - keyboardSize.height)
            }

        }
    }

    @objc func keyboardWillHide(notification: NSNotification) {
        if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {

            self.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height + keyboardSize.height)
        }
    }
0

Since there were no answer on how to do this in Combine, here is the approach i used.

  1. We create a publisher to listen to both Notifications, show and hide.
  2. For show we get the frame of the keyboard from the notifications userInfo and check if the current active responder is contained in it. If it's covered return keyboard frame height. If it is not covered return 0, we don't want to move the frame. For the hide notification we simply return 0.
private var keyboardHeightPublisher: AnyPublisher<CGFloat, Never> {
    Publishers.Merge(
        NotificationCenter.default
            .publisher(for: UIResponder.keyboardWillShowNotification)
            .compactMap { $0.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect }
            .map { $0.intersects(self.view.firstResponder!.frame) ? $0.height : 0 }
            .map { $0 * -1 },
        NotificationCenter.default
            .publisher(for: UIResponder.keyboardWillHideNotification)
            .map { _ in CGFloat(0) }
    ).eraseToAnyPublisher()
}

In the viewDidLoad we simply subscribe to the publisher changing the views frame accordingly.

override func viewDidLoad() {
    super.viewDidLoad()

    keyboardHeightPublisher.sink{ [weak self] height in
        self?.view.frame.origin.y = height
    }.store(in: &cancelables)
}

EDIT
Be careful! If the firstResponder is in a subview, you have to calculate the frame corresponding to the whole screen to check if they actually intersect.
Example:

let myViewGlobalFrame = myView.convert(myView.frame, to: parentView)
Deitsch
  • 1,610
  • 14
  • 28
-2

works for me

override func viewDidLoad() {

        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}

 var isScroll = false


@objc func keyboardWillShow(sender: NSNotification) {
    if( !isScroll  ){
        self.view.frame.origin.y -= 150
        isScroll = true
    }
}
@objc func keyboardWillHide(sender: NSNotification) {
    if( isScroll  ){
        self.view.frame.origin.y += 150
        isScroll = false
    }
}
  • 1
    Welcome to SO! When posting code in your answer, it is important to explain why your code works or how it solves OP's problem :) – Joel Aug 31 '18 at 14:34