Necesito verificar el estado de visibilidad del teclado en mi aplicación iOS.
Pseudocódigo:
if(keyboardIsPresentOnWindow) {
//Do action 1
}
else if (keyboardIsNotPresentOnWindow) {
//Do action 2
}
¿Cómo puedo comprobar esta condición?
Necesito verificar el estado de visibilidad del teclado en mi aplicación iOS.
Pseudocódigo:
if(keyboardIsPresentOnWindow) {
//Do action 1
}
else if (keyboardIsNotPresentOnWindow) {
//Do action 2
}
¿Cómo puedo comprobar esta condición?
Respuestas:
… O tomar el camino más fácil:
Cuando ingresa un campo de texto, se convierte en el primer respondedor y aparece el teclado. Puede comprobar el estado del teclado con [myTextField isFirstResponder]
. Si regresa YES
, entonces el teclado está activo.
El código de drawnonward es muy parecido, pero choca con el espacio de nombres de UIKit y podría ser más fácil de usar.
@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
es un método especial llamado por el tiempo de ejecución de Objective-C. Se llama para cada clase después de que se cargue el binario de la aplicación, pero antes de main()
que se ingrese la función. No hay garantía de que un grupo de liberación automática esté activo.
NSAutoreleasePool
alloc
/ release
ahora se puede reemplazar rodeando el código en@autoreleasepool { }
Cree un UIKeyboardListener
cuando sepa que el teclado no está visible, por ejemplo, llamando [UIKeyboardListener shared]
desde 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
para llamar a init en esta clase de escucha para que funcione genéricamente como arrastrar y soltar en cualquier proyecto y se inicialice desde el segundo lanzamiento de la aplicación en lugar de tener que recordar iniciarlo en cualquier lugar.
Creo que debe utilizar las notificaciones que se proporcionan sobre el teclado:
Notificaciones de teclado
Cuando el sistema muestra u oculta el teclado, publica varias notificaciones de teclado. Estas notificaciones contienen información sobre el teclado, incluido su tamaño, que puede usar para cálculos que involucran vistas en movimiento. Registrarse para recibir estas notificaciones es la única forma de obtener algunos tipos de información sobre el teclado. El sistema envía las siguientes notificaciones para eventos relacionados con el teclado:
* UIKeyboardWillShowNotification * UIKeyboardDidShowNotification * UIKeyboardWillHideNotification * UIKeyboardDidHideNotification
Para obtener más información sobre estas notificaciones, consulte sus descripciones en Referencia de clase UIWindow. Para obtener información sobre cómo mostrar y ocultar el teclado, consulte Texto e Internet.
Implementación de 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
}
}
Usar la jerarquía de subvista de ventana como indicación para mostrar el teclado es un truco. Si Apple cambia su implementación subyacente, todas estas respuestas se romperían.
La forma correcta sería monitorear la visualización del teclado y ocultar las notificaciones en toda la aplicación, como dentro de su Delegado de aplicación:
En AppDelegate.h:
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (assign, nonatomic) BOOL keyboardIsShowing;
@end
En 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;
}
Entonces puedes verificar usando:
BOOL keyboardIsShowing = ((AppDelegate*)[UIApplication sharedApplication].delegate).keyboardIsShowing;
Debe tenerse en cuenta que las notificaciones de mostrar / ocultar el teclado no se activarán cuando el usuario esté usando un teclado bluetooth o externo.
Agregar una extensión
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
}
}
}
Luego verifique si el teclado está presente,
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) })
Esto es de la Guía de programación de texto de iOS publicada por Apple aquí: https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html
Básicamente, llame a "registerForKeyBoardNotifications" en su ViewDidLoad. Luego, cada vez que el teclado se activa, se llama a "keyboardWasShown". Y cada vez que el teclado desaparece, se llama a "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;
}
Ahora en iOS8 esta solución, por supuesto, no funciona. Fue escrito inicialmente para IOS4 / 5.
Prueba esta solución:
- (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;
}
Algunas observaciones:
El patrón recomendado para un objeto singleton sería el siguiente. dispatch_once se asegura de que la clase se inicialice una vez de forma segura para subprocesos y que la variable estática no sea visible en el exterior. Y es GCD estándar, por lo que no es necesario conocer detalles de bajo nivel de Objective-C.
+ (KeyboardStateListener *)sharedInstance
{
static KeyboardStateListener* shared;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shared = [[KeyboardStateListener alloc] init];
// Other initialisations
});
return shared;
}
Por lo general, no solo desea saber si el teclado es visible o no, sino qué tan grande es. No todos los teclados tienen el mismo tamaño. Los teclados de iPhone son más pequeños que los de iPad. Entonces querría otra propiedad @property (readonly, nonatomic) CGRect keyboardRect;
que se establece en el método noticeShowKeyboard: como este:
NSValue* value = notification.userInfo [UIKeyboardFrameEndUserInfoKey];
_keyboardRect = value.CGRectValue;
Es importante notar que el rectángulo está en las coordenadas UIWindow y no respeta la rotación de la pantalla. Entonces, la persona que llama convertiría ese rectángulo llamando
KeyboardStateListener* listener = [KeyboardStateListener sharedInstance];
CGRect windowRect = listener.keyboardRect;
CGRect viewRect = [myView convertRect:windowRect fromView:self.window];
Si el usuario gira la pantalla mientras el teclado está visible, se le dirá a la aplicación que el teclado está oculto y luego se mostrará nuevamente. Cuando se muestra, lo más probable es que todavía no se hayan rotado otras vistas. Entonces, si observa que el teclado oculta / muestra eventos usted mismo, convierta las coordenadas cuando realmente las necesite, no en la notificación.
Si el usuario divide o desacopla el teclado, o usa un teclado de hardware, las notificaciones siempre mostrarán el teclado como oculto. Desacoplar o fusionar el teclado enviará una notificación de "teclado mostrado".
El oyente debe inicializarse mientras el teclado está oculto; de lo contrario, se perderá la primera notificación y se asumirá que el teclado está oculto cuando no lo esté.
Por eso es muy importante saber lo que realmente quiere. Este código es útil para mover cosas fuera del camino del teclado (con un teclado dividido o desacoplado, eso es responsabilidad del usuario). No le dice si el usuario puede ver un teclado en la pantalla (en el caso de un teclado dividido). No le dice si el usuario puede escribir (por ejemplo, cuando hay un teclado de hardware). Mirar otras ventanas no funciona si la aplicación crea otras ventanas por sí misma.
Implementación rápida :
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
}
}
Debido a que swift no ejecuta el método de carga de clases al inicio, es importante iniciar este servicio al iniciar la aplicación:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool
{
...
KeyboardStateListener.shared.start()
}
Esta es mi solución, encapsula todo en un solo método estático, y puede llamarlo en cualquier lugar para verificar:
+(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;
}
Y así es como se hace en 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");
}
No olvide cancelar el registro:
override func viewWillDisappear(animated: Bool) {
NSNotificationCenter.defaultCenter().removeObserver(self,
name: UIKeyboardDidShowNotification,
object: nil)
NSNotificationCenter.defaultCenter().removeObserver(self,
name: UIKeyboardWillHideNotification,
object: nil)
}
Y si desea cerrar el teclado al presionar el botón "Regresar":
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;
}
}
Prueba esta función
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;
}
BOOL isTxtOpen = [txtfieldObjct isFirstReponder]. Si devuelve SÍ, entonces el teclado está activo.
Para comprobar si aparece el teclado meteorológico, podemos utilizar las notificaciones predefinidas del teclado.
UIKeyboardDidShowNotification, UIKeyboardDidHideNotification
Por ejemplo, puedo usar el siguiente código para escuchar la notificación del teclado
// Escuche las apariciones y desapariciones del teclado
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardDidShow:)
name:UIKeyboardDidShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardDidHide:)
name:UIKeyboardDidHideNotification
object:nil];
en los métodos puedo recibir notificaciones
- (void)keyboardDidShow: (NSNotification *) notifyKeyBoardShow{
// key board is closed
}
- (void)keyboardDidHide: (NSNotification *) notifyKeyBoardHide{
// key board is opened
}
Rápido 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
}
}
}
Puede verificar iterativamente todas las vistas de texto, campos de texto y etiquetas en las subvistas de una vista principal para ver si alguna es la primera respuesta con algo como esto:
-(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;
}
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
}
}
Creo que esto puede ayudarte
+(BOOL)isKeyBoardInDisplay {
BOOL isExists = NO;
for (UIWindow *keyboardWindow in [[UIApplication sharedApplication] windows]) {
if ([[keyboardWindow description] hasPrefix:@"<UITextEffectsWindow"] == YES) {
isExists = YES;
}
}
return isExists;
}
Gracias,
Naveen Shan