如何以编程方式检查iOS应用程序中是否存在键盘?


111

我需要在我的iOS应用中检查键盘可见性的条件。

伪代码:

if(keyboardIsPresentOnWindow) {
    //Do action 1
}
else if (keyboardIsNotPresentOnWindow) {
    //Do action 2
}

如何检查这种情况?


什么样的应用程序?什么语言?什么平台?我最好的猜测是iPhone?
尼克·贝德福德

4
问题已解决。让游戏开始!
罗伯特·哈维

也许是这样

Answers:


68

…或采取简单的方法:

当您输入textField时,它将成为第一响应者并出现键盘。您可以使用来检查键盘的状态[myTextField isFirstResponder]。如果返回YES,则键盘处于活动状态。


20
好的解决方案,但是,如果使用了硬件键盘,则此方法将无法工作(在iPad上并不常见)。
Andrei Herford 2014年

4
这不能回答问题。这将告诉您文本字段是否是第一响应者。我有一个带有多个子视图控制器的视图控制器,所有子视图控制器都包含UITextFields。使用此方法,我无法从父视图控制器判断是否显示了键盘。唯一可靠的方法是使用其他答案中说明的通知方法
TimWhiting

63

Drawnonward的代码非常接近,但与UIKit的命名空间冲突,因此可以更易于使用。

@interface KeyboardStateListener : NSObject {
    BOOL _isVisible;
}
+ (KeyboardStateListener *)sharedInstance;
@property (nonatomic, readonly, getter=isVisible) BOOL visible;
@end

static KeyboardStateListener *sharedInstance;

@implementation KeyboardStateListener

+ (KeyboardStateListener *)sharedInstance
{
    return sharedInstance;
}

+ (void)load
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    sharedInstance = [[self alloc] init];
    [pool release];
}

- (BOOL)isVisible
{
    return _isVisible;
}

- (void)didShow
{
    _isVisible = YES;
}

- (void)didHide
{
    _isVisible = NO;
}

- (id)init
{
    if ((self = [super init])) {
        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
        [center addObserver:self selector:@selector(didShow) name:UIKeyboardDidShowNotification object:nil];
        [center addObserver:self selector:@selector(didHide) name:UIKeyboardWillHideNotification object:nil];
    }
    return self;
}

@end

4
为什么需要自己的游泳池?
Dan Rosenstark'7

18
+load是由Objective-C运行时调用的特殊方法。在应用程序二进制加载之后但在输入main()函数之前,每个类都会调用该函数。无法保证自动释放池将处于活动状态。
rpetrich

1
MattDiPasquale:如果删除+ load方法,则sharedInstance将永远不会初始化。由于不能保证在运行时调用+ load方法时自动释放池处于活动状态,因此,如果它们调用自动释放,则必须将所有调用包装到系统提供的类中。
rpetrich's

3
好答案!我知道这已经有好几年了,但是NSAutoreleasePool alloc/ release现在可以用@autoreleasepool { }
chown 2012年

3
不要忘记删除Observer,可能是在KeyboardStateListener的dealloc中删除了。
SushiGrass Jacob

32

UIKeyboardListener当您知道键盘不可见时,请创建一个,例如通过[UIKeyboardListener shared]从调用applicationDidFinishLaunching

@implementation UIKeyboardListener

+ (UIKeyboardListener) shared {
    static UIKeyboardListener sListener;    
    if ( nil == sListener ) sListener = [[UIKeyboardListener alloc] init];

    return sListener;
}

-(id) init {
    self = [super init];

    if ( self ) {
        NSNotificationCenter        *center = [NSNotificationCenter defaultCenter];
        [center addObserver:self selector:@selector(noticeShowKeyboard:) name:UIKeyboardDidShowNotification object:nil];
        [center addObserver:self selector:@selector(noticeHideKeyboard:) name:UIKeyboardWillHideNotification object:nil];
    }

    return self;
}

-(void) noticeShowKeyboard:(NSNotification *)inNotification {
    _visible = true;
}

-(void) noticeHideKeyboard:(NSNotification *)inNotification {
    _visible = false;
}

-(BOOL) isVisible {
    return _visible;
}

@end

注意:您可以用来+(void)load在此侦听器类上调用init,这样它通常可以拖放到任何项目中并从第二个应用程序启动时进行初始化,而不必记住在任何地方进行初始化。
艾伯特·伦肖

30

我认为您需要使用有关键盘的通知:

来自:http : //developer.apple.com/iphone/library/documentation/UIKit/Reference/UITextField_Class/Reference/UITextField.html

键盘通知

当系统显示或隐藏键盘时,它会发布多个键盘通知。这些通知包含有关键盘的信息,包括其大小,您可以将其用于涉及移动视图的计算。注册这些通知是获取有关键盘某些类型信息的唯一方法。系统为键盘相关事件提供以下通知:

* UIKeyboardWillShowNotification
* UIKeyboardDidShowNotification
* UIKeyboardWillHideNotification
* UIKeyboardDidHideNotification

有关这些通知的更多信息,请参见UIWindow类参考中的说明。有关如何显示和隐藏键盘的信息,请参见文本和Web。


我检查了这些通知,但不知道如何检查这些通知。如果您可以发布一些示例,那将非常有帮助。
Jitendra Singh,2009年

2
看看NSNotificationCenter。您必须注册感兴趣的通知。不要忘记在应用程序退出时注销。
ThomasMüller2009年

13

Swift 3实施

    import Foundation
class KeyboardStateListener: NSObject
{
    static let shared = KeyboardStateListener()
    var isVisible = false

    func start() {
        NotificationCenter.default.addObserver(self, selector: #selector(didShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(didHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
    }

    func didShow()
    {
        isVisible = true
    }

    func didHide()
    {
        isVisible = false
    } 
}

1
我建议删除deinit中的观察者,或者如果该观察者的视图控制器将消失
Juan Boero

3
如果这是单例的,则使用deinit毫无意义,因为它永远都不会被初始化
Sirens

11

使用窗口子视图层次结构作为键盘显示的指示是黑客。如果苹果改变了他们的底层实现,那么所有这些答案都会被打破。

正确的方法是监视整个应用程序(例如在应用程序委托中)的键盘显示和隐藏通知:

在AppDelegate.h中:

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (assign, nonatomic) BOOL keyboardIsShowing;

@end

在AppDelegate.m中:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{

    // Monitor keyboard status application wide
    self.keyboardIsShowing = NO;
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:)
                                             name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:)
                                             name:UIKeyboardWillHideNotification object:nil];

    return YES;
}

- (void)keyboardWillShow:(NSNotification*)aNotification
{
    self.keyboardIsShowing = YES;
}

- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
    self.keyboardIsShowing = NO;
}

然后,您可以使用以下命令进行检查:

BOOL keyboardIsShowing = ((AppDelegate*)[UIApplication sharedApplication].delegate).keyboardIsShowing;

请注意,当用户使用蓝牙或外接键盘时,键盘显示/隐藏通知不会触发。


10

添加扩展

extension UIApplication {
    /// Checks if view hierarchy of application contains `UIRemoteKeyboardWindow` if it does, keyboard is presented
    var isKeyboardPresented: Bool {
        if let keyboardWindowClass = NSClassFromString("UIRemoteKeyboardWindow"),
            self.windows.contains(where: { $0.isKind(of: keyboardWindowClass) }) {
            return true
        } else {
            return false
        }
    }
}

然后检查是否存在键盘,

if UIApplication.shared.isKeyboardPresented {
     print("Keyboard presented")
} else { 
     print("Keyboard is not presented")
}

可以做guard let keyboardWindowClass = NSClassFromString("UIRemoteKeyboardWindow") else { return false }; return UIApplication.shared.windows.contains(where: { $0.isKind(of: keyboardWindowClass) })
粘土桥

5

这来自Apple在此处发布的iOS文本编程指南:https : //developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html

基本上在您的ViewDidLoad中调用“ registerForKeyBoardNotifications”。然后,每次键盘激活时,都会调用“ keyboardWasShown”。每次键盘消失时,都会调用“ keyboardWillBeHidden”。

// Call this method somewhere in your view controller setup code.
- (void)registerForKeyboardNotifications {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil];
}

// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification {
    NSLog(@"Keyboard is active.");
    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
    scrollView.contentInset = contentInsets;
    scrollView.scrollIndicatorInsets = contentInsets;

    // If active text field is hidden by keyboard, scroll it so it's visible
    // Your app might not need or want this behavior.
    CGRect aRect = self.view.frame;
    aRect.size.height -= kbSize.height;
    if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) {
        [self.scrollView scrollRectToVisible:activeField.frame animated:YES];
    }
}

// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification {
    NSLog(@"Keyboard is hidden");
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    scrollView.contentInset = contentInsets;
    scrollView.scrollIndicatorInsets = contentInsets;
}

5

现在,在iOS8中,此解决方案当然不起作用。它最初是为IOS4 / 5编写的。

试试这个解决方案:

- (BOOL) isKeyboardOnScreen 
{
    BOOL isKeyboardShown = NO;

    NSArray *windows = [UIApplication sharedApplication].windows;
    if (windows.count > 1) {
        NSArray *wSubviews =  [windows[1]  subviews];
        if (wSubviews.count) {
            CGRect keyboardFrame = [wSubviews[0] frame];
            CGRect screenFrame = [windows[1] frame];
            if (keyboardFrame.origin.y+keyboardFrame.size.height == screenFrame.size.height) {
                isKeyboardShown = YES;
            }
        }
    }

    return isKeyboardShown;
}

2
假设多个窗口包含一个键盘,而键盘始终是第二个元素,则是无效的。
jmah 2014年

1
@jmah当然,这不是通用方法,但它涵盖了大量的应用程序案例。任何尝试获取有关键盘信息的尝试都将使用某些特定的视图层次结构,因为Apple在这种情况下没有提供任何有用的API。
malex

这不工作,什么工作对我来说是迭代通过各方意见,对所有UITextFields或UITextView的检查,如果他们的第一反应......如果有的话则返回true键盘是可见否则它不会
AMD

4

一些观察:

推荐的单例对象模式如下。dispatch_once确保以线程安全的方式初始化该类一次,并且在外部看不到该静态变量。它是标准的GCD,因此无需了解Objective-C的底层细节。

+ (KeyboardStateListener *)sharedInstance
{
    static KeyboardStateListener* shared;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        shared = [[KeyboardStateListener alloc] init];
        // Other initialisations
    });

    return shared;
}

通常,您不想只知道键盘是否可见,而是要知道它的大小。键盘的大小并不相同。iPhone键盘比iPad键盘小。因此,您需要另一个@property (readonly, nonatomic) CGRect keyboardRect;在noticeShowKeyboard:方法中设置的属性,如下所示:

NSValue* value = notification.userInfo [UIKeyboardFrameEndUserInfoKey];
_keyboardRect = value.CGRectValue;

重要的是要注意矩形位于UIWindow坐标中,并且不考虑屏幕旋转。因此,调用者可以通过调用来转换该矩形

KeyboardStateListener* listener = [KeyboardStateListener sharedInstance];
CGRect windowRect = listener.keyboardRect;
CGRect viewRect = [myView convertRect:windowRect fromView:self.window];

如果在键盘可见的情况下用户旋转屏幕,则会告知应用程序该键盘已隐藏,然后再次显示。当显示时,其他视图很可能尚未旋转。因此,如果您自己观察键盘的隐藏/显示事件,请在实际需要时(而不是在通知中)转换坐标。

如果用户拆分或取消固定键盘,或使用硬件键盘,则通知将始终将键盘显示为隐藏状态。卸下或合并键盘将发送“显示键盘”通知。

必须在隐藏键盘的同时初始化侦听器,否则将丢失第一个通知,并且假定未隐藏键盘时将其隐藏。

因此,了解您的实际需求非常重要。此代码对于将内容移开键盘的方式很有用(使用拆分或未固定的键盘,这是用户的责任)。它不会告诉您用户是否可以在屏幕上看到键盘(如果是分体式键盘)。它不会告诉您用户是否可以键入(例如,当有硬件键盘时)。如果该应用程序本身创建了其他窗口,则无法查看其他窗口。


关于iPad中键盘的良好警告,thanx!
2015年

3

迅捷的实现:

class KeyboardStateListener: NSObject
{
  static var shared = KeyboardStateListener()
  var isVisible = false

  func start() {
    let nc = NSNotificationCenter.defaultCenter()
    nc.addObserver(self, selector: #selector(didShow), name: UIKeyboardDidShowNotification, object: nil)
    nc.addObserver(self, selector: #selector(didHide), name: UIKeyboardDidHideNotification, object: nil)
  }

  func didShow()
  {
    isVisible = true
  }

  func didHide()
  {
    isVisible = false
  } 
}

由于swift 在启动时不会执行类加载方法,因此在应用启动时启动此服务很重要:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool
{
  ...    
  KeyboardStateListener.shared.start() 
}

最后使用iOS 13,swift 5.0,似乎不需要加载类吗?
user3069232 '19

3

这是我的解决方案,它将所有内容封装到一个静态方法中,您可以在任何地方调用它进行检查:

+(BOOL)isKeyboardVisible{
    static id tokenKeyboardWillShow = nil;
    static id tokenKeyboardWillHide = nil;
    static BOOL isKbVisible = NO;
    @synchronized (self) {
        if (tokenKeyboardWillShow == nil){
            tokenKeyboardWillShow = [[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardWillShowNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
                @synchronized (self) {
                    isKbVisible = YES;
                }
            }];
        }

        if (tokenKeyboardWillHide == nil){
            tokenKeyboardWillHide = [[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardWillHideNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
                @synchronized (self) {
                    isKbVisible = NO;
                }
            }];
        }
    }

    return isKbVisible;
}

2

这是在Swift中执行的方法:

 func registerForKeyboardNotifications() {
    NSNotificationCenter.defaultCenter().addObserver(
        self,
        selector: "keyboardWasShown:",
        name: UIKeyboardDidShowNotification,
        object: nil)

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

func keyboardWasShown(notification: NSNotification) {
    println("Keyboard was shown");
}

func keyboardWillBeHidden(notification: NSNotification) {
    println("Keyboard was dismissed");
}

不要忘记注销:

 override func viewWillDisappear(animated: Bool) {
    NSNotificationCenter.defaultCenter().removeObserver(self,
        name: UIKeyboardDidShowNotification,
        object: nil)

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

如果要在按下“返回”按钮时关闭键盘,请执行以下操作:

class ViewController: UIViewController, UITextFieldDelegate {

@IBOutlet weak var yourTextField: UITextField!

override func viewDidLoad() {
    super.viewDidLoad()
    registerForKeyboardNotifications()
    yourTextField.delegate = self
}

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

}

1

试试这个功能

BOOL UIKeyboardIsVisible(){

BOOL keyboardVisible=NO;
// Locate non-UIWindow.
UIWindow *keyboardWindow = nil;
for (UIWindow *testWindow in [[UIApplication sharedApplication] windows]) {
    if (![[testWindow class] isEqual:[UIWindow class]]) {
        keyboardWindow = testWindow;
        break;
    }
}
// Locate UIKeyboard.
for (UIView *possibleKeyboard in [keyboardWindow subviews]) {
    // iOS 4 sticks the UIKeyboard inside a UIPeripheralHostView.
    if ([[possibleKeyboard description] hasPrefix:@"<UIPeripheralHostView"]) {
        keyboardVisible=YES;
    }
    if ([[possibleKeyboard description] hasPrefix:@"<UIKeyboard"]) {
        keyboardVisible=YES;
        break;
    }
}
return keyboardVisible;

}

来自:iOS:如何访问`UIKeyboard`?


1

BOOL isTxtOpen = [txtfieldObjct isFirstReponder]。如果返回“是”,则键盘处于活动状态。


1

要检查天气键盘是否出现,我们可以使用键盘预定义的通知。

UIKeyboardDidShowNotification,UIKeyboardDidHideNotification

例如,我可以使用以下代码来监听键盘通知

//监听键盘的出现和消失

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

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

在我可以收到通知的方法中

- (void)keyboardDidShow: (NSNotification *) notifyKeyBoardShow{
    // key board is closed
}

- (void)keyboardDidHide: (NSNotification *) notifyKeyBoardHide{
    // key board is opened
}

1

斯威夫特4

extension UIViewController {
    func registerKeyboardNotifications() {
        let center = NotificationCenter.default
        center.addObserver(self, selector: #selector(keyboardWillBeShown(note:)), name: Notification.Name.UIKeyboardWillShow, object: nil)
        center.addObserver(self, selector: #selector(keyboardWillBeHidden(note:)), name: Notification.Name.UIKeyboardWillHide, object: nil)
    }

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

    }

    @objc
    func keyboardWillBeShown(note: Notification) {}

    @objc
    func keyboardWillBeHidden(note: Notification) {}

}

final class MyViewController: UIViewController {

    // MARK: - Properties
    var isKeyboardVisible = false

    // MARK: - Life Cycle
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        registerKeyboardNotifications()
    }

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

    // MARK: - Keyboard Handling
    override func keyboardWillBeShown(note: Notification) {
        isKeyboardVisible = true
        let userInfo = note.userInfo
        let keyboardFrame = userInfo?[UIKeyboardFrameEndUserInfoKey] as! CGRect
        let contentInset = UIEdgeInsetsMake(0.0, 0.0, keyboardFrame.height, 0.0)
        tableView.contentInset = contentInset
    }

   override func keyboardWillBeHidden(note: Notification) {
        tableView.contentInset = .zero
        isKeyboardVisible = false
   }

   // MARK: - Test
   fileprivate func test() {
        if isKeyboardVisible { // do something
        }
   }
}

对我来说效果很好(Xcode 10.2,Swift4),只是好奇为什么没人支持呢?
infinity_coding7

不可以,如果先前的视图控制器已经提供了键盘,那将不起作用。
里卡多

0

您可以迭代检查父视图的子视图中的所有文本视图,文本字段和标签,以查看是否有任何类似的内容是第一响应者:

-(BOOL)isKeyboardActiveInView:(UIView *)view {
    for (UIView *anyView in [view subviews]) {
        if ([anyView isKindOfClass:[UITextField class]]) {
            if (((UITextField *)anyView).isFirstResponder) {
                return YES;
            }
        } else if ([anyView isKindOfClass:[UILabel class]]) {
            if (((UILabel *)anyView).isFirstResponder) {
                return YES;
            }
        } else if ([anyView isKindOfClass:[UITextView class]]) {
            if (((UITextView *)anyView).isFirstResponder) {
                return YES;
            }
        } else {
            if ([self isKeyboardActiveInView:anyView]) {
                return YES;
            }
        }
    }
    return NO;
}

如果您有子视图控制器,此操作将失败
Ricardo

-1

SWIFT 4.2 / SWIFT 5

class Listener {
   public static let shared = Listener()
   var isVisible = false

   // Start this listener if you want to present the toast above the keyboard.
   public func startKeyboardListener() {
      NotificationCenter.default.addObserver(self, selector: #selector(didShow), name: UIResponder.keyboardWillShowNotification, object: nil)
      NotificationCenter.default.addObserver(self, selector: #selector(didHide), name: UIResponder.keyboardWillHideNotification, object: nil)
   }

   @objc func didShow() {
     isVisible = true
   }

    @objc func didHide(){
       isVisible = false
    }
}

-5

我认为这可能对您有帮助,

+(BOOL)isKeyBoardInDisplay  {

    BOOL isExists = NO;
    for (UIWindow *keyboardWindow in [[UIApplication sharedApplication] windows])   {
        if ([[keyboardWindow description] hasPrefix:@"<UITextEffectsWindow"] == YES) {
            isExists = YES;
        }  
    }

    return isExists;
}

谢谢,

纳文山


1
在iOS 6上,只有作品还没有出现!一旦键盘被显示一次,它就会停止工作。
James Laurenstin 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.