如何可靠地检测 iOS 9 上是否连接了外接键盘?
How to reliably detect if an external keyboard is connected on iOS 9?
在 iOS9 之前,确定是否连接外部键盘的最可靠方法是监听 UIKeyboardWillShowNotification
并使文本字段成为第一响应者,如 [=13] 中所述=].使用虚拟键盘时会触发通知,但不会在使用外接键盘时触发。
但是,此行为现在已随着 iOS9 发生变化。UIKeyboardWillShowNotification
也会在连接外部键盘时触发,因为现在显示了新的键盘工具栏。
仍然可以检测键盘高度,判断显示的是较小的工具栏还是较大的虚拟键盘。然而,这种方法并不可靠,因为键盘高度在各个测试版之间发生了变化,并且不能指望随着时间的推移保持不变。
是否有更可靠的方法可以与iOS 9一起使用?
您可以尝试使用 Core Bluetooth
检查正在宣传服务的外围设备
CBCentralManager *centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
[centralManager scanForPeripheralsWithServices:nil options:nil];
并且您应该实现委托:
- (void)centralManager:(CBCentralManager * _Nonnull)central
didDiscoverPeripheral:(CBPeripheral * _Nonnull)peripheral
advertisementData:(NSDictionary<NSString *,
id> * _Nonnull)advertisementData
RSSI:(NSNumber * _Nonnull)RSSI{
}
您可以在连接外部设备时订阅通知:
[[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];
或者只检索连接设备的列表:
EAAccessoryManager* accessoryManager = [EAAccessoryManager sharedAccessoryManager];
if (accessoryManager)
{
NSArray* connectedAccessories = [accessoryManager connectedAccessories];
NSLog(@"ConnectedAccessories = %@", connectedAccessories);
}
回到最初的问题后,我找到了一个有效的解决方案。
显示常规虚拟键盘时,键盘框架似乎在屏幕尺寸范围内。但是,当连接物理键盘并显示键盘工具栏时,键盘框架位于屏幕外。我们可以检查键盘框架是否在屏幕外,以确定是否显示键盘工具栏。
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
}
}
我使用的是 Sarah Elan 答案的变体。在某些观点上,我对她的方法有疑问。我从来没有完全弄清楚导致问题的原因。但这里有另一种方法可以确定它是否是您拥有的 ios9 外部键盘 'undo' 栏,而不是全尺寸键盘。
它可能不是很向前兼容,因为如果他们改变撤消栏的大小,这就会刹车。但是,它完成了工作。我欢迎批评,因为一定有更好的方法...
//... 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;
}
此代码支持iOS 8 和iOS 9,inputAccessoryView,具有双重保护常量,为iOS 未来版本的新变化和支持新设备做好准备:
#define gThresholdForHardwareKeyboardToolbar 160.f // it's 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;
}
请注意,硬件键盘可能存在但未使用。
私有 API 解决方案:(必须获取私有头文件 - 使用 RuntimeViewer)。
适用于没有 AppStore 限制的企业应用程序。
#import "UIKit/UIKeyboardImpl.h"
+ (BOOL)isHardwareKeyboardMode
{
UIKeyboardImpl *kbi = [UIKeyboardImpl sharedInstance];
BOOL externalKeyboard = kbi.inHardwareKeyboardMode;
NSLog(@"Using external keyboard? %@", externalKeyboard?@"YES":@"NO");
return externalKeyboard;
}
如果您使工具栏无关紧要,那么键盘就不会出现。通过清空其左右组来做到这一点(至少在 iOS 12.4):
textField.inputAssistantItem.leadingBarButtonGroups = []
textField.inputAssistantItem.trailingBarButtonGroups = []
...如果有帮助,这里有一个快速的观察方法:
// 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)
}
- None 这里的答案对我有用。我有 iPad Air 和 iOS 13.x.
我想,通过检查键盘的高度,我能够做到这一点。而已!请注意,当连接外接键盘时,屏幕键盘的高度约为 50-60px。在此处查看工作演示:https://youtu.be/GKi-g0HOQUc
所以在你的活动中keyboardWillShow
,只需获取键盘高度,看看它是否在 50-60 左右,如果是,那么我们可以假设连接了外接键盘。
@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)
// If with keyboard, the onscreen keyboard height is 55.
// otherwise, the onscreen keyboard has >= 408px in height.
// The 66 digit came from a Whosebug comment that the keyboard height is sometimes around that number.
if keyboard.size.height <= 66 {
hasExternalKeyboard = true
} else {
hasExternalKeyboard = false
}
}
iOS 14 SDK 终于带来 public API 为:GCKeyboard
。检查外接键盘是否连接:
let isKeyboardConnected = GCKeyboard.coalesced != nil
备注:
import GameController
- 您可能需要将其附在
if #available(iOS 14.0, *)
中
在 iOS 15 中,当存在硬件键盘并且您支持 iOS 的早期版本(不支持明确指示存在外部键盘)时,获得的键盘高度和宽度通过:
CGRect kbEndFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
两者的值为 0
因此您可以通过以下方式检测外部硬件:
if(kbEndFrame.size.height == 0)
// External/virtual KB exists
else
// Virtual on screen KB exists
在 iOS9 之前,确定是否连接外部键盘的最可靠方法是监听 UIKeyboardWillShowNotification
并使文本字段成为第一响应者,如 [=13] 中所述=].使用虚拟键盘时会触发通知,但不会在使用外接键盘时触发。
但是,此行为现在已随着 iOS9 发生变化。UIKeyboardWillShowNotification
也会在连接外部键盘时触发,因为现在显示了新的键盘工具栏。
仍然可以检测键盘高度,判断显示的是较小的工具栏还是较大的虚拟键盘。然而,这种方法并不可靠,因为键盘高度在各个测试版之间发生了变化,并且不能指望随着时间的推移保持不变。
是否有更可靠的方法可以与iOS 9一起使用?
您可以尝试使用 Core Bluetooth
检查正在宣传服务的外围设备CBCentralManager *centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
[centralManager scanForPeripheralsWithServices:nil options:nil];
并且您应该实现委托:
- (void)centralManager:(CBCentralManager * _Nonnull)central
didDiscoverPeripheral:(CBPeripheral * _Nonnull)peripheral
advertisementData:(NSDictionary<NSString *,
id> * _Nonnull)advertisementData
RSSI:(NSNumber * _Nonnull)RSSI{
}
您可以在连接外部设备时订阅通知:
[[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];
或者只检索连接设备的列表:
EAAccessoryManager* accessoryManager = [EAAccessoryManager sharedAccessoryManager];
if (accessoryManager)
{
NSArray* connectedAccessories = [accessoryManager connectedAccessories];
NSLog(@"ConnectedAccessories = %@", connectedAccessories);
}
回到最初的问题后,我找到了一个有效的解决方案。
显示常规虚拟键盘时,键盘框架似乎在屏幕尺寸范围内。但是,当连接物理键盘并显示键盘工具栏时,键盘框架位于屏幕外。我们可以检查键盘框架是否在屏幕外,以确定是否显示键盘工具栏。
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
}
}
我使用的是 Sarah Elan 答案的变体。在某些观点上,我对她的方法有疑问。我从来没有完全弄清楚导致问题的原因。但这里有另一种方法可以确定它是否是您拥有的 ios9 外部键盘 'undo' 栏,而不是全尺寸键盘。
它可能不是很向前兼容,因为如果他们改变撤消栏的大小,这就会刹车。但是,它完成了工作。我欢迎批评,因为一定有更好的方法...
//... 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;
}
此代码支持iOS 8 和iOS 9,inputAccessoryView,具有双重保护常量,为iOS 未来版本的新变化和支持新设备做好准备:
#define gThresholdForHardwareKeyboardToolbar 160.f // it's 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;
}
请注意,硬件键盘可能存在但未使用。
私有 API 解决方案:(必须获取私有头文件 - 使用 RuntimeViewer)。
适用于没有 AppStore 限制的企业应用程序。
#import "UIKit/UIKeyboardImpl.h"
+ (BOOL)isHardwareKeyboardMode
{
UIKeyboardImpl *kbi = [UIKeyboardImpl sharedInstance];
BOOL externalKeyboard = kbi.inHardwareKeyboardMode;
NSLog(@"Using external keyboard? %@", externalKeyboard?@"YES":@"NO");
return externalKeyboard;
}
如果您使工具栏无关紧要,那么键盘就不会出现。通过清空其左右组来做到这一点(至少在 iOS 12.4):
textField.inputAssistantItem.leadingBarButtonGroups = []
textField.inputAssistantItem.trailingBarButtonGroups = []
...如果有帮助,这里有一个快速的观察方法:
// 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)
}
- None 这里的答案对我有用。我有 iPad Air 和 iOS 13.x.
我想,通过检查键盘的高度,我能够做到这一点。而已!请注意,当连接外接键盘时,屏幕键盘的高度约为 50-60px。在此处查看工作演示:https://youtu.be/GKi-g0HOQUc
所以在你的活动中keyboardWillShow
,只需获取键盘高度,看看它是否在 50-60 左右,如果是,那么我们可以假设连接了外接键盘。
@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)
// If with keyboard, the onscreen keyboard height is 55.
// otherwise, the onscreen keyboard has >= 408px in height.
// The 66 digit came from a Whosebug comment that the keyboard height is sometimes around that number.
if keyboard.size.height <= 66 {
hasExternalKeyboard = true
} else {
hasExternalKeyboard = false
}
}
iOS 14 SDK 终于带来 public API 为:GCKeyboard
。检查外接键盘是否连接:
let isKeyboardConnected = GCKeyboard.coalesced != nil
备注:
import GameController
- 您可能需要将其附在
if #available(iOS 14.0, *)
中
在 iOS 15 中,当存在硬件键盘并且您支持 iOS 的早期版本(不支持明确指示存在外部键盘)时,获得的键盘高度和宽度通过:
CGRect kbEndFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
两者的值为 0
因此您可以通过以下方式检测外部硬件:
if(kbEndFrame.size.height == 0)
// External/virtual KB exists
else
// Virtual on screen KB exists