How to reliably determine if an external keyboard is connected on iOS 9?

Previously, for iOS 9, the most reliable method for determining whether an external keyboard is connected is to listen to UIKeyboardWillShowNotification and create a text field by the first responder, as described in this question . The notification will be triggered when using the virtual keyboard, but will not be triggered when using the external keyboard.

However, this behavior has now changed from iOS 9. UIKeyboardWillShowNotification also works when an external keyboard is connected, as a new keyboard toolbar is now displayed.

You can still determine the height of the keyboard and decide whether to display a smaller toolbar or a larger virtual keyboard. However, this method is not reliable, since the height of the keyboard has changed between different beta versions and cannot be considered unchanged over time.

Is there a more reliable method that can be used with iOS 9?

+32
ios objective-c ios9 keyboard
Aug 13 '15 at 15:00
source share
7 answers

Returning to the original question, I found a solution that works.

It seems that when a regular virtual keyboard is displayed, the keyboard frame is within the size of the screen. However, when a physical keyboard is connected and the keyboard toolbar is displayed, the keyboard frame is off-screen. We can check if the keyboard frame is off the screen to determine if the keyboard toolbar is displayed.

Objective-c

 - (void) keyboardWillShow:(NSNotification *)notification { NSDictionary* userInfo = [notification userInfo]; CGRect keyboardFrame = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; CGRect keyboard = [self.view convertRect:keyboardFrame fromView:self.view.window]; CGFloat height = self.view.frame.size.height; if ((keyboard.origin.y + keyboard.size.height) > height) { self.hasKeyboard = YES; } } 

Swift

 @objc func keyboardWillShow(_ notification: NSNotification) { guard let userInfo = notification.userInfo else {return} let keyboardScreenEndFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue let keyboard = self.view.convert(keyboardScreenEndFrame, from: self.view.window) let height = self.view.frame.size.height if (keyboard.origin.y + keyboard.size.height) > height { self.hasKeyboard = true } } 
+50
Sep 01 '15 at 18:19
source share

This code supports iOS 8 and iOS 9, inputAccessoryView, has a double protected constant that will be ready for new changes in future versions of iOS and to support new devices:

 #define gThresholdForHardwareKeyboardToolbar 160.f // it minimum height of the software keyboard on non-retina iPhone in landscape mode - (bool)isHardwareKeyboardUsed:(NSNotification*)keyboardNotification { NSDictionary* info = [keyboardNotification userInfo]; CGRect keyboardEndFrame; [[info valueForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame]; float height = [[UIScreen mainScreen] bounds].size.height - keyboardEndFrame.origin.y; return height < gThresholdForHardwareKeyboardToolbar; } 

Note. A hardware keyboard may be present but not used.

+5
Feb 13 '16 at 21:30
source share

I am using Sarah Alan's answer option. I am having problems with her approach in certain views. I never got to the bottom of the problem. But here is another way to determine if this is the "undo" keyboard of an external ios9 keyboard, and not a full-sized keyboard.

This is probably not very compatible with the transition, because if they change the size of the undo line, it slows down. But he did the job. I welcome criticism as there must be a better way ...

 //... somewhere ... #define HARDWARE_KEYBOARD_SIZE_IOS9 55 // + (BOOL) isExternalKeyboard:(NSNotification*)keyboardNotification { NSDictionary* info = [keyboardNotification userInfo]; CGRect keyboardEndFrame; [[info valueForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame]; CGRect keyboardBeginFrame; [[info valueForKey:UIKeyboardFrameBeginUserInfoKey] getValue:&keyboardBeginFrame]; CGFloat diff = keyboardEndFrame.origin.y - keyboardBeginFrame.origin.y; return fabs(diff) == HARDWARE_KEYBOARD_SIZE_IOS9; } 
+2
Sep 21 '15 at 5:35
source share

Private API solution: (you need to capture a private header file - use RuntimeViewer).

Works well for enterprise applications where you have no AppStore restrictions.

 #import "UIKit/UIKeyboardImpl.h" + (BOOL)isHardwareKeyboardMode { UIKeyboardImpl *kbi = [UIKeyboardImpl sharedInstance]; BOOL externalKeyboard = kbi.inHardwareKeyboardMode; NSLog(@"Using external keyboard? %@", externalKeyboard?@"YES":@"NO"); return externalKeyboard; } 
+2
Aug 03 '17 at 9:09 on
source share

If you make the toolbar obsolete, the keyboard will not appear. Do this by disabling the left and right groups (at least in iOS 12.4):

 textField.inputAssistantItem.leadingBarButtonGroups = [] textField.inputAssistantItem.trailingBarButtonGroups = [] 

... and if that helps, this is a quick way to observe:

 // Watch for a soft keyboard to show up let observer = NotificationCenter.default.addObserver(forName: UIWindow.keyboardWillShowNotification, object: nil, queue: nil) { notification in print("no external keyboard") } // Stop observing shortly after, since the keyboard should have shown by now DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { NotificationCenter.default.removeObserver(observer) } 
+1
Aug 01 '19 at 21:07 on
source share

You can sign up for a notification when connecting an external device:

 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceConnected:) name:EAAccessoryDidConnectNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceDisconnected:) name:EAAccessoryDidDisconnectNotification object:nil]; [[EAAccessoryManager sharedAccessoryManager] registerForLocalNotifications]; 

Or just get a list of connected devices:

 EAAccessoryManager* accessoryManager = [EAAccessoryManager sharedAccessoryManager]; if (accessoryManager) { NSArray* connectedAccessories = [accessoryManager connectedAccessories]; NSLog(@"ConnectedAccessories = %@", connectedAccessories); } 
0
Sep 01 '15 at 9:28
source share

You can try to check the peripherals that are advertising services using Core Bluetooth.

 CBCentralManager *centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil]; [centralManager scanForPeripheralsWithServices:nil options:nil]; 

And you should implement the delegate:

 - (void)centralManager:(CBCentralManager * _Nonnull)central didDiscoverPeripheral:(CBPeripheral * _Nonnull)peripheral advertisementData:(NSDictionary<NSString *, id> * _Nonnull)advertisementData RSSI:(NSNumber * _Nonnull)RSSI{ } 
-one
Aug 25 '15 at 20:04
source share



All Articles