227

I'm using Swift for programing with iOS and I'm using this code to move the UITextField, but it does not work. I call the function keyboardWillShow correctly, but the textfield doesn't move. I'm using autolayout.

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

deinit {
    NSNotificationCenter.defaultCenter().removeObserver(self);
}

func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        //let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)

        var frame = self.ChatField.frame
        frame.origin.y = frame.origin.y - keyboardSize.height + 167
        self.chatField.frame = frame
        println("asdasd")
    }
}
seantomburke
  • 8,246
  • 3
  • 15
  • 22
Pedro Manfredi
  • 3,628
  • 5
  • 17
  • 20
  • 2
    Walkthrough guide with Project files: https://www.codebeaulieu.com/43/Triggering-NSNotificationCenter-notifications-from-the-UI – Dan Beaulieu Oct 02 '15 at 16:26
  • Perhaps deinit and viewDidLoad are not balanced. – Ricardo Nov 02 '16 at 11:36
  • Based on both Apple's docs and personal experience. Here's my git repo using UIScrollView to move TF: https://github.com/29satnam/MoveTextFieldWhenKeyboardAppearsSwift – Codetard Jul 18 '17 at 09:26

37 Answers37

328

There are a couple of improvements to be made on the existing answers.

Firstly the UIKeyboardWillChangeFrameNotification is probably the best notification as it handles changes that aren't just show/hide but changes due to keyboard changes (language, using 3rd party keyboards etc.) and rotations too (but note comment below indicating the keyboard will hide should also be handled to support hardware keyboard connection).

Secondly the animation parameters can be pulled from the notification to ensure that animations are properly together.

There are probably options to clean up this code a bit more especially if you are comfortable with force unwrapping the dictionary code.

 class MyViewController: UIViewController {

   // This constraint ties an element at zero points from the bottom layout guide
   @IBOutlet var keyboardHeightLayoutConstraint: NSLayoutConstraint?
 
   override func viewDidLoad() {
     super.viewDidLoad()
     NotificationCenter.default.addObserver(self,
       selector: #selector(self.keyboardNotification(notification:)),
       name: UIResponder.keyboardWillChangeFrameNotification,
       object: nil)
   }
 
   deinit {
     NotificationCenter.default.removeObserver(self)
   }
 
   @objc func keyboardNotification(notification: NSNotification) {
     guard let userInfo = notification.userInfo else { return }

     let endFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
     let endFrameY = endFrame?.origin.y ?? 0
     let duration:TimeInterval = (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
     let animationCurveRawNSN = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber
     let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue
     let animationCurve:UIView.AnimationOptions = UIView.AnimationOptions(rawValue: animationCurveRaw)

     if endFrameY >= UIScreen.main.bounds.size.height {
       self.keyboardHeightLayoutConstraint?.constant = 0.0
     } else {
       self.keyboardHeightLayoutConstraint?.constant = endFrame?.size.height ?? 0.0
     }

     UIView.animate(
       withDuration: duration,
       delay: TimeInterval(0),
       options: animationCurve,
       animations: { self.view.layoutIfNeeded() },
       completion: nil)
   }
}
Joseph Lord
  • 6,136
  • 1
  • 24
  • 29
  • Hi @joseph-lord, I think the `animateWithDuration` is actually doing nothing. Try changing the duration to something like `10.0` seconds, or even just commenting out the whole animation call, and just running `self.view.layoutIfNeeded()` instead. For me, it seems to be overridden by the keyboard's own animation. Maybe iOS calls `commitAnimation` after calling the keyboard notification. – ndbroadbent Feb 07 '15 at 04:09
  • @nathan.f77 Are you changing a `keyboardHeightLayoutConstraint` that has been set up between the bottom thing that you want to animate and the bottom layout guide? That line is key and the constraint should not be `nil`. The `self.view.layoutIfNeeded` in the animation block should apply the updated constraint. – Joseph Lord Feb 11 '15 at 15:41
  • 1
    @JosephLord nice. but I found this doesn't work when keyboard is hiding because `endFrame?.size.height` is not nil. I got the end frame as `UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 1024}, {768, 264}}";`. Ran on iOS 8.3 iPad Simulator, Portrait. Xcode6.3 beta4. – Hlung Mar 28 '15 at 07:44
  • @Hlung If that is the endframe on hiding it sounds like the keyboard animated offscreen but didn't actually shrink. You may need to get the window size to know for sure that 1024 is the bottom of the screen. It would then need the constraint constant set to screenheight - endFrame.origin.y. – Joseph Lord Apr 05 '15 at 00:59
  • 8
    if the keyboard doesn't hide, try using this code if endFrame?.origin.y >= UIScreen.mainScreen().bounds.size.height { self.keyboardHeightLayoutConstraint?.constant = 0.0 } else { self.keyboardHeightLayoutConstraint?.constant = endFrame.size.height } – Gabriel Goncalves May 15 '15 at 16:27
  • When the keyboard is dismissed it does not seem to reset the view. This is probably just an overall error with my constraints but yeah :S – nsij22 May 31 '15 at 17:33
  • 4
    keyBoardHeightLayoutConstraint is a constraint defined in InterfaceBuilder constraining the bottom of the view that you want to move/shrink to the bottom layout guide or the bottom of the main view for the viewcontroller. The constant is initially set to zero and will be adjusted to make room for the keyboard when the keyboard appears or changes size. – Joseph Lord Aug 03 '15 at 16:48
  • Works best for me so far. I suggest to read out the constraint's initial constant in case it's not zero: var keyboardHeightLayoutConstraintConstant: CGFloat? = 0 ... override func viewDidAppear(animated: Bool) { self.keyboardHeightLayoutConstraintConstant = self.keyboardHeightLayoutConstraint?.constant } ... func keyboardNotification(notification: NSNotification) { .... self.keyboardHeightLayoutConstraint?.constant = endFrame?.size.height ?? 0.0 self.keyboardHeightLayoutConstraint?.constant += keyboardHeightLayoutConstraintConstant! – Eduard Feicho Jan 19 '16 at 18:39
  • I didn't read the "PLEASE NOTE" at the bottom and just realised that @Gabox solved my issue that the keyboard was simply translated rather than shrunk!! Thanks. – Eduard Feicho Jan 19 '16 at 22:12
  • the constrain dont go to zero after keyboard dismissed – David Alejandro Londoño Mejía Feb 01 '16 at 21:06
  • for some reason the view will animate with the keyboard when its dismissed, but the animation is skipped when it appears... even thought its the same code handling both, quite strange. if i use the UIKeyboard**Did**ChangeFrameNotification it works fine though, but then the animation is delayed because the keyboard has to finish its animation first – Fonix Jul 22 '16 at 03:11
  • ok nevermind, it was just the simulator being silly, it works fine on device – Fonix Jul 22 '16 at 04:38
  • That works like a charm... Thanks alot! But There's a slight problem on iPad when the keyboard is split or detached... Any good practice to handle these two special cases? – Zaphod Aug 25 '16 at 14:36
  • @Fonix I'm getting the issue you're talking about with the keyboard not animating on appearance, yet still working on disappearance with iOS 10 now. It was working fine before I updated to iOS 10 too. Is it still working fine on your device with the new upgrade? – Thomas Sep 14 '16 at 03:18
  • Hi, this works perfectly! it was the answer i was looking for, but when i tab outside for keyboard to dissapear, the view moves lower. what can i do? – Stephy Samaniego Jan 03 '17 at 16:40
  • 2
    Note that `.UIKeyboardWillChangeFrame` doesn't fire when a hardware keyboard is connected, even though the iOS keyboard disappears. You need to observe `.UIKeyboardWillHide` as well to catch that edge case. – jamesk Feb 24 '17 at 17:30
  • @jamesk Good catch, do you have a proposed edit? Is it as simple as adding the extra observer or does the handling code need updating too? I've not got time to experiment at the moment. – Joseph Lord Feb 28 '17 at 10:00
  • 1
    @Sulthan works okay.. My issue is that is getting a bit higher than the keybaord. is there any way we can fix this? – Pavlos Mar 15 '17 at 00:07
  • Code didnt work for me so i just removed if/else and replaced with `self..frame.origin.y = endFrameY - self..frame.height` – JavaBeast May 01 '18 at 05:10
  • There are several CocoaPods that provide robust keyboard avoiding solutions. I recommend https://github.com/IdleHandsApps/IHKeyboardAvoiding, but of course I do, I wrote it :) – Fraser Jun 24 '18 at 22:09
  • @Fraser I just couldn't get your CocoaPods working, how to import your module? Please improve the installation part of README. – giovannipds Jul 29 '18 at 20:20
  • @giovannipds `import IHKeyboardAvoiding` is all you need. Raise an issue on Github if you're still having issues – Fraser Jul 30 '18 at 02:12
  • @Fraser thanks for answering, pretty sure I've tried that. It was probably an Xcode delay or something. Anyway, I dealt with it in other way. – giovannipds Aug 08 '18 at 18:29
  • doesn't work for Swift 4 you should edit the answer or don't mark it as a solution for Swift 4.x – MikeG Mar 05 '19 at 21:12
  • @JosephLord can you explain to me, why not used input accessory view ?? – a.masri Apr 18 '19 at 20:52
  • @a.masri The intention here is rearranging content on the screen to allow space for the keyboard. I believe the accessory view is for attaching additional keyboard specific functionality to the keyboard. – Joseph Lord Apr 26 '19 at 09:08
  • You can use my gist it's well commented, it's made for views with ScrollView, but you can just change the scrollView part to your UIView it works perfectly. https://gist.github.com/Sjahriyar/916e93153a29dc602b45f29d39182352 – Shahriyar Feb 05 '20 at 10:15
  • I've been struggling for days to fix the animation issue with text view. For me UIKeyboardWillChangeFrame fixed the issue. It works perfectly with this notification instead of keyboardDidShowNotification or keyboardWillShowNotification notifications. Please mark UIKeyboardWillChangeFrame in bold to highlight it. – Raymond Jun 07 '20 at 15:45
  • How would I do this for a UI Created programmatically? Do I just define a let `bottomConstraint` in the viewController and then add it in `NSLayoutConstraint.activate[]`? – Parth Tamane Jan 13 '21 at 08:30
  • got it to work will make a new answer. But there was a correction in the constraint setting. `self.keyboardHeightLayoutConstraint?.constant = endFrame?.size.height ?? 0.0` this should be `self.discussionsMessageBoxBottomAnchor.constant = -1 * (endFrame?.size.height ?? 0.0)` – Parth Tamane Jan 13 '21 at 08:44
  • @ParthTamane I think it depends which way round the constraint is made. The original answer was written before iOS had anchors (or is that just the name you gave a constraint). – Joseph Lord Jan 13 '21 at 17:14
  • The anchor is a NSLayoutConstraint. But the constraint is based on anchor: view.bottomAnchor – Parth Tamane Jan 13 '21 at 17:30
129

If you're using Auto Layout, I assume you've set the Bottom Space to Superview constraint. If that's the case, you simply have to update the constraint's value. Here's how you do it with a little bit of animation.

func keyboardWasShown(notification: NSNotification) {
    let info = notification.userInfo!
    let keyboardFrame: CGRect = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue()

    UIView.animateWithDuration(0.1, animations: { () -> Void in
        self.bottomConstraint.constant = keyboardFrame.size.height + 20
    })
}

The hardcoded 20 is added only to pop the textfield above the keyboard just a bit. Otherwise the keyboard's top margin and textfield's bottom margin would be touching.

When the keyboard is dismissed, reset the constraint's value to its original one.

Isuru
  • 27,485
  • 56
  • 174
  • 278
  • 1
    Can you explain me pls how i define it? Thanks! i always control all on storyboard – Pedro Manfredi Sep 05 '14 at 21:15
  • 1
    Select the textfield, go to Editor -> pin and then you'll see some constraint options. – Isuru Sep 06 '14 at 03:30
  • yep i have it but when i use self.bottomConstraint.constant = keyboardFrame.size.height + 20 i don't have a member named bottomConstraint. Thanks!! – Pedro Manfredi Sep 09 '14 at 20:24
  • 4
    bottomConstraint is the name I gave to the constraint. I selected the constrant, dragged and created an IBOutlet to it and gave that name. You can create IBOutlets to constraints just as you could do with other UI elements like buttons and text fields. – Isuru Sep 10 '14 at 04:58
  • What can I do if I am not using Auto Layout? – Haring10 Oct 30 '14 at 18:28
  • @JoshHarington Have you looked into a library like [this](https://github.com/IdleHandsApps/IHKeyboardAvoiding) one? – Isuru Oct 31 '14 at 04:12
  • 3
    This answer worked great except the animation happened immediately for me. Check [How do I animate constraint changes?](http://stackoverflow.com/a/12664093/1438339) for how to animate properly. – Adam Johns Mar 11 '15 at 15:06
  • You need to create an `IBOutlet` for the constraint: `@IBOutlet var constraint_input: NSLayoutConstraint!` – superarts.org May 26 '15 at 06:17
  • @Isuru Do I have to set some thing else, because when I gave the above function its not even getting into that function, should I register some notification? Please help – vinbhai4u May 30 '15 at 06:44
  • 2
    @vinbhai4u You have to register for `UIKeyboardWillShowNotification` notification. Look at the code in the OP's question. – Isuru May 30 '15 at 06:56
  • @Isuru I am trying to achieve http://stackoverflow.com/questions/30533285/scrollview-with-tab-bar can you please take a look at the question I have asked? it would be a great help – vinbhai4u May 30 '15 at 07:04
  • Thanks @Isuru I got the answer and posted in http://stackoverflow.com/questions/30533285/scrollview-with-tab-bar thanks for your help – vinbhai4u May 30 '15 at 11:34
  • 8
    @AdamJohns To animate the constraint change, update the constant outside the `animateWithDuration` and call `self.view.layoutIfNeeded()` inside the animate block. – Max Aug 27 '15 at 08:11
  • Nice answer, Thanks. It's always better to update constraint instead of frame, and it's simple. – Ashok Sep 16 '15 at 07:01
  • `bottomConstraint` is not valid – Hobbyist Feb 03 '16 at 02:31
  • This worked for me, I created a top constraint with priority 999 and a bottom constraint with constant >= 0. So when keyboard pushes layout up the top constraint will allow it as it has lower priority – user1354603 Feb 05 '16 at 12:04
  • What if you have a tableView above the view you are trying to move? I got the code to work in a separate view, with no constraints (Table views) above it, but how would you handle the above constraint? – enlyte Feb 05 '16 at 20:06
  • 1
    I concur with @Max. To properly animate it you should update the constraint before calling the animateWithDuration method and then inside this method just call layoutIfNeeded over the self.view – GKK Oct 21 '16 at 11:22
119

A simple solution is to move view up with constant of keyboard height.

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

@objc func keyboardWillShow(sender: NSNotification) {
     self.view.frame.origin.y = -150 // Move view 150 points upward 
}

@objc func keyboardWillHide(sender: NSNotification) {
     self.view.frame.origin.y = 0 // Move view to original position  
}

Swift 5:

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(sender:)), name: UIResponder.keyboardWillShowNotification, object: nil);

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(sender:)), name: UIResponder.keyboardWillHideNotification, object: nil);
Saqib Omer
  • 4,917
  • 7
  • 46
  • 65
  • 4
    I like this simple solution. But I added a keyboardShowing boolean since I have move than one textfield. I only move the keyboard once while it is showing. Thanks. – Ken Feb 18 '15 at 17:02
  • 2
    instead of moving the view, move the textView – ericgu Mar 23 '15 at 23:11
  • 1
    This will keep minus the view's y value if user switch the input language. – Jeffrey Neo Apr 08 '15 at 00:22
  • instead of changing the self.view i did self.myConstraint value, it works but the thing is the view (to which constraint is applied) keeps on moving up. did anyone face this issue ? – Sashi Apr 15 '15 at 20:48
  • You are adding constraint constant which will affect the view to which that constraint is attached. @Sashi – Saqib Omer Apr 16 '15 at 07:04
  • Yea, I figured that out after a while. I was adding some specified number of pixels every time keyboard is clicked, but forgot to remove that while hiding keyboard. Now the view moves correctly, but is there a way to make that transition smooth? Like animating it up by that pixels? – Sashi Apr 16 '15 at 15:13
  • There are no pixels in iOS instead there are points. For animation see this question. http://stackoverflow.com/questions/25693130/move-textfield-when-keyboard-appears-swift or better ask new question. – Saqib Omer Apr 16 '15 at 17:16
  • I like this simple solution, but ti does not work for me. I can see the view being moved, but then it resets back to it's original place/frame. It is as if ViewDidLayoutSubviews are interfering.. – KML Jun 27 '15 at 10:45
  • Or maybe it has something to do with AutoLayout? – KML Jun 27 '15 at 10:52
  • It is not changing any layout constraint. Add a breakpoint in keyboardWillHide function to check when it is being called. – Saqib Omer Jun 27 '15 at 18:33
  • @Sashi could you post your solution please? currently facing the same issue – kye Oct 11 '15 at 02:40
  • The code is nice..but the problem is when you tap on different textfields in a single viewcontroller repeatedly then it actually moving the complete view continuously and throughing the textfields out of the screen... – onCompletion Nov 02 '15 at 13:01
  • 6
    Instead of using `self.view.frame.origin.y -= 150` use `self.view.frame.origin.y = -150` and instead of `self.view.frame.origin.y += 150` use `self.view.frame.origin.y = 0`. This prevents the view moving 150 each time a new field is touched. – gunwin Mar 03 '16 at 22:20
  • It is working fine. but animation is missing. Thanks – Muhammad Haroon Iqbal Sep 03 '20 at 07:28
47

For moving your view while editing textfield try this , I have applied this ,

Option 1 :- ** **Update in Swift 5.0 and iPhone X , XR , XS and XS Max Move using NotificationCenter

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

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

Note:- If you will not deregister than it will call from child class and will reason of crashing or else.

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    NotificationCenter.default.addObserver( self, selector: #selector(keyboardWillShow(notification:)), name:  UIResponder.keyboardWillShowNotification, object: nil )
}
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
}

@objc func keyboardWillShow( notification: Notification) {
    if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
        var newHeight: CGFloat
        let duration:TimeInterval = (notification.userInfo![UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
        let animationCurveRawNSN = notification.userInfo![UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber
        let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue
        let animationCurve:UIView.AnimationOptions = UIView.AnimationOptions(rawValue: animationCurveRaw)
        if #available(iOS 11.0, *) {
            newHeight = keyboardFrame.cgRectValue.height - self.view.safeAreaInsets.bottom
        } else {
            newHeight = keyboardFrame.cgRectValue.height
        }
        let keyboardHeight = newHeight  + 10 // **10 is bottom margin of View**  and **this newHeight will be keyboard height**
        UIView.animate(withDuration: duration,
                       delay: TimeInterval(0),
                       options: animationCurve,
                       animations: {
                        self.view.textViewBottomConstraint.constant = keyboardHeight **//Here you can manage your view constraints for animated show**
                        self.view.layoutIfNeeded() },
                       completion: nil)
    }
}

Option 2 :- Its work fine

func textFieldDidBeginEditing(textField: UITextField) {
        self.animateViewMoving(up: true, moveValue: 100)
}
func textFieldDidEndEditing(textField: UITextField) {
        self.animateViewMoving(up: false, moveValue: 100)
}

func animateViewMoving (up:Bool, moveValue :CGFloat){
    var movementDuration:NSTimeInterval = 0.3
    var movement:CGFloat = ( up ? -moveValue : moveValue)
    UIView.beginAnimations( "animateView", context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)
    UIView.setAnimationDuration(movementDuration )
    self.view.frame = CGRectOffset(self.view.frame, 0,  movement)
    UIView.commitAnimations()
}

I got this answer from this source UITextField move up when keyboard appears in Swift

IN the Swift 4 ---

func textFieldDidBeginEditing(_ textField: UITextField) {
        animateViewMoving(up: true, moveValue: 100)
    }

    func textFieldDidEndEditing(_ textField: UITextField) {
        animateViewMoving(up: false, moveValue: 100)
    }
    func animateViewMoving (up:Bool, moveValue :CGFloat){
        let movementDuration:TimeInterval = 0.3
        let movement:CGFloat = ( up ? -moveValue : moveValue)
        UIView.beginAnimations( "animateView", context: nil)
        UIView.setAnimationBeginsFromCurrentState(true)
        UIView.setAnimationDuration(movementDuration ) 
        self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
        UIView.commitAnimations()
    }
Community
  • 1
  • 1
Jogendra.Com
  • 5,980
  • 2
  • 24
  • 33
  • 1
    @Jogendra.Com,Thanks for your hard work.But,It work best on iPhone 5,4s and 6.But,how can i disable it on iPhone 6plus and iPad (higher ones) – Thiha Aung Jan 06 '16 at 10:23
  • If you are using Option 1, please be sure to add a constraint IBOutlet. You will create a constraint that you want to resize using Auto-layout then drag and drop it to the viewcontroller to create an IBOutlet that I reference it as self.iboutletConstraint.constant in the animate function. Also this does not re-adjust the outlet on hiding the keyboard, I handled that by resetting the constraint to it's original value. – Hammad Tariq Jul 01 '19 at 11:05
21

I love clean Swift code. So here's the tightest code I could come up with to move a text view up/down with the keyboard. It's currently working in an iOS8/9 Swift 2 production app.

UPDATE (March 2016): I just tightened up my previous code as much as possible. Also, there are a bunch of popular answers here that hardcode the keyboard height and animation parameters. There's no need for that, not to mention that the numbers in these answers don't always line up with the actual values I'm seeing on my 6s+ iOS9 (keyboard height of 226, duration of 0.25, and animation curve of 7). In any case, it's almost no extra code to get those values straight from the system. See below.

override func viewDidLoad() {
    super.viewDidLoad()

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

func animateWithKeyboard(notification: NSNotification) {

    // Based on both Apple's docs and personal experience, 
    // I assume userInfo and its documented keys are available.
    // If you'd like, you can remove the forced unwrapping and add your own default values.

    let userInfo = notification.userInfo!
    let keyboardHeight = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue().height
    let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as! Double
    let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as! UInt
    let moveUp = (notification.name == UIKeyboardWillShowNotification)

    // baseContraint is your Auto Layout constraint that pins the
    // text view to the bottom of the superview.

    baseConstraint.constant = moveUp ? -keyboardHeight : 0

    let options = UIViewAnimationOptions(rawValue: curve << 16)
    UIView.animateWithDuration(duration, delay: 0, options: options,
        animations: {
            self.view.layoutIfNeeded()
        },
        completion: nil
    )

}

NOTE: This code covers the most comment/general case. However, more code may be needed to handle different orientations and/or custom keyboards Here's an in-depth article on working with the iOS keyboard. If you need to handle every scenario, this may help.

scootermg
  • 1,196
  • 13
  • 11
  • Seems to be Swift 1.1 and I think won't compile in Swift 1.2 because it uses `as` for force casts. `as!` may work but as you can see elsewhere on this page I avoid force casts and force unwrapping myself. – Joseph Lord Apr 24 '15 at 14:44
  • Compiles now in Swift 1.2. And I added a comment to the code re: the forced unwrapping. Cheers. – scootermg Mar 03 '16 at 17:18
  • Oops. I meant Swift 2. – scootermg Mar 03 '16 at 17:25
  • Depending on how you linked up your `baseConstraint` it might be `baseConstraint.constant = moveUp ? keyboardHeight : 0` instead of `baseConstraint.constant = moveUp ? -keyboardHeight : 0`. – limfinity Apr 13 '16 at 11:45
15

Edit: I recommend an easier and cleaner solution. Just change the class of bottom spacing constraint to KeyboardLayoutConstraint. It will automatically expand to the keyboard height.


This is an improved version of @JosephLord 's answer.

As tested on iOS 8.3 iPad Simulator, Portrait. Xcode6.3 beta4, I found his answer doesn't work when keyboard is hiding because UIKeyboardFrameEndUserInfoKey is "NSRect: {{0, 1024}, {768, 264}}";. The height is never 0.

This goes back to use the traditional UIKeyboardWillShowNotification and UIKeyboardWillHideNotification to better tell when keyboard is hiding rather than relying on the end frame's height. UIKeyboardWillShowNotification is also sent when keyboard frame is changed so it should cover all use cases.

    // You have to set this up in storyboard first!. 
    // It's a vertical spacing constraint between view and bottom of superview.
    @IBOutlet weak var bottomSpacingConstraint: NSLayoutConstraint! 

    override func viewDidLoad() {
        super.viewDidLoad()

        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardNotification:"), name:UIKeyboardWillShowNotification, object: nil);
        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardNotification:"), name:UIKeyboardWillHideNotification, object: nil);
    }

    deinit {
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }

    func keyboardNotification(notification: NSNotification) {

        let isShowing = notification.name == UIKeyboardWillShowNotification

        if let userInfo = notification.userInfo {
            let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue()
            let endFrameHeight = endFrame?.size.height ?? 0.0
            let duration:NSTimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
            let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
            let animationCurveRaw = animationCurveRawNSN?.unsignedLongValue ?? UIViewAnimationOptions.CurveEaseInOut.rawValue
            let animationCurve:UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
            self.bottomSpacingConstraint?.constant = isShowing ? endFrameHeight : 0.0
            UIView.animateWithDuration(duration,
                delay: NSTimeInterval(0),
                options: animationCurve,
                animations: { self.view.layoutIfNeeded() },
                completion: nil)
        }
    }
Hlung
  • 12,632
  • 6
  • 66
  • 87
  • Could you please explain your edit? I couldn't get it to work. I have a UIScrollView with a Button at the bottom. I setted the class at the margin bottom constraint on the bottom. – schw4ndi Feb 07 '17 at 23:04
  • @schw4ndi to what views does your bottom constraints tied to? it should be connecting bottom of scrollview to the bottom of that scrollview's superview. – Hlung Feb 08 '17 at 02:46
  • Oh thanks, i had the constraint between the button and the scrollView – schw4ndi Feb 08 '17 at 13:21
9

You can use this library and just one line of code in appDidFinishedLaunching and u are done..

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

    IQKeyboardManager.sharedManager().enable = true
    return true
}

IQKeyboardManager - adjust view whenever keyboard appear link - https://github.com/hackiftekhar/IQKeyboardManager

Abdul Karim
  • 3,458
  • 1
  • 32
  • 46
9

i am working with swift 4 and i am solved this issue without use any extra bottom constraint look my code is here.its really working on my case

1) Add Notification Observer in did load

override func viewDidLoad() {
        super.viewDidLoad()
        setupManager()
        // Do any additional setup after loading the view.
        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)
    }

2) Remove Notification Observer like

deinit {
        NotificationCenter.default.removeObserver(self)
    }

3) Add keyboard show/ hide methods like

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

@objc func keyboardWillHide(notification: NSNotification) {
        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
            UIView.animate(withDuration: 0.1, animations: { () -> Void in
                self.view.frame.origin.y += keyboardSize.height
                self.view.layoutIfNeeded()
            })
        }
    }

4) Add textfeild delegate and add touchesBegan methods .usefull for hide the keyboard when touch outside the textfeild on screen

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

    }
pansora abhay
  • 673
  • 7
  • 14
  • needs to be `UIKeyboardFrameEndUserInfoKey` – Micro May 08 '18 at 04:49
  • 1
    `NSNotification.Name.UIKeyboardWillShow ` is Renamed to `UIResponder.keyboardWillShowNotification` also `UIKeyboardFrameBeginUserInfoKey ` to `UIResponder.keyboardFrameBeginUserInfoKey` – smj Mar 01 '19 at 01:03
8

This is an improved version of @JosephLord and @Hlung's answer. It can apply whether you have tabbar or not. And it would perfectly restore view that is moved by keyboard to original position.

// You have to set this up in storyboard first!. 
// It's a vertical spacing constraint between view and bottom of superview.
@IBOutlet weak var bottomSpacingConstraint: NSLayoutConstraint! 

override func viewDidLoad() {
        super.viewDidLoad()            

        //    Receive(Get) Notification
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardNotification:", name: UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardNotification:", name: UIKeyboardWillHideNotification, object: nil)


        self.originalConstraint = self.keyboardHeightLayoutConstraint?.constant //for original coordinate.
}

func keyboardNotification(notification: NSNotification) {
        let isShowing = notification.name == UIKeyboardWillShowNotification

        var tabbarHeight: CGFloat = 0
        if self.tabBarController? != nil {
            tabbarHeight = self.tabBarController!.tabBar.frame.height
        }
        if let userInfo = notification.userInfo {
            let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue()
            let duration:NSTimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
            let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
            let animationCurveRaw = animationCurveRawNSN?.unsignedLongValue ?? UIViewAnimationOptions.CurveEaseInOut.rawValue
            let animationCurve:UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
            self.keyboardHeightLayoutConstraint?.constant = isShowing ? (endFrame!.size.height - tabbarHeight) : self.originalConstraint!
            UIView.animateWithDuration(duration,
                delay: NSTimeInterval(0),
                options: animationCurve,
                animations: { self.view.layoutIfNeeded() },
                completion: nil)
        }
}
Jeff Gu Kang
  • 3,930
  • 2
  • 28
  • 37
8

I created a Swift 3 protocol to handle the keyboard appearance / disappearance

import UIKit

protocol KeyboardHandler: class {

var bottomConstraint: NSLayoutConstraint! { get set }

    func keyboardWillShow(_ notification: Notification)
    func keyboardWillHide(_ notification: Notification)
    func startObservingKeyboardChanges()
    func stopObservingKeyboardChanges()
}


extension KeyboardHandler where Self: UIViewController {

    func startObservingKeyboardChanges() {

        // NotificationCenter observers
        NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillShow, object: nil, queue: nil) { [weak self] notification in
          self?.keyboardWillShow(notification)
        }

        // Deal with rotations
        NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil, queue: nil) { [weak self] notification in
          self?.keyboardWillShow(notification)
        }

        // Deal with keyboard change (emoji, numerical, etc.)
        NotificationCenter.default.addObserver(forName: NSNotification.Name.UITextInputCurrentInputModeDidChange, object: nil, queue: nil) { [weak self] notification in
          self?.keyboardWillShow(notification)
        }

        NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillHide, object: nil, queue: nil) { [weak self] notification in
          self?.keyboardWillHide(notification)
        }
    }


    func keyboardWillShow(_ notification: Notification) {

      let verticalPadding: CGFloat = 20 // Padding between the bottom of the view and the top of the keyboard

      guard let value = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue else { return }
      let keyboardHeight = value.cgRectValue.height

      // Here you could have more complex rules, like checking if the textField currently selected is actually covered by the keyboard, but that's out of this scope.
      self.bottomConstraint.constant = keyboardHeight + verticalPadding

      UIView.animate(withDuration: 0.1, animations: { () -> Void in
          self.view.layoutIfNeeded()
      })
  }


  func keyboardWillHide(_ notification: Notification) {
      self.bottomConstraint.constant = 0

      UIView.animate(withDuration: 0.1, animations: { () -> Void in
          self.view.layoutIfNeeded()
      })
  }


  func stopObservingKeyboardChanges() {
      NotificationCenter.default.removeObserver(self)
  }

}

Then, to implement it in a UIViewController, do the following:

  • let the viewController conform to this protocol :

    class FormMailVC: UIViewControlle, KeyboardHandler {
    
  • start observing keyboard changes in viewWillAppear:

    // MARK: - View controller life cycle
    override func viewWillAppear(_ animated: Bool) {
      super.viewWillAppear(animated)
      startObservingKeyboardChanges()
    }
    
  • stop observing keyboard changes in viewWillDisappear:

    override func viewWillDisappear(_ animated: Bool) {
      super.viewWillDisappear(animated)
      stopObservingKeyboardChanges()
    }
    
  • create an IBOutlet for the bottom constraint from the storyboard:

    // NSLayoutConstraints
    @IBOutlet weak var bottomConstraint: NSLayoutConstraint!
    

    (I recommend having all of your UI embedded inside a "contentView", and linking to this property the bottom constraint from this contentView to the bottom layout guide) Content view bottom constraint

  • change the constraint priority of the top constraint to 250 (low)

Content view top constraint

This is to let the whole content view slide upwards when the keyboard appears. The priority must be lower than any other constraint priority in the subviews, including content hugging priorities / content compression resistance priorities.

  • Make sure that your Autolayout has enough constraints to determine how the contentView should slide up.

You may have to add a "greater than equal" constraint for this: "greater than equal" constraint

And here you go! Without keyboard

With keyboard

Frédéric Adda
  • 5,240
  • 4
  • 50
  • 67
6

Easiest way that doesn't 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 object/view/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,326
  • 1
  • 11
  • 14
  • 2
    Great solution! But you need the Safe Area.Bottom as the first item on the constraint (didn't work for me when it was the second item). And it worked best with the constant set to 0 as it preserves the constant and adjusts it, rather than just moving it far enough to show the field and keyboard. – Mythlandia Mar 13 '18 at 04:24
  • Do you have KeyboardLayoutConstraint swift4 version ? – jeet.chanchawat Oct 11 '18 at 11:55
6

Such simple UIViewController extension can be used

//MARK: - Observers
extension UIViewController {

    func addObserverForNotification(notificationName: String, actionBlock: (NSNotification) -> Void) {
        NSNotificationCenter.defaultCenter().addObserverForName(notificationName, object: nil, queue: NSOperationQueue.mainQueue(), usingBlock: actionBlock)
    }

    func removeObserver(observer: AnyObject, notificationName: String) {
        NSNotificationCenter.defaultCenter().removeObserver(observer, name: notificationName, object: nil)
    }
}

//MARK: - Keyboard observers
extension UIViewController {

    typealias KeyboardHeightClosure = (CGFloat) -> ()

    func addKeyboardChangeFrameObserver(willShow willShowClosure: KeyboardHeightClosure?,
        willHide willHideClosure: KeyboardHeightClosure?) {
            NSNotificationCenter.defaultCenter().addObserverForName(UIKeyboardWillChangeFrameNotification,
                object: nil, queue: NSOperationQueue.mainQueue(), usingBlock: { [weak self](notification) in
                    if let userInfo = notification.userInfo,
                        let frame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue(),
                        let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Double,
                        let c = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? UInt,
                        let kFrame = self?.view.convertRect(frame, fromView: nil),
                        let kBounds = self?.view.bounds {

                            let animationType = UIViewAnimationOptions(rawValue: c)
                            let kHeight = kFrame.size.height
                            UIView.animateWithDuration(duration, delay: 0, options: animationType, animations: {
                                if CGRectIntersectsRect(kBounds, kFrame) { // keyboard will be shown
                                    willShowClosure?(kHeight)
                                } else { // keyboard will be hidden
                                    willHideClosure?(kHeight)
                                }
                                }, completion: nil)
                    } else {
                            print("Invalid conditions for UIKeyboardWillChangeFrameNotification")
                    }
            })
    }

    func removeKeyboardObserver() {
        removeObserver(self, notificationName: UIKeyboardWillChangeFrameNotification)
    }
}

Example of usage

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

        removeKeyboardObserver()
    }

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

    addKeyboardChangeFrameObserver(willShow: { [weak self](height) in
        //Update constraints here
        self?.view.setNeedsUpdateConstraints()
        }, willHide: { [weak self](height) in
        //Reset constraints here
        self?.view.setNeedsUpdateConstraints()
    })
}

Swift 4 solution

//MARK: - Observers
extension UIViewController {

  func addObserverForNotification(_ notificationName: Notification.Name, actionBlock: @escaping (Notification) -> Void) {
    NotificationCenter.default.addObserver(forName: notificationName, object: nil, queue: OperationQueue.main, using: actionBlock)
  }

  func removeObserver(_ observer: AnyObject, notificationName: Notification.Name) {
    NotificationCenter.default.removeObserver(observer, name: notificationName, object: nil)
  }
}

//MARK: - Keyboard handling
extension UIViewController {

  typealias KeyboardHeightClosure = (CGFloat) -> ()

  func addKeyboardChangeFrameObserver(willShow willShowClosure: KeyboardHeightClosure?,
                                      willHide willHideClosure: KeyboardHeightClosure?) {
    NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillChangeFrame,
                                           object: nil, queue: OperationQueue.main, using: { [weak self](notification) in
                                            if let userInfo = notification.userInfo,
                                              let frame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,
                                              let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Double,
                                              let c = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? UInt,
                                              let kFrame = self?.view.convert(frame, from: nil),
                                              let kBounds = self?.view.bounds {

                                              let animationType = UIViewAnimationOptions(rawValue: c)
                                              let kHeight = kFrame.size.height
                                              UIView.animate(withDuration: duration, delay: 0, options: animationType, animations: {
                                                if kBounds.intersects(kFrame) { // keyboard will be shown
                                                  willShowClosure?(kHeight)
                                                } else { // keyboard will be hidden
                                                  willHideClosure?(kHeight)
                                                }
                                              }, completion: nil)
                                            } else {
                                              print("Invalid conditions for UIKeyboardWillChangeFrameNotification")
                                            }
    })
  }

  func removeKeyboardObserver() {
    removeObserver(self, notificationName: NSNotification.Name.UIKeyboardWillChangeFrame)
  }
}

Swift 4.2

//MARK: - Keyboard handling
extension UIViewController {

    func addObserverForNotification(_ notificationName: Notification.Name, actionBlock: @escaping (Notification) -> Void) {
        NotificationCenter.default.addObserver(forName: notificationName, object: nil, queue: OperationQueue.main, using: actionBlock)
    }

    func removeObserver(_ observer: AnyObject, notificationName: Notification.Name) {
        NotificationCenter.default.removeObserver(observer, name: notificationName, object: nil)
    }

    typealias KeyboardHeightClosure = (CGFloat) -> ()

    func removeKeyboardObserver() {
        removeObserver(self, notificationName: UIResponder.keyboardWillChangeFrameNotification)
    }

    func addKeyboardChangeFrameObserver(willShow willShowClosure: KeyboardHeightClosure?,
                                        willHide willHideClosure: KeyboardHeightClosure?) {
        NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillChangeFrameNotification,
                                               object: nil, queue: OperationQueue.main, using: { [weak self](notification) in
                                                if let userInfo = notification.userInfo,
                                                    let frame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,
                                                    let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double,
                                                    let c = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt,
                                                    let kFrame = self?.view.convert(frame, from: nil),
                                                    let kBounds = self?.view.bounds {

                                                    let animationType = UIView.AnimationOptions(rawValue: c)
                                                    let kHeight = kFrame.size.height
                                                    UIView.animate(withDuration: duration, delay: 0, options: animationType, animations: {
                                                        if kBounds.intersects(kFrame) { // keyboard will be shown
                                                            willShowClosure?(kHeight)
                                                        } else { // keyboard will be hidden
                                                            willHideClosure?(kHeight)
                                                        }
                                                    }, completion: nil)
                                                } else {
                                                    print("Invalid conditions for UIKeyboardWillChangeFrameNotification")
                                                }
        })
    }
}
Huy-Anh Hoang
  • 656
  • 5
  • 19
ale_stro
  • 806
  • 9
  • 23
  • 2
    much more "Swift" than the other solutions / works great / reusable in every controller without rewriting everything -> definitely the best one here :) – Tib Jun 29 '16 at 15:49
  • I tried but no chance, is UIScrollView needed or what? – erdemgc Feb 01 '19 at 08:14
  • @erdemgc did you see Example of usage? All you need is just UIViewControlller + addKeyboardChangeFrameObserver and then do not forget to remove it – ale_stro Feb 04 '19 at 13:02
  • The `removeKeyboardObserver()` method here does not actually remove the observer. If you don't call this, you will see a `Invalid conditions for UIKeyboardWillChangeFrameNotification` in the console from the add method. If you call this, you will see the same error, meaning the observer isn't removed. The documentation states "To unregister observations, you pass the object returned by this method to `removeObserver(_:)`." So what you do instead is save the object returned by that method, then pass it in when you want to remove the observer. – Huy-Anh Hoang Apr 26 '19 at 21:44
  • Actually, once scrollview is loaded, bound is assigned a value and you can't detect if a keyboard will be hidden if keyboard frame intersects with bound. – James Kim May 05 '19 at 12:52
  • @Hemang you can update any constraint you need. this closures shows you height of keyboard – ale_stro Jan 27 '20 at 10:33
4
struct MoveKeyboard {
    static let KEYBOARD_ANIMATION_DURATION : CGFloat = 0.3
    static let MINIMUM_SCROLL_FRACTION : CGFloat = 0.2;
    static let MAXIMUM_SCROLL_FRACTION : CGFloat = 0.8;
    static let PORTRAIT_KEYBOARD_HEIGHT : CGFloat = 216;
    static let LANDSCAPE_KEYBOARD_HEIGHT : CGFloat = 162;
}


  func textFieldDidBeginEditing(textField: UITextField) {
    let textFieldRect : CGRect = self.view.window!.convertRect(textField.bounds, fromView: textField)
    let viewRect : CGRect = self.view.window!.convertRect(self.view.bounds, fromView: self.view)

    let midline : CGFloat = textFieldRect.origin.y + 0.5 * textFieldRect.size.height
    let numerator : CGFloat = midline - viewRect.origin.y - MoveKeyboard.MINIMUM_SCROLL_FRACTION * viewRect.size.height
    let denominator : CGFloat = (MoveKeyboard.MAXIMUM_SCROLL_FRACTION - MoveKeyboard.MINIMUM_SCROLL_FRACTION) * viewRect.size.height
    var heightFraction : CGFloat = numerator / denominator

    if heightFraction < 0.0 {
        heightFraction = 0.0
    } else if heightFraction > 1.0 {
        heightFraction = 1.0
    }

    let orientation : UIInterfaceOrientation = UIApplication.sharedApplication().statusBarOrientation
    if (orientation == UIInterfaceOrientation.Portrait || orientation == UIInterfaceOrientation.PortraitUpsideDown) {
        animateDistance = floor(MoveKeyboard.PORTRAIT_KEYBOARD_HEIGHT * heightFraction)
    } else {
        animateDistance = floor(MoveKeyboard.LANDSCAPE_KEYBOARD_HEIGHT * heightFraction)
    }

    var viewFrame : CGRect = self.view.frame
    viewFrame.origin.y -= animateDistance

    UIView.beginAnimations(nil, context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)
    UIView.setAnimationDuration(NSTimeInterval(MoveKeyboard.KEYBOARD_ANIMATION_DURATION))

    self.view.frame = viewFrame

    UIView.commitAnimations()
}


func textFieldDidEndEditing(textField: UITextField) {
    var viewFrame : CGRect = self.view.frame
    viewFrame.origin.y += animateDistance

    UIView.beginAnimations(nil, context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)

    UIView.setAnimationDuration(NSTimeInterval(MoveKeyboard.KEYBOARD_ANIMATION_DURATION))

    self.view.frame = viewFrame

    UIView.commitAnimations()

}

And Lastly since we are using delegates methods

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

refactored from using objective-c http://www.cocoawithlove.com/2008/10/sliding-uitextfields-around-to-avoid.html

Solomon Ayoola
  • 2,299
  • 2
  • 15
  • 21
  • This solution worked for me, although I had to do some additional work: declare an `var animateDistance: CGFloat!` plus I had to handle the UIKeyboardWillHideNotification for when the user presses the hide keyboard button. – Rhuantavan Jun 10 '15 at 09:46
4

Another solution that doesn't depend on autolayout, constraints or any outlets. What you do need is your field(s) in a scrollview.

override func viewDidLoad() {
    super.viewDidLoad()

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

func makeSpaceForKeyboard(notification: NSNotification) {
    let info = notification.userInfo!
    let keyboardHeight:CGFloat = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue().size.height
    let duration:Double = info[UIKeyboardAnimationDurationUserInfoKey] as! Double

    if notification.name == UIKeyboardWillShowNotification {
        UIView.animateWithDuration(duration, animations: { () -> Void in
            var frame = self.view.frame
            frame.size.height = frame.size.height - keyboardHeight
            self.view.frame = frame
        })
    } else {
        UIView.animateWithDuration(duration, animations: { () -> Void in
            var frame = self.view.frame
            frame.size.height = frame.size.height + keyboardHeight
            self.view.frame = frame
        })
    }

}
Simpa
  • 483
  • 5
  • 9
4

Here is my version for a solution for Swift 2.2:

First register for Keyboard Show/Hide Notifications

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

Then in methods coresponding for those notifications move the main view up or down

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

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

The trick is in the "keyboardWillShow" part which get calls every time "QuickType Suggestion Bar" is expanded or collapsed. Then we always set the y coordinate of the main view which equals the negative value of total keyboard height (with or without the "QuickType bar" portion).

At the end do not forget to remove observers

deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
Pavle Mijatovic
  • 733
  • 8
  • 6
3

The following is a simple solution, whereby the text field has a constraint tying it to the bottom layout guide. It simply adds the keyboard height to the constraint's constant.

// This constraint ties the text field to the bottom layout guide
@IBOutlet var textFieldToBottomLayoutGuideConstraint: NSLayoutConstraint!

override func viewDidLoad() {
    super.viewDidLoad()

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

func keyboardWillShow(sender: NSNotification) {
    if let keyboardSize = (sender.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        self.textFieldToBottomLayoutGuideConstraint?.constant += keyboardSize.height
    }
}

func keyboardWillHide(sender: NSNotification) {
    if let keyboardSize = (sender.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        self.textFieldToBottomLayoutGuideConstraint?.constant -= keyboardSize.height
    }
}
echessa
  • 49
  • 2
  • 8
3

Well, I think i might be too late but i found another simple version of Saqib's answer. I'm using Autolayout with constraints. I have a small view inside of another main view with username and password fields. Instead of changing the y coordinate of the view i'm saving the original constraint value in a variable and changing the constraint's constant to some value and again after the keyboard dismisses, i'm setting up the constraint to original one. This way it avoids the problem Saqib's answer has, (The view keeps on moving up and does not stop). Below is my code...

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);
    self.originalConstraint = self.centerYConstraint.constant
  }

  func keyboardWillShow(sender: NSNotification) {
    self.centerYConstraint.constant += 30
  }

  func keyboardWillHide(sender: NSNotification) {
    self.centerYConstraint.constant = self.originalConstraint
  }
Sashi
  • 493
  • 3
  • 6
  • 19
  • Inside **keyboardWillShow** method check for the condition **if self.centerYConstraint.constant == self.originalCenterYConstraint** then have that one line of code in between this condition. OriginalCenterYContraint is the original value of centerYContraint that I'm storing in viewdidload. This worked for me. – Sashi Apr 16 '15 at 15:18
3

Swift 4.x answer, merging answers from @Joseph Lord and @Isuru. bottomConstraint represents the bottom constraint of the view you're interested in moving.

override func viewDidLoad() {
    // Call super
    super.viewDidLoad()

    // Subscribe to keyboard notifications
    NotificationCenter.default.addObserver(self,
                                           selector: #selector(keyboardNotification(notification:)),
                                           name: UIResponder.keyboardWillChangeFrameNotification,
                                           object: nil)        
}


deinit {
    NotificationCenter.default.removeObserver(self)
}


@objc func keyboardNotification(notification: NSNotification) {
    if let userInfo = notification.userInfo {
        // Get keyboard frame
        let keyboardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue

        // Set new bottom constraint constant
        let bottomConstraintConstant = keyboardFrame.origin.y >= UIScreen.main.bounds.size.height ? 0.0 : keyboardFrame.size.height

        // Set animation properties
        let duration = (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
        let animationCurveRawNSN = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber
        let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue
        let animationCurve = UIView.AnimationOptions(rawValue: animationCurveRaw)

        // Animate the view you care about
        UIView.animate(withDuration: duration, delay: 0, options: animationCurve, animations: {
            self.bottomConstraint.constant = bottomConstraintConstant
            self.view.layoutIfNeeded()
        }, completion: nil)
    }
}
Crashalot
  • 31,452
  • 56
  • 235
  • 393
3

Very Simple and no need to code more. Just add pod 'IQKeyboardManagerSwift' in your podfile, and in your AppDelegate page add code below.

import IQKeyboardManagerSwift

and in method didFinishLaunchingWithOptions() type

IQKeyboardManager.shared.enable = true

that is it. check this video link for better understanding https://youtu.be/eOM94K1ZWN8 Hope this will help you.

RajeshKumar R
  • 13,989
  • 2
  • 35
  • 63
Raghib Arshi
  • 503
  • 4
  • 10
  • Does it work for TextView and how can I change title for return key "Done" ? – tdt kien Jan 19 '20 at 21:08
  • Goto:- "IQKeyboardManager.m" Replace this line(Line No. 968):- [textField addDoneOnKeyboardWithTarget:self action:@selector(doneAction:) shouldShowPlaceholder:_shouldShowTextFieldPlaceholder] with this:- [textField addRightButtonOnKeyboardWithText:@"YOURTEXT" target:self action:@selector(doneAction:) shouldShowPlaceholder:_shouldShowTextFieldPlaceholder]; And haven't tried yet for the textview, hope this would help you. – Raghib Arshi Jan 20 '20 at 01:43
3

Complete code for managing keyboard.

        override func viewWillAppear(_ animated: Bool) {
            NotificationCenter.default.addObserver(self, selector: #selector(StoryMediaVC.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
            NotificationCenter.default.addObserver(self, selector: #selector(StoryMediaVC.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
        }
        override func viewWillDisappear(_ animated: Bool) {
            NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
            NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
        }
        @objc func keyboardWillShow(notification: NSNotification) {
            guard let userInfo = notification.userInfo else {return}
            guard let keyboardSize = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {return}
            let keyboardFrame = keyboardSize.cgRectValue

            if self.view.bounds.origin.y == 0{
                self.view.bounds.origin.y += keyboardFrame.height
            }
        }


        @objc func keyboardWillHide(notification: NSNotification) {
            if self.view.bounds.origin.y != 0 {
                self.view.bounds.origin.y = 0
            }
        }
Avijit Nagare
  • 7,801
  • 7
  • 34
  • 62
3

A Swift 5 solution for frédéric-adda :


protocol KeyboardHandler: class {
    var bottomConstraint: NSLayoutConstraint! { get set }
    
    func keyboardWillShow(_ notification: Notification)
    func keyboardWillHide(_ notification: Notification)
    func startObservingKeyboardChanges()
    func stopObservingKeyboardChanges()
}


extension KeyboardHandler where Self: UIViewController {

    func startObservingKeyboardChanges() {

        // NotificationCenter observers
        NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: nil) { [weak self] notification in
          self?.keyboardWillShow(notification)
        }

        // Deal with rotations
        NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillChangeFrameNotification, object: nil, queue: nil) { [weak self] notification in
          self?.keyboardWillShow(notification)
        }

        // Deal with keyboard change (emoji, numerical, etc.)
        NotificationCenter.default.addObserver(forName: UITextInputMode.currentInputModeDidChangeNotification, object: nil, queue: nil) { [weak self] notification in
          self?.keyboardWillShow(notification)
        }

        NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: nil) { [weak self] notification in
          self?.keyboardWillHide(notification)
        }
    }


    func keyboardWillShow(_ notification: Notification) {

      let verticalPadding: CGFloat = 20 // Padding between the bottom of the view and the top of the keyboard

        guard let value = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }
      let keyboardHeight = value.cgRectValue.height

      // Here you could have more complex rules, like checking if the textField currently selected is actually covered by the keyboard, but that's out of this scope.
      self.bottomConstraint.constant = keyboardHeight + verticalPadding

      UIView.animate(withDuration: 0.1, animations: { () -> Void in
          self.view.layoutIfNeeded()
      })
  }


  func keyboardWillHide(_ notification: Notification) {
      self.bottomConstraint.constant = 0

      UIView.animate(withDuration: 0.1, animations: { () -> Void in
          self.view.layoutIfNeeded()
      })
  }


  func stopObservingKeyboardChanges() {
      NotificationCenter.default.removeObserver(self)
  }
}

In any UIViewController:

  • Conform to KeyboardHandler protocol
extension AnyViewController: KeyboardHandler {} 
  • Add bottom constraint for the last-bottom element on the screen.
@IBOutlet var bottomConstraint: NSLayoutConstraint! 
  • Add Observer subscribe/unsubscribe:
 override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        startObservingKeyboardChanges()
 }
    
 override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        stopObservingKeyboardChanges()
 }

Enjoy!

Meseery
  • 1,667
  • 2
  • 19
  • 19
2

I have done by following manner:

This is useful when textfield superview is view

class AdminLoginViewController: UIViewController,
UITextFieldDelegate{

    @IBOutlet weak var txtUserName: UITextField!
    @IBOutlet weak var txtUserPassword: UITextField!
    @IBOutlet weak var btnAdminLogin: UIButton!

    private var activeField : UIView?

    var param:String!
    var adminUser : Admin? = nil
    var kbHeight: CGFloat!

    override func viewDidLoad()
    {
        self.addKeyBoardObserver()
        self.addGestureForHideKeyBoard()
    }

    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    func addGestureForHideKeyBoard()
    {
        let tapGesture = UITapGestureRecognizer(target: self, action: Selector("hideKeyboard"))
        tapGesture.cancelsTouchesInView = false
        view.addGestureRecognizer(tapGesture)
    }

    func hideKeyboard() {
        self.view.endEditing(true)
    }

    func addKeyBoardObserver(){

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

    func removeObserver(){
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }

    //MARK:- textfiled Delegate

    func textFieldShouldBeginEditing(textField: UITextField) -> Bool
    {
         activeField = textField

        return true
    }
    func textFieldShouldEndEditing(textField: UITextField) -> Bool
    {
        if activeField == textField
        {
            activeField = nil
        }

        return true
    }

    func textFieldShouldReturn(textField: UITextField) -> Bool {

        if txtUserName == textField
        {
            txtUserPassword.becomeFirstResponder()
        }
        else if (textField == txtUserPassword)
        {
            self.btnAdminLoginAction(nil)
        }
        return true;
    }

    func willChangeKeyboardFrame(aNotification : NSNotification)
    {
       if self.activeField != nil && self.activeField!.isFirstResponder()
    {
        if let keyboardSize =  (aNotification.userInfo![UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue()
        {
            let dy = (self.activeField?.superview?.convertRect((self.activeField?.frame)!, toView: view).origin.y)!

            let height = (self.view.frame.size.height - keyboardSize.size.height)

            if dy > height
            {
                var frame = self.view.frame

                frame.origin.y = -((dy - height) + (self.activeField?.frame.size.height)! + 20)

                self.view.frame = frame
            }
        }
    }
    else
    {
        var frame = self.view.frame
        frame.origin.y = 0
        self.view.frame = frame
    }
    } }
Krishna Gawade
  • 151
  • 1
  • 6
2
    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

    }
David
  • 3,055
  • 1
  • 31
  • 48
Kamalkumar.E
  • 264
  • 2
  • 11
  • 1
    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:05
1

I have done in following manner :

class SignInController: UIViewController , UITextFieldDelegate {

@IBOutlet weak var scrollView: UIScrollView!

// outlet declartion
@IBOutlet weak var signInTextView: UITextField!

var kbHeight: CGFloat!

/**
*
* @method viewDidLoad
*
*/

override func viewDidLoad() {
    super.viewDidLoad()

    self.signInTextView.delegate = self

}// end viewDidLoad

/**
*
* @method viewWillAppear
*
*/

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)

}// end viewWillAppear

/**
*
* @method viewDidAppear
*
*/

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)


}// end viewDidAppear

/**
*
* @method viewWillDisappear
*
*/
override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

/**
*
* @method textFieldShouldReturn
* retun the keyboard value
*
*/

// MARK -
func textFieldShouldReturn(textField: UITextField) -> Bool {
    signInTextView.resignFirstResponder()
    return true;

}// end textFieldShouldReturn

// MARK - keyboardWillShow
func keyboardWillShow(notification: NSNotification) {
    if let userInfo = notification.userInfo {
        if let keyboardSize =  (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
            kbHeight = keyboardSize.height
            self.animateTextField(true)
        }
    }
}// end keyboardWillShow

// MARK - keyboardWillHide
func keyboardWillHide(notification: NSNotification) {
    self.animateTextField(false)
}// end keyboardWillHide

// MARK - animateTextField
func animateTextField(up: Bool) {
    var movement = (up ? -kbHeight : kbHeight)

    UIView.animateWithDuration(0.3, animations: {
        self.view.frame = CGRectOffset(self.view.frame, 0, movement)
    })
}// end animateTextField

/**
*
* @method didReceiveMemoryWarning
*
*/

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.

}// end didReceiveMemoryWarning


}// end SignInController
Vinod Joshi
  • 7,094
  • 46
  • 49
1

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
  • 381
  • 3
  • 22
1

Here is a generic solution for all TextField Steps -

1) Create a common ViewController that is extended by other ViewControllers

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.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        if self.view.frame.origin.y == 0 {
            self.view.frame.origin.y -= getMoveableDistance(keyboarHeight: keyboardSize.height)
        }
    }
}

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

//get the distance to move up the main view for the focus textfiled
func getMoveableDistance(keyboarHeight : CGFloat) ->  CGFloat{
    var y:CGFloat = 0.0
    if let activeTF = getSelectedTextField(){
        var tfMaxY = activeTF.frame.maxY
        var containerView = activeTF.superview!
        while containerView.frame.maxY != self.view.frame.maxY{
            let contViewFrm = containerView.convert(activeTF.frame, to: containerView.superview)
            tfMaxY = tfMaxY + contViewFrm.minY
            containerView = containerView.superview!
        }
        let keyboardMinY = self.view.frame.height - keyboarHeight
        if tfMaxY > keyboardMinY{
            y = (tfMaxY - keyboardMinY) + 10.0
        }
    }

    return y
}

2) Create a extension of UIViewController and the currently active TextField

//get active text field

extension UIViewController { func getSelectedTextField() -> UITextField? {

    let totalTextFields = getTextFieldsInView(view: self.view)

    for textField in totalTextFields{
        if textField.isFirstResponder{
            return textField
        }
    }

    return nil

}

func getTextFieldsInView(view: UIView) -> [UITextField] {

    var totalTextFields = [UITextField]()

    for subview in view.subviews as [UIView] {
        if let textField = subview as? UITextField {
            totalTextFields += [textField]
        } else {
            totalTextFields += getTextFieldsInView(view: subview)
        }
    }

    return totalTextFields
}

}

  • For some reason, I was having an issue in keyboardWillShow function, the keyboardSize was getting wrong size after the first keyboard toggle (the first toggle has correct frame). I fixed this by changing it to guard let userInfo = notification.userInfo else {return} guard let keyboardSize = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {return} let keyboardFrame = keyboardSize.cgRectValue if self.view.frame.origin.y == 0 { self.view.frame.origin.y -= getMoveableDistance(keyboarHeight: keyboardFrame.height) } .Hope this will help if someone got the same issue :) – Youstanzr Mar 22 '20 at 05:54
1

For anyone not using storyboards to set the layout constraint. This is a pure programmatic way to get it working on Swift 5:

  1. Define an empty constraint in your viewController. In this case, I am building a chat app that has a UIView containing text view at the very bottom of the screen.
var discussionsMessageBoxBottomAnchor: NSLayoutConstraint = NSLayoutConstraint()
  1. In viewDidLoad add the constraints for the bottom-most view. In this case discussionsMessageBox. Also, add the listener for keyboard events.

For correctly initializing the constraint, you need to first add the subview and then define the constraint.

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

view.addSubview(discussionsMessageBox)

if #available(iOS 11.0, *) {
    discussionsMessageBoxBottomAnchor = discussionsMessageBox.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0)
} else {
    // Fallback on earlier versions
    discussionsMessageBoxBottomAnchor = discussionsMessageBox.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0)
}

NSLayoutConstraint.activate([ discussionsMessageBoxBottomAnchor ])

  1. Define deinit.
deinit {
        NotificationCenter.default.removeObserver(self)
    }
  1. Next add the code that @JosephLord defined, with one correction to fix offset math.
extension DiscussionsViewController {
    @objc func keyboardNotification(notification: NSNotification) {
        guard let userInfo = notification.userInfo else { return }
        
        let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
        let endFrameY = endFrame?.origin.y ?? 0
        let duration:TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
        let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
        let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue
        let animationCurve:UIView.AnimationOptions = UIView.AnimationOptions(rawValue: animationCurveRaw)
        
        if endFrameY >= UIScreen.main.bounds.size.height {
            self.discussionsMessageBoxBottomAnchor.constant = 0.0
        } else {
            //Changed line
            self.discussionsMessageBoxBottomAnchor.constant = -1 * (endFrame?.size.height ?? 0.0)
        }
        
        UIView.animate(
            withDuration: duration,
            delay: TimeInterval(0),
            options: animationCurve,
            animations: { self.view.layoutIfNeeded() },
            completion: nil)
    }
}
Parth Tamane
  • 1,611
  • 10
  • 24
1

Extremely easy to install Swift or Objective-C.

For Swift 4 And 5 and Other Update Version

Here how it works:

IQKeyboardManager (Swift):- IQKeyboardManagerSwift is available through Cocoapods, to install it simply add the following line to your Podfile:

pod 'IQKeyboardManagerSwift'

In AppDelegate.swift, just import IQKeyboardManagerSwift framework and enable IQKeyboardManager.

import IQKeyboardManagerSwift

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

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

    IQKeyboardManager.sharedManager().enable = true
    
    IQKeyboardManager.shared.enable = true


    return true
    }
}
Chandan
  • 91
  • 6
0

I modified @Simpa solution a little bit.........

override func viewDidLoad() 
{

    super.viewDidLoad()
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("makeSpaceForKeyboard:"), name:UIKeyboardWillShowNotification, object: nil);
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("makeSpaceForKeyboard:"), name:UIKeyboardWillHideNotification, object: nil);
}

deinit{
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

var keyboardIsVisible = false
override func makeSpaceForKeyboard(notification: NSNotification) {

    let info = notification.userInfo!
    let keyboardHeight:CGFloat = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue().size.height
    let duration:Double = info[UIKeyboardAnimationDurationUserInfoKey] as! Double

    if notification.name == UIKeyboardWillShowNotification && keyboardIsVisible == false{

        keyboardIsVisible = true

        UIView.animateWithDuration(duration, animations: { () -> Void in
            var frame = self.view.frame
            frame.size.height = frame.size.height - keyboardHeight
            self.view.frame = frame
        })

    } else if keyboardIsVisible == true && notification.name == UIKeyboardWillShowNotification{

    }else {
        keyboardIsVisible = false

        UIView.animateWithDuration(duration, animations: { () -> Void in
            var frame = self.view.frame
            frame.size.height = frame.size.height + keyboardHeight
            self.view.frame = frame
        })
    }
}
Cloy
  • 2,051
  • 18
  • 30
0

None of them worked for and I ended up using content insets to move my view up when the keyboard appears.

Note: I was using a UITableView

Referenced solution @ keyboard-content-offset which was entirely written in objective C, the below solution is clean Swift.

Add the notification observer @ viewDidLoad()

NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(yourClass.keyboardWillBeShown), name:UIKeyboardWillShowNotification, object: nil);
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(yourClass.keyboardWillBeHidden), name:UIKeyboardWillHideNotification, object: nil);

To get the keyboard size, we first get the userInfo dictionary from the notification object, which stores any additional objects that our receiver might use.

From that dictionary we can get the CGRect object describing the keyboard’s frame by using the key UIKeyboardFrameBeginUserInfoKey.

Apply the content inset for the table view @ keyboardWillBeShown method,

func keyboardWillBeShown(sender: NSNotification)
{        
    // Move the table view

    if let keyboardSize = (sender.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue()
    {
        let contentInsets = UIEdgeInsetsMake(0.0, 0.0, (keyboardSize.height), 0.0);

        yourTableView.contentInset = contentInsets;

        yourTableView.scrollIndicatorInsets = contentInsets;
    }
}

Restore the view @ keyboardWillBeHidden method

func keyboardWillBeHidden(sender: NSNotification)
{
    // Moving back the table view back to the default position

    yourTableView.contentInset = UIEdgeInsetsZero;

    yourTableView.scrollIndicatorInsets = UIEdgeInsetsZero;
}

If you want to keep the device orientation also into consideration, use conditional statements to tailor the code to your needs.

// Portrait
UIEdgeInsetsMake(0.0, 0.0, (keyboardSize.height), 0.0);

// Landscape
UIEdgeInsetsMake(0.0, 0.0, (keyboardSize.width), 0.0);
Vikram Ezhil
  • 965
  • 8
  • 14
0
override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    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:Notification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0)
    }
}

func keyboardWillHide(_ notification:Notification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
    }
}

enter image description here

Nik Kov
  • 10,605
  • 4
  • 56
  • 96
Zany
  • 815
  • 1
  • 7
  • 4
0

The Swift 4 solution I use, takes in keyboard size. Replace serverStatusStackView with whatever view you care about, e.g.: self.view:

deinit {
    NotificationCenter.default.removeObserver(self)
}

@objc func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        serverStatusStackView.frame.origin.y = keyboardSize.height * 2 - serverStatusStackView.frame.height
    }
}

@objc func keyboardWillHide(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        serverStatusStackView.frame.origin.y += keyboardSize.height
    }
}

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

    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
A T
  • 10,508
  • 14
  • 85
  • 137
0

The best way is using NotificationCenter to catch keyboard actions. You can follow the steps in this short article https://medium.com/@demirciy/keyboard-handling-deb1a96a8207

Yusuf
  • 153
  • 1
  • 5
0

Swift 4.1,

Use TPKeyBoardAvoiding class for achieving this. This works fine with UIScrollView, UICollectionView, UITableView.

Just assign this class to your scrollview, collectionview or tableview in storyboard or create its object programmatically. All textfield or textviews inside TPKeyboardAvoiding scrollview will adjust automatically when the keyboard appears and disappears.

Here is the link for TPKeyboardAvoiding

TPKeyboardAvoiding for Swift 4.1,

import Foundation
import UIKit

// MARK: - TableView
class TPKeyboardAvoidingTableView:UITableView,UITextFieldDelegate, UITextViewDelegate {

    override var frame:CGRect{
        willSet{
            super.frame = frame
        }

        didSet{
            if hasAutomaticKeyboardAvoidingBehaviour() {return}
            TPKeyboardAvoiding_updateContentInset()
        }
    }

    override var contentSize:CGSize{
        willSet(newValue){
            if hasAutomaticKeyboardAvoidingBehaviour() {
                super.contentSize = newValue
                return
            }

            if newValue.equalTo(self.contentSize)
            {
                return
            }

            super.contentSize = newValue
            self.TPKeyboardAvoiding_updateContentInset()
        }

        //        didSet{
        //            self.TPKeyboardAvoiding_updateContentInset()
        //        }
    }


    override init(frame: CGRect, style: UITableViewStyle) {
        super.init(frame: frame, style: style)
        self.setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.setup()
    }

    override func awakeFromNib() {
        setup()
    }

    deinit{
        NotificationCenter.default.removeObserver(self)
    }

    func hasAutomaticKeyboardAvoidingBehaviour()->Bool
    {
        if #available(iOS 8.3, *) {
            if self.delegate is UITableViewController
            {
                return true
            }
        }

        return false
    }

    func focusNextTextField()->Bool
    {
        return self.TPKeyboardAvoiding_focusNextTextField()
    }

    @objc func scrollToActiveTextField()
    {
        return self.TPKeyboardAvoiding_scrollToActiveTextField()
    }

    override func willMove(toSuperview newSuperview: UIView?) {
        super.willMove(toSuperview: newSuperview)
        if newSuperview != nil {
            NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.TPKeyboardAvoiding_findFirstResponderBeneathView(self)?.resignFirstResponder()
        super.touchesEnded(touches, with: event)
    }

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

    override func layoutSubviews() {
        super.layoutSubviews()
        NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)

        Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), userInfo: nil, repeats: false)
    }
}

private extension TPKeyboardAvoidingTableView
{
    func setup()
    {
        if self.hasAutomaticKeyboardAvoidingBehaviour() { return }

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(TPKeyboardAvoiding_keyboardWillShow(_:)),
                                               name: NSNotification.Name.UIKeyboardWillChangeFrame,
                                               object: nil)

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(TPKeyboardAvoiding_keyboardWillHide(_:)),
                                               name: NSNotification.Name.UIKeyboardWillHide,
                                               object: nil)

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(scrollToActiveTextField),
                                               name: NSNotification.Name.UITextViewTextDidBeginEditing,
                                               object: nil)

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(scrollToActiveTextField),
                                               name: NSNotification.Name.UITextFieldTextDidBeginEditing,
                                               object: nil)
    }
}

// MARK: - CollectionView
class TPKeyboardAvoidingCollectionView:UICollectionView,UITextViewDelegate {

    override var contentSize:CGSize{
        willSet(newValue){
            if newValue.equalTo(self.contentSize)
            {
                return
            }

            super.contentSize = newValue
            self.TPKeyboardAvoiding_updateContentInset()
        }

        //        didSet{
        //            self.TPKeyboardAvoiding_updateContentInset()
        //        }
    }


    override var frame:CGRect{
        willSet{
            super.frame = frame
        }

        didSet{
            self.TPKeyboardAvoiding_updateContentInset()
        }
    }

    //    override init(frame: CGRect) {
    //        super.init(frame: frame)
    //    }

    override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
        super.init(frame: frame, collectionViewLayout: layout)
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        //        fatalError("init(coder:) has not been implemented")
        super.init(coder: aDecoder)
        self.setup()

    }

    override func awakeFromNib() {
        setup()
    }

    deinit{
        NotificationCenter.default.removeObserver(self)
    }

    func focusNextTextField()->Bool
    {
        return self.TPKeyboardAvoiding_focusNextTextField()
    }

    @objc func scrollToActiveTextField()
    {
        return self.TPKeyboardAvoiding_scrollToActiveTextField()
    }

    override func willMove(toSuperview newSuperview: UIView?) {
        super.willMove(toSuperview: newSuperview)
        if newSuperview != nil {
            NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.TPKeyboardAvoiding_findFirstResponderBeneathView(self)?.resignFirstResponder()
        super.touchesEnded(touches, with: event)
    }

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

    override func layoutSubviews() {
        super.layoutSubviews()
        NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)

        Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), userInfo: nil, repeats: false)
    }
}

private extension TPKeyboardAvoidingCollectionView
{
    func setup()
    {
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(TPKeyboardAvoiding_keyboardWillShow(_:)),
                                               name: NSNotification.Name.UIKeyboardWillChangeFrame,
                                               object: nil)

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(TPKeyboardAvoiding_keyboardWillHide(_:)),
                                               name: NSNotification.Name.UIKeyboardWillHide,
                                               object: nil)

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(scrollToActiveTextField),
                                               name: NSNotification.Name.UITextViewTextDidBeginEditing,
                                               object: nil)

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(scrollToActiveTextField),
                                               name: NSNotification.Name.UITextFieldTextDidBeginEditing,
                                               object: nil)
    }
}

// MARK: - ScrollView
class TPKeyboardAvoidingScrollView:UIScrollView,UITextFieldDelegate,UITextViewDelegate
{
    override var contentSize:CGSize{
        didSet{
            self.TPKeyboardAvoiding_updateFromContentSizeChange()
        }
    }


    override var frame:CGRect{
        didSet{
            self.TPKeyboardAvoiding_updateContentInset()
        }
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.setup()
    }

    override func awakeFromNib() {
        setup()
    }

    func contentSizeToFit()
    {
        self.contentSize = self.TPKeyboardAvoiding_calculatedContentSizeFromSubviewFrames()
    }

    func focusNextTextField() ->Bool
    {
        return self.TPKeyboardAvoiding_focusNextTextField()
    }

    @objc func scrollToActiveTextField()
    {
        return self.TPKeyboardAvoiding_scrollToActiveTextField()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.setup()
    }

    deinit{
        NotificationCenter.default.removeObserver(self)
    }

    override func willMove(toSuperview newSuperview: UIView?) {
        super.willMove(toSuperview: newSuperview)
        if newSuperview != nil {
            NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.TPKeyboardAvoiding_findFirstResponderBeneathView(self)?.resignFirstResponder()
        super.touchesEnded(touches, with: event)
    }

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

    override func layoutSubviews() {
        super.layoutSubviews()
        NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), object: self)

        Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_:)), userInfo: nil, repeats: false)
    }
}

private extension TPKeyboardAvoidingScrollView
{
    func setup()
    {
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(TPKeyboardAvoiding_keyboardWillShow(_:)),
                                               name: NSNotification.Name.UIKeyboardWillChangeFrame,
                                               object: nil)

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(TPKeyboardAvoiding_keyboardWillHide(_:)),
                                               name: NSNotification.Name.UIKeyboardWillHide,
                                               object: nil)

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(scrollToActiveTextField),
                                               name: NSNotification.Name.UITextViewTextDidBeginEditing,
                                               object: nil)

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(scrollToActiveTextField),
                                               name: NSNotification.Name.UITextFieldTextDidBeginEditing,
                                               object: nil)
    }
}

// MARK: - Process Event
let kCalculatedContentPadding:CGFloat = 10;
let kMinimumScrollOffsetPadding:CGFloat = 20;

extension UIScrollView
{
    @objc func TPKeyboardAvoiding_keyboardWillShow(_ notification:Notification)
    {
        guard let userInfo = notification.userInfo else { return }
        guard let rectNotification = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue else
        {
            return
        }

        let keyboardRect = self.convert(rectNotification.cgRectValue , from: nil)
        if keyboardRect.isEmpty
        {
            return
        }

        let state = self.keyboardAvoidingState()

        guard let firstResponder = self.TPKeyboardAvoiding_findFirstResponderBeneathView(self) else { return}
        state.keyboardRect = keyboardRect
        if !state.keyboardVisible
        {
            state.priorInset = self.contentInset
            state.priorScrollIndicatorInsets = self.scrollIndicatorInsets
            state.priorPagingEnabled = self.isPagingEnabled
        }

        state.keyboardVisible = true
        self.isPagingEnabled = false

        if self is TPKeyboardAvoidingScrollView
        {
            state.priorContentSize = self.contentSize
            if self.contentSize.equalTo(CGSize.zero)
            {
                self.contentSize = self.TPKeyboardAvoiding_calculatedContentSizeFromSubviewFrames()
            }
        }

        let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Float ?? 0.0
        let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? Int ?? 0
        let options = UIViewAnimationOptions(rawValue: UInt(curve))

        UIView.animate(withDuration: TimeInterval(duration),
                       delay: 0,
                       options: options,
                       animations: { [weak self]() -> Void in
                        if let actualSelf = self
                        {
                            actualSelf.contentInset = actualSelf.TPKeyboardAvoiding_contentInsetForKeyboard()
                            let viewableHeight = actualSelf.bounds.size.height - actualSelf.contentInset.top - actualSelf.contentInset.bottom
                            let point = CGPoint(x: actualSelf.contentOffset.x, y: actualSelf.TPKeyboardAvoiding_idealOffsetForView(firstResponder, viewAreaHeight: viewableHeight))
                            actualSelf.setContentOffset(point, animated: false)

                            actualSelf.scrollIndicatorInsets = actualSelf.contentInset
                            actualSelf.layoutIfNeeded()
                        }

        }) { (finished) -> Void in

        }
    }

    @objc func TPKeyboardAvoiding_keyboardWillHide(_ notification:Notification)
    {
        guard let userInfo = notification.userInfo else { return }

        guard let rectNotification = userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue else
        {
            return
        }
        let keyboardRect = self.convert(rectNotification.cgRectValue , from: nil)
        if keyboardRect.isEmpty
        {
            return
        }
        let state = self.keyboardAvoidingState()

        if !state.keyboardVisible
        {
            return
        }
        state.keyboardRect = CGRect.zero
        state.keyboardVisible = false

        let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Float ?? 0.0
        let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? Int ?? 0
        let options = UIViewAnimationOptions(rawValue: UInt(curve))

        UIView.animate(withDuration: TimeInterval(duration),
                       delay: 0,
                       options: options,
                       animations: { [weak self]() -> Void in
                        if let actualSelf = self
                        {
                            if actualSelf is TPKeyboardAvoidingScrollView {
                                actualSelf.contentSize = state.priorContentSize
                                actualSelf.contentInset = state.priorInset
                                actualSelf.scrollIndicatorInsets = state.priorScrollIndicatorInsets
                                actualSelf.isPagingEnabled = state.priorPagingEnabled
                                actualSelf.layoutIfNeeded()
                            }
                        }

        }) { (finished) -> Void in

        }
    }

    func TPKeyboardAvoiding_updateFromContentSizeChange()
    {
        let state = self.keyboardAvoidingState()
        if state.keyboardVisible
        {
            state.priorContentSize = self.contentSize
        }
    }

    func TPKeyboardAvoiding_focusNextTextField() ->Bool
    {
        guard let firstResponder = self.TPKeyboardAvoiding_findFirstResponderBeneathView(self) else { return false}
        guard let view = self.TPKeyboardAvoiding_findNextInputViewAfterView(firstResponder, beneathView: self) else { return false}
        Timer.scheduledTimer(timeInterval: 0.1, target: view, selector: #selector(becomeFirstResponder), userInfo: nil, repeats: false)

        return true

    }

    func TPKeyboardAvoiding_scrollToActiveTextField()
    {
        let state = self.keyboardAvoidingState()

        if !state.keyboardVisible { return }

        let visibleSpace = self.bounds.size.height - self.contentInset.top - self.contentInset.bottom

        let idealOffset = CGPoint(x: 0,
                                  y: self.TPKeyboardAvoiding_idealOffsetForView(self.TPKeyboardAvoiding_findFirstResponderBeneathView(self),
                                                                                viewAreaHeight: visibleSpace))

        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double((Int64)(0 * NSEC_PER_SEC)) / Double(NSEC_PER_SEC)) {[weak self] () -> Void in
            self?.setContentOffset(idealOffset, animated: true)
        }
    }

    //Helper
    func TPKeyboardAvoiding_findFirstResponderBeneathView(_ view:UIView) -> UIView?
    {
        for childView in view.subviews
        {
            if childView.responds(to: #selector(getter: isFirstResponder)) && childView.isFirstResponder
            {
                return childView
            }
            let result = TPKeyboardAvoiding_findFirstResponderBeneathView(childView)
            if result != nil
            {
                return result
            }
        }
        return nil
    }

    func TPKeyboardAvoiding_updateContentInset()
    {
        let state = self.keyboardAvoidingState()
        if state.keyboardVisible
        {
            self.contentInset = self.TPKeyboardAvoiding_contentInsetForKeyboard()
        }
    }

    func TPKeyboardAvoiding_calculatedContentSizeFromSubviewFrames() ->CGSize
    {
        let wasShowingVerticalScrollIndicator = self.showsVerticalScrollIndicator
        let wasShowingHorizontalScrollIndicator = self.showsHorizontalScrollIndicator

        self.showsVerticalScrollIndicator = false
        self.showsHorizontalScrollIndicator = false

        var rect = CGRect.zero

        for view in self.subviews
        {
            rect = rect.union(view.frame)
        }

        rect.size.height += kCalculatedContentPadding
        self.showsVerticalScrollIndicator = wasShowingVerticalScrollIndicator
        self.showsHorizontalScrollIndicator = wasShowingHorizontalScrollIndicator

        return rect.size
    }

    func TPKeyboardAvoiding_idealOffsetForView(_ view:UIView?,viewAreaHeight:CGFloat) -> CGFloat
    {
        let contentSize = self.contentSize

        var offset:CGFloat = 0.0
        let subviewRect =  view != nil ? view!.convert(view!.bounds, to: self) : CGRect.zero

        var padding = (viewAreaHeight - subviewRect.height)/2
        if padding < kMinimumScrollOffsetPadding
        {
            padding = kMinimumScrollOffsetPadding
        }

        offset = subviewRect.origin.y - padding - self.contentInset.top

        if offset > (contentSize.height - viewAreaHeight)
        {
            offset = contentSize.height - viewAreaHeight
        }

        if offset < -self.contentInset.top
        {
            offset = -self.contentInset.top
        }

        return offset
    }

    func TPKeyboardAvoiding_contentInsetForKeyboard() -> UIEdgeInsets
    {
        let state = self.keyboardAvoidingState()
        var newInset = self.contentInset;

        let keyboardRect = state.keyboardRect
        newInset.bottom = keyboardRect.size.height - max(keyboardRect.maxY - self.bounds.maxY, 0)

        return newInset

    }

    func TPKeyboardAvoiding_viewIsValidKeyViewCandidate(_ view:UIView)->Bool
    {
        if view.isHidden || !view.isUserInteractionEnabled {return false}

        if view is UITextField
        {
            if (view as! UITextField).isEnabled {return true}
        }

        if view is UITextView
        {
            if (view as! UITextView).isEditable {return true}
        }

        return false
    }

    func TPKeyboardAvoiding_findNextInputViewAfterView(_ priorView:UIView,beneathView view:UIView, candidateView bestCandidate: inout UIView?)
    {
        let priorFrame = self.convert(priorView.frame, to: priorView.superview)
        let candidateFrame = bestCandidate == nil ? CGRect.zero : self.convert(bestCandidate!.frame, to: bestCandidate!.superview)

        var bestCandidateHeuristic = -sqrt(candidateFrame.origin.x*candidateFrame.origin.x + candidateFrame.origin.y*candidateFrame.origin.y) + ( Float(fabs(candidateFrame.minY - priorFrame.minY))<Float.ulpOfOne ? 1e6 : 0)

        for childView in view.subviews
        {
            if TPKeyboardAvoiding_viewIsValidKeyViewCandidate(childView)
            {
                let frame = self.convert(childView.frame, to: view)
                let heuristic = -sqrt(frame.origin.x*frame.origin.x + frame.origin.y*frame.origin.y)
                    + (Float(fabs(frame.minY - priorFrame.minY)) < Float.ulpOfOne ? 1e6 : 0)

                if childView != priorView && (Float(fabs(frame.minY - priorFrame.minY)) < Float.ulpOfOne
                    && frame.minX > priorFrame.minX
                    || frame.minY > priorFrame.minY)
                    && (bestCandidate == nil || heuristic > bestCandidateHeuristic)
                {
                    bestCandidate = childView
                    bestCandidateHeuristic = heuristic
                }
            }else
            {
                self.TPKeyboardAvoiding_findNextInputViewAfterView(priorView, beneathView: view, candidateView: &bestCandidate)
            }
        }
    }

    func TPKeyboardAvoiding_findNextInputViewAfterView(_ priorView:UIView,beneathView view:UIView) ->UIView?
    {
        var candidate:UIView?
        self.TPKeyboardAvoiding_findNextInputViewAfterView(priorView, beneathView: view, candidateView: &candidate)
        return candidate
    }


    @objc func TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(_ obj: AnyObject)
    {
        func processWithView(_ view: UIView) {
            for childView in view.subviews
            {
                if childView is UITextField || childView is UITextView
                {
                    self.TPKeyboardAvoiding_initializeView(childView)
                }else
                {
                    self.TPKeyboardAvoiding_assignTextDelegateForViewsBeneathView(childView)
                }
            }
        }

        if let timer = obj as? Timer, let view = timer.userInfo as? UIView {
            processWithView(view)
        }
        else if let view = obj as? UIView {
            processWithView(view)
        }
    }

    func TPKeyboardAvoiding_initializeView(_ view:UIView)
    {
        if let textField = view as? UITextField,
            let delegate = self as? UITextFieldDelegate, textField.returnKeyType == UIReturnKeyType.default &&
            textField.delegate !== delegate
        {
            textField.delegate = delegate
            let otherView = self.TPKeyboardAvoiding_findNextInputViewAfterView(view, beneathView: self)
            textField.returnKeyType = otherView != nil ? .next : .done

        }
    }

    func keyboardAvoidingState()->TPKeyboardAvoidingState
    {
        var state = objc_getAssociatedObject(self, &AssociatedKeysKeyboard.DescriptiveName) as? TPKeyboardAvoidingState
        if state == nil
        {
            state = TPKeyboardAvoidingState()
            self.state = state
        }

        return self.state!
    }

}

// MARK: - Internal object observer
internal class TPKeyboardAvoidingState:NSObject
{
    var priorInset = UIEdgeInsets.zero
    var priorScrollIndicatorInsets = UIEdgeInsets.zero

    var keyboardVisible = false
    var keyboardRect = CGRect.zero
    var priorContentSize = CGSize.zero

    var priorPagingEnabled = false
}

internal extension UIScrollView
{
    fileprivate struct AssociatedKeysKeyboard {
        static var DescriptiveName = "KeyBoard_DescriptiveName"
    }

    var state:TPKeyboardAvoidingState?{
        get{
            let optionalObject:AnyObject? = objc_getAssociatedObject(self, &AssociatedKeysKeyboard.DescriptiveName) as AnyObject?
            if let object:AnyObject = optionalObject {
                return object as? TPKeyboardAvoidingState
            } else {
                return nil
            }
        }
        set{
            objc_setAssociatedObject(self, &AssociatedKeysKeyboard.DescriptiveName, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}
Pramod More
  • 1,092
  • 2
  • 16
  • 47
0

Just enclose your textbox inside a view and then override inputAccessoryView returning the view. Important: Your view should be created programmatically. Do not use @IBOutlets.

 override var inputAccessoryView: UIView? {
    get {
        return newlyProgramaticallyCreatedView
    }}
TDesign
  • 411
  • 4
  • 7
0

If you're like me, using Autolayout and not getting the keyboard when running the App on a simulator. This might be because Apple make you use the keyboard of you're computer as first Keyboard.

To make the keyboard from the device appear :

shift + cmd + K

Sounds stupid but I would have loved to find this answer 3 hours ago :)

Brian Tompsett - 汤莱恩
  • 5,195
  • 62
  • 50
  • 120
Cublax
  • 520
  • 5
  • 14
-2

In Swift 4.0 -

func textFieldDidBeginEditing(_ textField: UITextField) {
        animateViewMoving(up: true, moveValue: 100)
    }

    func textFieldDidEndEditing(_ textField: UITextField) {
        animateViewMoving(up: false, moveValue: 100)
    }
    func animateViewMoving (up:Bool, moveValue :CGFloat){
        let movementDuration:TimeInterval = 0.3
        let movement:CGFloat = ( up ? -moveValue : moveValue)
        UIView.beginAnimations( "animateView", context: nil)
        UIView.setAnimationBeginsFromCurrentState(true)
        UIView.setAnimationDuration(movementDuration ) 
        self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
        UIView.commitAnimations()
    }
Abhishek Mishra
  • 1,547
  • 13
  • 30