25

I'm trying to update this code to swift 3:

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

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

So far, I've just tried the auto corrections given by the compiler. This results in code like this:

let notificationCenter = NotificationCenter.default()
notificationCenter.addObserver(self, selector: Selector(("keyboardWillShow:")), name: NSNotification.Name.UIKeyboardWillShow, object: nil)

notificationCenter.addObserver(self, selector: Selector(("keyboardWillHide:")), name: NSNotification.Name.UIKeyboardWillHide, object: nil)`

Unfortunately, that doesn't take me far, resulting in additional errors.

Has anyone solved this please?

Please note that I'm just trying how to write the notifications. I'm not (yet) trying to fix the notification functions.. Thanks

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
ICL1901
  • 7,632
  • 14
  • 90
  • 138
  • 3
    Possible duplicate of [Swift 3 NSNotificationCenter Keyboardwillshow/hide](http://stackoverflow.com/questions/37825327/swift-3-nsnotificationcenter-keyboardwillshow-hide) – LC 웃 Jun 17 '16 at 08:07
  • A bit different - the referenced question is about the keyboard show/hide functions. This question is about the notification setup. – ICL1901 Jun 17 '16 at 08:28
  • 1
    ok let me know if that didnot help – LC 웃 Jun 17 '16 at 08:29
  • Thanks Anish, I'm still trying to figure out how to write the notifications themselves. – ICL1901 Jun 17 '16 at 08:33

12 Answers12

39

Swift 4.2 Xcode 10 (10L213o)

The main changes compared with Swift 3 are in the UIWindow.keyboardWillShowNotification and UIWindow.keyboardWillHideNotification

let notifier = NotificationCenter.default
notifier.addObserver(self,
                     selector: #selector(KeyboardLayoutConstraint.keyboardWillShowNotification(_:)),
                     name: UIWindow.keyboardWillShowNotification,
                     object: nil)
notifier.addObserver(self,
                     selector: #selector(KeyboardLayoutConstraint.keyboardWillHideNotification(_:)),
                     name: UIWindow.keyboardWillHideNotification,
                     object: nil)


@objc
func keyboardWillShowNotification(_ notification: NSNotification) {}

@objc
func keyboardWillHideNotification(_ notification: NSNotification) {}
fssilva
  • 995
  • 11
  • 7
33

Swift 4

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

func keyboardWillShow(notification: NSNotification) {
     print("keyboardWillShow")
}

func keyboardWillHide(notification: NSNotification){
     print("keyboardWillHide")
}

deinit {
     NotificationCenter.default.removeObserver(self)
}

You can also get keyboard info using below code inside these methods.

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

@objc func keyboardWillChange(notification: NSNotification) {
     let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
     let curve = notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt
     let curFrame = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
     let targetFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
     let deltaY = targetFrame.origin.y - curFrame.origin.y
 }
ZAFAR007
  • 3,049
  • 1
  • 34
  • 45
  • Thanks! I was about to revise my question. – ICL1901 Jan 25 '18 at 18:48
  • Is it better to use `UIKeyboardDidShow` rather than `UIKeyboardWillShow`, or is it basically the same thing? – Michael Hsu May 06 '18 at 01:43
  • @MichaelHsu It is depends on you. If you need to call function before keyboard appearance, then you can use `keyboardWillShow` method otherwise you can use `UIKeyboardDidShow` that will call after keyboard appearance – ZAFAR007 May 10 '18 at 08:58
  • @MichaelHsu it's very rare that you want to use `didShow`; you generally want your keyboard avoiding to be animated and copy the animation curve and duration of the keyboard. – Dan Rosenstark May 13 '18 at 13:37
  • 3
    Although this solution works, it's worth a mention that you need to remove notification observer in viewWillDisappear to avoid memory leak. Simply add: `override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) NotificationCenter.default.removeObserver(self) }` – nja Sep 17 '18 at 15:09
25

I fixed this issue by writing the code like this

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: .UIKeyboardWillShow, object: nil)
Ben Rawner
  • 401
  • 1
  • 5
  • 3
23

For Swift 4.2 .UIKeyboardWillShow is renamed to UIResponder.keyboardWillShowNotification and .UIKeyboardWillHide is renamed to UIResponder.keyboardWillHideNotification

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

   @objc func NameOfSelector() {
       //Actions when notification is received
    }
13

You can replace the deprecated string literal Selector with the type-checked #selector(Class.method) pair:

let center = NotificationCenter.default
center.addObserver(self,
                   selector: #selector(keyboardWillShow(_:)),
                   name: .UIKeyboardWillShow,
                   object: nil)

center.addObserver(self,
                   selector: #selector(keyboardWillHide(_:)),
                   name: .UIKeyboardWillHide,
                   object: nil)

The #selector syntax is much safer, since Swift is able to check at compile time that the specified method actually exists.

For more information about Swift selectors, see rickster's detailed answer.

Community
  • 1
  • 1
  • Awesome I just used this! Side note in case anyone runs in my issue: when implementing the `keyboardWillShow` and the `keyboardWillHide`, do not make them private functions or you will have a compiler error. – Sami Jan 07 '17 at 18:53
11

Swift 5.1 + Combine + SwiftUI

@State var keyboardHeight: CGFloat = 0 // or @Published if one is in ViewModel: ObservableObject

private var cancellableSet: Set<AnyCancellable> = []
    
init() {
            
   let notificationCenter = NotificationCenter.default
        
   notificationCenter.publisher(for: UIWindow.keyboardWillShowNotification)
       .map {
             guard
                 let info = $0.userInfo,
                 let keyboardFrame = info[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect
                 else { return 0 }

             return keyboardFrame.height
         }
         .assign(to: \.keyboardHeight, on: self)
         .store(in: &cancellableSet)
        
     notificationCenter.publisher(for: UIWindow.keyboardDidHideNotification)
         .map { _ in 0 }
         .assign(to: \.keyboardHeight, on: self)
         .store(in: &cancellableSet)
    }
    
Community
  • 1
  • 1
Sergey
  • 358
  • 3
  • 12
3

In Swift 3.0

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

}

Keybord Show and Hide

func keyboardWillShow(notification: NSNotification) 
{

      // Your Code Here
}

func keyboardWillHide(notification: NSNotification)
{  
   //Your Code Here     
}
Amul4608
  • 1,390
  • 14
  • 30
2

You can perform keyboard notification on both version of Swift respectively.

Add Objserver:

NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidShow), name: .UIKeyboardWillShow, object: nil)

Call function swift 3

func keyboardDidShow() {
          print("keyboardDidShow")
       }

Call function In swift 4

@objc func keyboardDidShow() {
      print("keyboardDidShow")
   }
Sunil M.
  • 554
  • 5
  • 17
2
  NotificationCenter.default.addObserver(self, selector: Selector(("keyboardWillShow:")), name:UIResponder.keyboardWillShowNotification, object: nil);
    NotificationCenter.default.addObserver(self, selector: Selector(("keyboardWillHide:")), name:UIResponder.keyboardWillHideNotification, object: nil);
SCS
  • 435
  • 3
  • 12
1

Here is the best solution that works for me as far (used from "Lets Build That App" YouTube channel)

class ChatVC: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {

// reference to your UIView with message TextField

    @IBOutlet weak var ChatView: UIView!   


// bottom constrain to your UIView (in my case ChatView)

    var bottomConstraint: NSLayoutConstraint?  

    override func viewDidLoad() {
        super.viewDidLoad()

// add some text in the placeholder if you want

        messageField.placeholder = "Type your message.." 

// here we add two notifications for showing and hiding the keyboard

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

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


// defines the start position for message textField that will be shown on the screen

        bottomConstraint = NSLayoutConstraint(item: ChatViewField!, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: -40)    
        view.addConstraint(bottomConstraint!)    
    }

// handles notifications for both cases when keyboard pops up and disappears  

  @objc func handleKeyboardNotification(notification: NSNotification){
        if let userInfo = notification.userInfo {

            let keyboardFrame =  (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
            print(keyboardFrame)

            let isKeyboardShowing = notification.name == UIResponder.keyboardWillShowNotification

            bottomConstraint?.constant = isKeyboardShowing ? -keyboardFrame.height : -40

// makes animation at the same time as the keyboard

UIView.animate(withDuration: 0, delay: 0, options: UIView.AnimationOptions.curveEaseOut, animations: {

                self.view.layoutIfNeeded()
            }) { (completed) in    
            }   
        }
    }
zmag
  • 7,825
  • 12
  • 32
  • 42
Ivan.I
  • 11
  • 2
1

You can create a protocol that has this logic and your UIViewController implements it, in the viewDidLoad you call the config method to bind it.

I use a constraint at the bottom to move the whole view with the same animation as the keyboard.

Here you have an example using ZAFAR007's answer:

protocol ViewControllerKeyboardType {
    var bottomConstraint: NSLayoutConstraint! { get set }
    
    func configKeyboardNotification()
    
    func keyboardWillChangeHandler(notification: NSNotification)
}

extension ViewControllerKeyboardType where Self: UIViewController {
    func keyboardWillChangeHandler(notification: NSNotification) {
        let duration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double ?? 0.0
        let curve = notification.userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt ?? UIView.AnimationOptions.curveLinear.rawValue
        let curFrame = (notification.userInfo![UIResponder.keyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
        let targetFrame = (notification.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
        let deltaY = targetFrame.origin.y - curFrame.origin.y
        
        UIView.animate(withDuration: duration, delay: 0, options: UIView.AnimationOptions(rawValue: curve)) {
            self.bottomConstraint.constant = self.bottomConstraint.constant - deltaY
            self.view.layoutIfNeeded()
        }
    }
    
    func configKeyboardNotification() {
        NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillChangeFrameNotification, object: nil, queue: nil) { [weak self] (notification) in
            self?.keyboardWillChangeHandler(notification: notification as NSNotification)
        }
    }
}

Usage:

class MyViewController: UIViewController, ViewControllerKeyboardType {
    @IBOutlet weak var bottomConstraint: NSLayoutConstraint!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.configKeyboardNotification()
    }
}
mroca
  • 116
  • 5
0

If you are an idiot like me and fancy Combine:

Simple extensions for your UIViewController:

import UIKit
import Combine 

extension Notification {
    var keyboardHeight: CGFloat {
        return (userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect)?.height ?? 0
    }
}

enum KeyboardState {
    case willShow
    case willHide
}

struct KeyboardObject: Equatable {
    var state: KeyboardState
    var height: CGFloat
}

extension UIViewController {
    private var keyboardWillShowPublisher: AnyPublisher<KeyboardObject, Never> {
        NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification,object: nil)
        .compactMap {
            return KeyboardObject(state: .willShow, height: $0.keyboardHeight)
        }.eraseToAnyPublisher()
    }
    
    private var keyboardWillHidePublisher: AnyPublisher<KeyboardObject, Never> {
        NotificationCenter.default
            .publisher(for: UIResponder.keyboardWillHideNotification,object: nil)
            .compactMap { _ in
                return KeyboardObject(state: .willHide, height: 0.0)
            }.eraseToAnyPublisher()
    }
    
    func keyboardListener() -> AnyPublisher<KeyboardObject, Never> {
        Publishers.Merge(keyboardWillShowPublisher, keyboardWillHidePublisher)
            .removeDuplicates()
            .eraseToAnyPublisher()
    }
}

In your UIViewController:

import UIKit
import Combine

final class SomeVC: UIViewController {
    var keyboardPublisher: AnyCancellable?
    private var flowLayoutConstraint: NSLayoutConstraint!
    private let defaults = UserDefaults.standard
    override func viewWillAppear(_ animated: Bool) {
        keyboardPublisher = self.keyboardListener()
            .sink(receiveValue: { [unowned self] keyboard in
                switch keyboard.state {
                case .willShow:
                    manageKeyboardChange(value: keyboard.height)
                case .willHide:
                // I used a value saved in my userDefaults, but just set it back to the original constraint for that view without the keyboard
                    manageKeyboardChange(value: defaults.double(forKey: "opaqueHeight"))
                }
            })
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        keyboardPublisher?.cancel()
    }
    
    func manageKeyboardChange(value: CGFloat) {
        self.flowLayoutConstraint.constant = value
        UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations:  { [weak self] in
            self?.view.layoutIfNeeded()
        }, completion: nil)
        
        print("CHANGE")
    }
}

Thel
  • 396
  • 2
  • 8