如何以编程方式检查 iOS 应用程序中是不是存在键盘?
Posted
技术标签:
【中文标题】如何以编程方式检查 iOS 应用程序中是不是存在键盘?【英文标题】:How can I programmatically check whether a keyboard is present in iOS app?如何以编程方式检查 iOS 应用程序中是否存在键盘? 【发布时间】:2010-12-02 05:05:30 【问题描述】:我需要检查我的 ios 应用中键盘可见性的情况。
伪代码:
if(keyboardIsPresentOnWindow)
//Do action 1
else if (keyboardIsNotPresentOnWindow)
//Do action 2
如何检查这种情况?
【问题讨论】:
什么应用?什么语言?什么平台?我最好的猜测是 iPhone? 问题已解决。让游戏开始吧! 或许this ***.com/a/52417737/7276810 【参考方案1】:我认为您需要使用提供的有关键盘的通知:
发件人:http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UITextField_Class/Reference/UITextField.html
键盘通知
当系统显示或隐藏 键盘,它发布了几个键盘 通知。这些通知 包含有关的信息 键盘,包括它的大小,其中 您可以用于计算 涉及移动视图。注册 这些通知是唯一的方法 获取某些类型的信息 键盘。该系统提供 以下通知 键盘相关事件:
* UIKeyboardWillShowNotification * UIKeyboardDidShowNotification * UIKeyboardWillHideNotification * UIKeyboardDidHideNotification
有关这些的更多信息 通知,查看他们的描述 在 UIWindow 类参考中。为了 有关如何显示和隐藏的信息 键盘,请参阅文本和网页。
【讨论】:
我查看了这些通知,但不知道如何查看这些通知。如果您可以发布一些示例,那将非常有帮助。 看看 NSNotificationCenter。您必须注册您感兴趣的通知。不要忘记在您的应用程序退出时取消注册。【参考方案2】:当您知道键盘不可见时创建一个UIKeyboardListener
,例如通过从applicationDidFinishLaunching
调用[UIKeyboardListener shared]
。
@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 ,以便它通常作为拖放到任何项目中并从第二个应用程序启动时初始化,而不是您拥有记得在任何地方初始化它。【参考方案3】:
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
【讨论】:
为什么需要自己的池?+load
是 Objective-C 运行时调用的特殊方法。在应用程序二进制文件加载之后,但在输入 main()
函数之前,每个类都会调用它。无法保证自动释放池将处于活动状态。
MattDiPasquale:如果+load方法被删除,sharedInstance将永远不会被初始化。由于无法保证在运行时调用 +load 方法时自动释放池处于活动状态,因此有必要包装对系统提供的类的所有调用,以防它们调用自动释放。
不错的答案!我知道这已经有几年的历史了,但是 NSAutoreleasePool
alloc
/release
现在可以替换为 @autoreleasepool
中的代码
别忘了移除 Observer,可能在 KeyboardStateListener 的 dealloc 中。【参考方案4】:
我想这可能对你有帮助,
+(BOOL)isKeyBoardInDisplay
BOOL isExists = NO;
for (UIWindow *keyboardWindow in [[UIApplication sharedApplication] windows])
if ([[keyboardWindow description] hasPrefix:@"<UITextEffectsWindow"] == YES)
isExists = YES;
return isExists;
谢谢,
纳文山
【讨论】:
在iOS 6上,只有作品还没有出现!一旦他们的键盘显示一次,它就会停止工作。【参考方案5】:…或者采取简单的方法:
当您输入一个文本字段时,它会成为第一响应者,并出现键盘。
您可以使用[myTextField isFirstResponder]
检查键盘的状态。如果它返回YES
,则键盘处于活动状态。
【讨论】:
很好的解决方案,但是如果使用硬件键盘,这将不起作用(在 iPad 上并不罕见)。 这不能回答问题。这会告诉您文本字段是否是第一响应者。我有一个带有多个子视图控制器的视图控制器,所有子视图控制器都包含 UITextFields。使用这种方法,我无法从我的父视图控制器判断键盘是否显示。唯一可靠的方法是使用其他答案中解释的通知方法【参考方案6】:试试这个功能
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: How to access the `UIKeyboard`?
【讨论】:
【参考方案7】:现在在 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;
【讨论】:
假设多个窗口意味着一个键盘并且键盘总是第二个元素是无效的。 @jmah 当然这不是通用的方法,但它涵盖了大量的应用案例。任何尝试获取有关键盘信息的尝试都使用一些特定的视图层次结构,因为 Apple 没有为这种情况提供任何有用的 API。 这不起作用,对我有用的是遍历所有视图,并为所有 UITextFields 或 UITextView 检查它们是否是第一响应者...如果其中任何一个返回 true 键盘是可见的,否则它不是 【参考方案8】:一些观察:
单例对象的推荐模式如下。 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 键盘的好警告,谢谢!【参考方案9】:BOOL isTxtOpen = [txtfieldObjct isFirstReponder]。如果返回 YES,则键盘处于活动状态。
【讨论】:
【参考方案10】:这是来自 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;
【讨论】:
【参考方案11】:使用窗口子视图层次结构作为键盘显示的指示是一种 hack。如果 Apple 改变他们的底层实现,所有这些答案都会被打破。
正确的方法是在应用程序范围内监视键盘显示和隐藏通知,例如在您的 App Delegate 内部:
在 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;
请注意,当用户使用蓝牙或外接键盘时,键盘显示/隐藏通知不会触发。
【讨论】:
【参考方案12】:下面是如何在 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;
【讨论】:
【参考方案13】:要检查天气键盘是否出现,我们可以使用键盘预定义的通知。
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
【讨论】:
【参考方案14】:Swift 实现:
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 在启动时不执行类 load 方法,所以在应用启动时启动此服务很重要:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool
...
KeyboardStateListener.shared.start()
【讨论】:
使用iOS 13,swift 5.0这最后一点,类加载似乎没有必要?【参考方案15】: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
【讨论】:
我建议删除 deinit 中的观察者,或者如果它在视图中的视图控制器将消失 如果这是一个单例,那么使用 deinit 是没有意义的,因为它永远不会被 deinited【参考方案16】:这是我的解决方案,它将所有内容封装到一个静态方法中,您可以在任何地方调用它来检查:
+(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;
【讨论】:
【参考方案17】:您可以迭代地检查父视图的子视图中的所有文本视图、文本字段和标签,以查看是否有类似以下内容的第一响应者:
-(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;
【讨论】:
如果你有子视图控制器会失败【参考方案18】:添加扩展程序
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) )
当应用程序具有社交登录(如 facebook、google)时始终返回 true【参考方案19】:
斯威夫特 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)只是好奇为什么没有人赞成这个? 不,如果之前的视图控制器已经提供了键盘,这将不起作用。 理想情况下不应该出现前一个控制器提供键盘的情况,因为响应者不会出现在新控制器中,它将被关闭。【参考方案20】: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
【讨论】:
【参考方案21】:SwiftUI - 完整示例
import SwiftUI
struct ContentView: View
@State private var text = defaultText
@State private var isKeyboardShowing = false
private static let defaultText = "write something..."
private var gesture = TapGesture().onEnded(_ in
UIApplication.shared.endEditing(true)
)
var body: some View
ZStack
Color.black
VStack(alignment: .leading)
TextField("placeholder", text: $text)
.foregroundColor(.white)
.padding()
.background(Color.green)
.padding()
.edgesIgnoringSafeArea(.all)
.onChange(of: isKeyboardShowing, perform: (isShowing) in
if isShowing
if text == Self.defaultText text = ""
else
if text == "" text = Self.defaultText
)
.simultaneousGesture(gesture)
.onReceive(NotificationCenter.default
.publisher(for: UIResponder.keyboardWillShowNotification), perform: (value) in
isKeyboardShowing = true
)
.onReceive(NotificationCenter.default
.publisher(for: UIResponder.keyboardWillHideNotification), perform: (value) in
isKeyboardShowing = false
)
extension UIApplication
func endEditing(_ force: Bool)
self.windows
.filter$0.isKeyWindow
.first?
.endEditing(force)
【讨论】:
以上是关于如何以编程方式检查 iOS 应用程序中是不是存在键盘?的主要内容,如果未能解决你的问题,请参考以下文章