如何可靠地检测 iOS 9 上是不是连接了外部键盘?

Posted

技术标签:

【中文标题】如何可靠地检测 iOS 9 上是不是连接了外部键盘?【英文标题】:How to reliably detect if an external keyboard is connected on iOS 9?如何可靠地检测 iOS 9 上是否连接了外部键盘? 【发布时间】:2015-11-06 15:16:00 【问题描述】:

ios 9 之前,确定是否连接了外部键盘的最可靠方法是侦听 UIKeyboardWillShowNotification 并将文本字段设置为第一响应者,如 this question 中所述。使用虚拟键盘时会触发通知,但使用外部键盘时不会触发。

然而,这种行为现在在 iOS 9 中有所改变。UIKeyboardWillShowNotification 也会在连接外部键盘时触发,因为现在显示了新的键盘工具栏。

仍然可以检测键盘高度并判断显示的是较小的工具栏还是较大的虚拟键盘。然而,这种方法并不可靠,因为键盘高度在各个测试版之间发生了变化,并且不能指望随着时间的推移保持不变。

有没有更可靠的方法可以在 iOS 9 上使用?

【问题讨论】:

只是一个问题。为什么需要知道是否连接了外接键盘? @agy 为了启用仅在用户使用外部键盘时才应该启用的功能。 这个怎么样? github.com/danielamitay/DAKeyboardControl iOS9下可以,不知道有没有外接键盘的检测。 我想知道是否连接了键盘,这样我就可以显示键盘快捷键(不是通过命令键获得的快捷键)——我想允许输入 1、2、3。不会是一个文本字段。 【参考方案1】:

回到最初的问题后,我找到了一个可行的解决方案。

当显示常规虚拟键盘时,键盘框架似乎在屏幕的尺寸范围内。但是,当连接物理键盘并显示键盘工具栏时,键盘框架位于屏幕外。我们可以检查键盘框架是否在屏幕外,以确定键盘工具栏是否正在显示。

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;
    

斯威夫特

@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
    

【讨论】:

谢谢!在 iOS9 上计算键盘的工具栏高度:CGFloat toolbarHeight = height - keyboard.origin.y 由于某种原因,这第一次工作正常,但如果我关闭并拉回键盘,它不能正确计算并说没有连接键盘。跨度> @trever 是的,这是因为您必须在 if 的 else 子句中重置 hasKeyboard 属性 我在 iOS 11 iPad 上测试了这个,keyboard.origin.y + keyboard.size.height 的值等于self.view.frame.size.height;但是,我发现 keyboard.size.height 在所有情况下都等于 55 我测试了 iOS 12.1 新 iPad Pro,toolbarHeight 是 69,所以不再是 55 高度了。【参考方案2】:

iOS 14 SDK 终于为此带来了公共 API:GCKeyboard。检查是否连接了外接键盘:

let isKeyboardConnected = GCKeyboard.coalesced != nil

注意事项:

import GameController 您可能需要将其包含在if #available(iOS 14.0, *)

【讨论】:

我可以确认这是可行的,谢谢!但是,我还需要检查键盘是否“活动”。对开式键盘在卡入到位后立即注册,但我必须找到一种方法来查看它是否已折叠并被使用。 我也有同样的问题。折叠对开键盘会导致上述内容返回 true。【参考方案3】:

此代码支持 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;

注意,硬件键盘可能存在但未使用。

【讨论】:

【参考方案4】:

我正在使用 Sarah Elan 的答案的变体。在某些观点上,我对她的做法有意见。我从来没有完全了解导致问题的原因。但这里有另一种方法来确定它是否是您拥有的 ios9 外部键盘“撤消”栏,而不是全尺寸键盘。

它可能不是很向前兼容,因为如果他们改变撤消栏的大小,这会刹车。但是,它完成了工作。我欢迎批评,因为一定有更好的方法......

//... 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;

【讨论】:

这是我最初走的路,它确实很好地完成了工作。当 iOS9 beta 版本之间的大小从 49 变为 55 时,问题就出现了。它似乎不再可靠了。 您不必对键盘的工具栏大小进行硬编码。使用这条线:CGFloat toolbarHeight = UIScreen.mainScreen().bounds.height - keyboardEndFrame.origin.y SoftDesigner,这个值没有用。 当设备处于纵向模式或横向模式并且打开或关闭拼写检查时,键盘大小不同。当有人使用外部开发的软件键盘时,Morover 的大小会有所不同。【参考方案5】:

私有 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;

【讨论】:

[UIKeyboardImpl sharedInstance] 在 iOS 14 上抛出异常 还为 iOS 14 添加了专门的答案【参考方案6】:

如果您使工具栏无关紧要,则键盘不会显示。通过清空其左右组来做到这一点(至少在 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)

【讨论】:

【参考方案7】:
    这里没有一个答案对我有用。我有装有 iOS 13.x 的 iPad Air。

我想,我可以通过检查键盘的高度来做到这一点。而已!请注意,当连接外接键盘时,屏幕键盘的高度约为 50-60 像素。在此处查看工作演示: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 *** comment that the keyboard height is sometimes around that number.
        if keyboard.size.height <= 66 
            hasExternalKeyboard = true
         else 
            hasExternalKeyboard = false
        
    

【讨论】:

【参考方案8】:

在 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

【讨论】:

【参考方案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


【讨论】:

我想检测是否连接了任何类型的外部键盘。我不是在寻找特定的配件。 @SarahElan 这可能会引导您找到解决方案。 你能扩展一下吗?它需要更多信息才能成为有用的答案。【参考方案10】:

您可以在连接外部设备时订阅通知:

[[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);

【讨论】:

那么你如何判断任何连接的配件是否是键盘? 我用条形码扫描仪试了一下,它没有出现在这个列表中

以上是关于如何可靠地检测 iOS 9 上是不是连接了外部键盘?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 UITextField 中的外部键盘检测“命令+退格”

如何在 C 预处理器中可靠地检测 Mac OS X、iOS、Linux、Windows? [复制]

netty 可以可靠地检测通道关闭/断开连接吗?

iOS:如何检测硬件蓝牙键盘上的转义/控制键?

我可以检测到 iOS 设备连接了外部 GPS 吗?

开机时是不是有可靠的方法重新连接配对的 ZAGG - Logitech 蓝牙键盘?