如何检测何时显示和隐藏键盘

如何检测何时显示和隐藏键盘,从我的应用程序?

114550 次浏览

Check out the Managing the Keyboard section of the "Text, Web, and Editing Programming Guide" for information on tracking the keyboard being shown or hidden, and how to display/dismiss it manually.

You'll want to register yourself for the 2 keyboard notifications:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name: UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector (keyboardDidHide:) name: UIKeyboardDidHideNotification object:nil];

Great post on how to adjust your TextField to the keyboard - http://iosdevelopertips.com/user-interface/adjust-textfield-hidden-by-keyboard.html

In the ViewDidLoad method of your class set up to listen for messages about the keyboard:

// Listen for keyboard appearances and disappearances
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardDidShow:)
name:UIKeyboardDidShowNotification
object:nil];


[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardDidHide:)
name:UIKeyboardDidHideNotification
object:nil];

Then in the methods you specify (in this case keyboardDidShow and keyboardDidHide) you can do something about it:

- (void)keyboardDidShow: (NSNotification *) notif{
// Do something here
}


- (void)keyboardDidHide: (NSNotification *) notif{
// Do something here
}

You could use KBKeyboardObserver library. It contains some examples and provides simple interface.

You may just need addObserver in viewDidLoad. But having addObserver in viewWillAppear and removeObserver in viewWillDisappear prevents rare crashes which happens when you are changing your view.

Swift 4.2

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillDisappear), name: UIResponder.keyboardWillHideNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillAppear), name: UIResponder.keyboardWillShowNotification, object: nil)
}


@objc func keyboardWillAppear() {
//Do something here
}


@objc func keyboardWillDisappear() {
//Do something here
}


override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self)
}

Swift 3 and 4

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillDisappear), name: Notification.Name.UIKeyboardWillHide, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillAppear), name: Notification.Name.UIKeyboardWillShow, object: nil)
}


@objc func keyboardWillAppear() {
//Do something here
}


@objc func keyboardWillDisappear() {
//Do something here
}


override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self)
}

Older Swift

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


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


func keyboardWillAppear(notification: NSNotification){
// Do something here
}


func keyboardWillDisappear(notification: NSNotification){
// Do something here
}


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

Swift 3:

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


func keyboardWillShow(_ notification: NSNotification){
// Do something here
}


func keyboardWillHide(_ notification: NSNotification){
// Do something here
}

If you have more then one UITextFields and you need to do something when (or before) keyboard appears or disappears, you can implement this approach.

Add UITextFieldDelegate to your class. Assign integer counter, let's say:

NSInteger editCounter;

Set this counter to zero somewhere in viewDidLoad. Then, implement textFieldShouldBeginEditing and textFieldShouldEndEditing delegate methods.

In the first one add 1 to editCounter. If value of editCounter becomes 1 - this means that keyboard will appear (in case if you return YES). If editCounter > 1 - this means that keyboard is already visible and another UITextField holds the focus.

In textFieldShouldEndEditing subtract 1 from editCounter. If you get zero - keyboard will be dismissed, otherwise it will remain on the screen.

Swift 4:

  NotificationCenter.default.addObserver( self, selector: #selector(ControllerClassName.keyboardWillShow(_:)),
name: Notification.Name.UIKeyboardWillShow,
object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(ControllerClassName.keyboardWillHide(_:)),
name: Notification.Name.UIKeyboardWillHide,
object: nil)

Next, adding method to stop listening for notifications when the object’s life ends:-

Then add the promised methods from above to the view controller:
deinit {
NotificationCenter.default.removeObserver(self)
}
func adjustKeyboardShow(_ open: Bool, notification: Notification) {
let userInfo = notification.userInfo ?? [:]
let keyboardFrame = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
let height = (keyboardFrame.height + 20) * (open ? 1 : -1)
scrollView.contentInset.bottom += height
scrollView.scrollIndicatorInsets.bottom += height
}


@objc func keyboardWillShow(_ notification: Notification) {
adjustKeyboardShow(true, notification: notification)
}
@objc func keyboardWillHide(_ notification: Notification) {
adjustKeyboardShow(false, notification: notification)
}

Swift 4 - dd 20 october 2017

override func viewDidLoad() {
[..]


NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillDisappear(_:)), name: Notification.Name.UIKeyboardWillHide, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillAppear(_:)), name: Notification.Name.UIKeyboardWillShow, object: nil)
}


@objc func keyboardWillAppear(_ notification: NSNotification) {
if let userInfo = notification.userInfo,
let keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue).cgRectValue {
let inset = keyboardFrame.height // if scrollView is not aligned to bottom of screen, subtract offset
scrollView.contentInset.bottom = inset
scrollView.scrollIndicatorInsets.bottom = inset
}
}


@objc func keyboardWillDisappear(_ notification: NSNotification) {
scrollView.contentInset.bottom = 0
scrollView.scrollIndicatorInsets.bottom = 0
}


deinit {
NotificationCenter.default.removeObserver(self)
}

Swift - 4

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


override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self) //remove observer
}


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


@objc func keyboardWillShow(_ notification: Notification) {


}


@objc func keyboardWillHide(_ notification: Notification) {


}

In Swift 4.2 the notification names have moved to a different namespace. So now it's

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




override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self)
}




func addKeyboardListeners() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
}


@objc private extension WhateverTheClassNameIs {


func keyboardWillShow(_ notification: Notification) {
// Do something here.
}


func keyboardWillHide(_ notification: Notification) {
// Do something here.
}
}

There is a CocoaPods to facilitate the observation on NSNotificationCentr for the keyboard's visibility here: https://github.com/levantAJ/Keyhi

pod 'Keyhi'

Swift 5

There answers above are correct. Although I would prefer to create a helper to wrap up the notification's observers.

The benefit:

  1. You don't have to repeat each time you handle the keyboard behaviors.
  2. You can extend other notification by implement other enum value
  3. It's useful when you have to deal with keyboard in several controllers.

Sample code:

extension KeyboardHelper {
enum Animation {
case keyboardWillShow
case keyboardWillHide
}


typealias HandleBlock = (_ animation: Animation, _ keyboardFrame: CGRect, _ duration: TimeInterval) -> Void
}


final class KeyboardHelper {
private let handleBlock: HandleBlock


init(handleBlock: @escaping HandleBlock) {
self.handleBlock = handleBlock
setupNotification()
}


deinit {
NotificationCenter.default.removeObserver(self)
}


private func setupNotification() {
_ = NotificationCenter.default
.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main) { [weak self] notification in
self?.handle(animation: .keyboardWillShow, notification: notification)
}


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


private func handle(animation: Animation, notification: Notification) {
guard let userInfo = notification.userInfo,
let keyboardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,
let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double
else { return }


handleBlock(animation, keyboardFrame, duration)
}
}

How to use:

private var keyboardHelper: KeyboardHelper?
...


override func viewDidLoad() {
...
keyboardHelper = KeyboardHelper { [unowned self] animation, keyboardFrame, duration in
switch animation {
case .keyboardWillShow:
print("keyboard will show")
case .keyboardWillHide:
print("keyboard will hide")
}
}

}

So ah, this is the real answer now.

import Combine




class MrEnvironmentObject {
/// Bind into yr SwiftUI views
@Published public var isKeyboardShowing: Bool = false


/// Keep 'em from deallocatin'
var subscribers: [AnyCancellable]? = nil


/// Adds certain Combine subscribers that will handle updating the
///  `isKeyboardShowing` property
///
/// - Parameter host: the UIHostingController of your views.
func setupSubscribers<V: View>(
host: inout UIHostingController<V>
) {
subscribers = [
NotificationCenter
.default
.publisher(for: UIResponder.keyboardWillShowNotification)
.sink { [weak self] _ in
self?.isKeyboardShowing = true
},
NotificationCenter
.default
.publisher(for: UIResponder.keyboardWillHideNotification)
.sink { [weak self, weak host] _ in
self?.isKeyboardShowing = false
// Hidden gem, ask me how I know:
UIAccessibility.post(
notification: .layoutChanged,
argument: host
)
},
// ...
Profit
.sink { [weak self] profit in profit() },
]
}
}