如何检测iPad上是否连接了外部键盘?

54

有没有办法检测iPad是否连接了外部(蓝牙或usb)键盘?

13个回答

36
一种间接且SDK-safe的方法是将文本字段设置为第一响应者。如果外部键盘存在,则不会发布UIKeyboardWillShowNotification本地通知。

更新:自iOS 9以后,这已不再正确,但您可以使用键盘尺寸来确定硬件或软件键盘是否涉及。有关详细信息,请参见如何可靠地检测iOS 9上是否连接了外部键盘?

您可以侦听"GSEventHardwareKeyboardAttached" (kGSEventHardwareKeyboardAvailabilityChangedNotification) Darwin通知,但这是私有API,因此如果使用此API可能会导致您的应用被拒绝。要检查外部硬件是否存在,请使用私有函数GSEventIsHardwareKeyboardAttached()
UIKit会监听此内容并相应地设置UIKeyboardImpl.isInHardwareKeyboardMode属性,但这也是私有API。

4
能否通过公共调用的方式来完成这个任务?我的应用被拒绝有点违背了我编写它的初衷 =) - carloe
7
我想指出,当你设置了 accessoryView 时,WillShowNotification 总是会被调用。因此,如果你试图测试是否要显示 accessoryView,那么这不会正常工作。 - Jason
15
虽然这些评论有些陈旧,但提到在iOS 9上即使连接外部键盘也会触发UIKeyboardWillShowNotification是一件好事。这是因为iOS 9引入了一个工具栏,在虚拟键盘上显示粘贴/撤销/重做等操作,即使连接了外部键盘也会显示。 - Joey Carson
4
现在如何在iOS9上实现这个? - Gerald Eersteling
4
请参考https://dev59.com/ZlwY5IYBdhLWcg3w_L-W以了解如何在iOS9上实现此操作的讨论。 - Sarah Elan
显示剩余4条评论

29

还有另外一个层次需要考虑。

  • 如果你没有设置inputAccessoryView,就像上面的解释所指出的那样,你将不会接收到通知。
  • 但是,如果你已经为文本视图设置了inputAccessoryView,那么当外部键盘出现时,你仍然会收到UIKeyboard通知——其逻辑是你需要将你的视图动画到正确的位置,因此你需要通知中包含的动画信息。

幸运的是,事件中有足够的信息来确定键盘是否会被呈现,尽管这仍然有点复杂。

如果我们检查通知字典,我们会看到以下信息:

UIKeyboardFrameBeginUserInfoKey = NSRect: {{0, 1024}, {768, 308}}
UIKeyboardFrameEndUserInfoKey = NSRect: {{0, 980}, {768, 308}}

那是在竖屏模式下;如果我们将设备旋转为倒立的竖屏模式,我们会得到:

UIKeyboardFrameBeginUserInfoKey = NSRect: {{0, -308}, {768, 308}}
UIKeyboardFrameEndUserInfoKey = NSRect: {{0, -264}, {768, 308}}

同样在 LandscapeLeft 和 LandscapeRight 方向,我们得到了不同的起始和结束位置。

嗯...这些数字是什么意思?你可以看到键盘一开始是离屏幕外的,但它确实会移动一点。更糟糕的是,根据设备的方向,键盘的位置也不同。

不过,我们已经有足够的信息来弄清楚发生了什么:

  1. 键盘从设备物理底部刚好离开屏幕开始移动,直到与输入辅助视图处于相同高度(但被其遮挡)。
  2. 所以,在竖屏模式下,它从 1024 移动到了 980,我们必须有一个高度为 44 的输入辅助视图,而事实上确实如此。
  3. 因此,在竖屏模式下,如果结束的 y 坐标加上输入辅助视图的高度等于屏幕的高度,则键盘将不可见。需要处理其他旋转情况,但就是这个思路。

这是实现它的唯一方法,如果关联了inputAccessoryView。值得注意的是,从iOS 8开始,他们改变了设备上设置原点的方式。每次在任何方向上,左上角都是(0,0)。 - cirronimbo

8

在@user721239的基础上,if条件确定键盘底部是否超出了self.view的框架。 "convertRect"可使框架在任何方向下都正常化。

- (void)keyboardWillShow:(NSNotification *)notification {
keyboardFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
keyboardFrame = [self.view convertRect:keyboardFrame fromView:nil]; // convert orientation
keyboardSize = keyboardFrame.size;
//NSLog(@"keyboardFrame.origin.y = %f", keyboardFrame.origin.y);
//NSLog(@"keyboardFrame.size.height = %f", keyboardFrame.size.height);
BOOL hardwareKeyboardPresent = FALSE;;
if ((keyboardFrame.origin.y + keyboardFrame.size.height) > (self.view.frame.size.height+self.navigationController.navigationBar.frame.size.height)) {
    hardwareKeyboardPresent = TRUE;
}
//NSLog(@"bottomOfKeyboard = %f", bottomOfKeyboard);
//NSLog(@"self.view.frame.size.height = %f", self.view.frame.size.height);

5

即使在UITextView实例上使用一个设置为CGRectZero的UIView实例的inputAccessoryView,也可以通过硬件键盘获得键盘通知。


4

这是我用来从UIKeyboardWillShowNotification中的键盘userInfo获取高度的代码。适用于物理键盘和虚拟键盘。

NSValue* aValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];

CGRect keyboardRect = [aValue CGRectValue];

CGFloat deviceHeight = [UIScreen mainScreen].bounds.size.height;
CGFloat deviceWidth = [UIScreen mainScreen].bounds.size.width;

CGFloat newKeyboardHeight;

if (interfaceOrientation == UIInterfaceOrientationPortrait)
    newKeyboardHeight = deviceHeight - keyboardRect.origin.y;
else if (interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
    newKeyboardHeight = keyboardRect.size.height + keyboardRect.origin.y;
else if (interfaceOrientation == UIInterfaceOrientationLandscapeLeft)
    newKeyboardHeight = deviceWidth - keyboardRect.origin.x;
else
    newKeyboardHeight = keyboardRect.size.width + keyboardRect.origin.x;

一个 switch 语句会更加简洁。 - Rivera

3

根据这个线程,我已经编写了两个静态方法,我可以轻松从键盘通知方法中调用它们,以便在键盘出现时正确地调整视图的大小(通常是UIScrollViews),无论类型(软件 vs 硬件):

+ (void)keyboardWillShowHide:(NSNotification *)notification
                  inView:(UIView *)view
              adjustView:(UIView *)viewToAdjust
{
    // How much should we adjust the view's frame by?
    CGFloat yOffset = [SMKeyboardUtil keyboardOffsetForKeyboardNotification:notification
                                                                        inView:view];
    CGRect viewFrame = viewToAdjust.frame;
    viewFrame.size.height -= yOffset;

    // Get the animation parameters being used to show the keyboard. We'll use the same animation parameters as we
    // resize our view.
    UIViewAnimationCurve animationCurve;
    NSTimeInterval animationDuration;
    [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
    [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];

    // Resize the view's frame to subtract/add the height of the keyboard (and any inputAccessoryView)
    [UIView beginAnimations:@"animate resiz view" context:nil];
    [UIView setAnimationDuration:animationDuration];
    [UIView setAnimationCurve:animationCurve];
    [viewToAdjust setFrame:viewFrame];
    [UIView commitAnimations];

}

+ (CGFloat)keyboardOffsetForKeyboardNotification:(NSNotification *)notification
                                      inView:(UIView *)view
{
    NSAssert(notification.userInfo[UIKeyboardFrameBeginUserInfoKey], @"Invalid keyboard notification");

    // Get the frame of keyboard from the notification
    CGRect keyboardFrameBeginRaw = [notification.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
    CGRect keyboardFrameEndRaw = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];

    // Because the frame we get from the notification is raw screen coordinates, without accounting for device orientation,
    // we need to convert the frame to be relative to our view.
    CGRect keyboardFrameBegin = [view convertRect:keyboardFrameBeginRaw fromView:nil];
    CGRect keyboardFrameEnd = [view convertRect:keyboardFrameEndRaw fromView:nil];

    // We could examine the size of the frame, but this does not account for hardware keyboards. Instead,
    // we need to need the delta between the start and end positions to determine how much to modify
    // the size of our view.
    return keyboardFrameBegin.origin.y - keyboardFrameEnd.origin.y;
}

2
您可以使用以下代码,它还会在连接硬件键盘时计算键盘/工具栏高度。您需要订阅KeyboardWillShow通知:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];

然后如下处理通知:

- (void)keyboardWillShow:(NSNotification *)notification
{       
    // Information we want to determine from notification
    BOOL isHardwareKB = NO;
    CGFloat keyboardHeight;

    // Notification info
    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;

    // Determine if hardware keyboard fired this notification
    if ((keyboard.origin.y + keyboard.size.height) > height) {
        isHardwareKB = YES;
        keyboardHeight = height - keyboard.origin.y;    // toolbar height
    } else {
        isHardwareKB = NO;
        // As this value can change depending on rotation
        keyboardHeight = MIN(keyboardFrame.size.width, keyboardFrame.size.height);
    }

    // adjust view ui constraints ext ext depending on keyboard height 
    //  ....
}

您也可以处理KeyboardWillHide通知。当硬件和软件键盘的第一响应者时,它将被触发。

- (void)keyboardWillShow:(NSNotification *)notification
{       
    // Information we want to determine from notification
    BOOL isHardwareKB; // this is irrelevant since it is hidden
    CGFloat keyboardHeight = 0; // height is now 0

    // Do any view layout logic here for keyboard height = 0 
    //  ...
}

不要忘记移除观察者:

-(void) dealloc {
      [[NSNotificationCenter defaultCenter] removeObserver:self];
      [super dealloc];
}

2

由于先前回答中的大多数方法已在iOS 8和9中被弃用,因此我将键盘报告的框架与当前窗口相交,以获取实际可见的键盘框架。然后,您只需检查高度是否发生了变化。

CGRect reportedKeyboardFrameRaw = [[[notification userInfo] valueForKey: UIKeyboardFrameEndUserInfoKey] CGRectValue];

CGRect reportedKeyboardFrame = [self.view.window convertRect: reportedKeyboardFrameRaw fromWindow:nil];

CGRect visibleKeyboardFrame = CGRectIntersection(reportedKeyboardFrame, self.view.window.frame);

if (reportedKeyboardFrame.size.height != visibleKeyboardFrame.size.height)
{
    // External keyboard present!
}

1
这是一个旧帖子,但从开始,我们现在可以通过GameController框架使用GCKeyboardGCKeyboardDidConnect/GCKeyboardDidDisconnect通知来跟踪硬件键盘。您可以像这样做:
import GameController

class ViewController: UIViewController {
    var isHardwareKeyboardConnected: Bool

    init() {
        isHardwareKeyboardConnected = GCKeyboard.coalesced != nil
        super.init(nibName: nil, bundle: nil)
        startObservingHardwareKeyboard()
    }

    func startObservingHardwareKeyboard() {
        NotificationCenter.default.addObserver(self, selector: #selector(hardwareKeyboardDidConnect), name: .GCKeyboardDidConnect, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(hardwareKeyboardDidDisconnect), name: .GCKeyboardDidDisconnect, object: nil)
    }

    @objc func hardwareKeyboardDidConnect(_ notification: Notification) {
        print("[Keyboard] Hardware keyboard did connect")
        isHardwareKeyboardConnected = true
    }

    @objc func hardwareKeyboardDidDisconnect(_ notification: Notification) {
        print("[Keyboard] Hardware keyboard did disconnect")
        isHardwareKeyboardConnected = false
    }
}

0

这不是直接检测外部键盘是否存在的答案,而是我正在执行此操作以检测在屏幕底部显示与键盘相关的视图所需的实际高度。

CGRect keyboardFrame = [[[notification userInfo] objectForKey:@"UIKeyboardFrameEndUserInfoKey"] CGRectValue];
CGFloat keyboardRelatedViewsHeight = self.view.window.frame.size.height - keyboardFrame.origin.y;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接