如何检测 iPad 上是不是存在外接键盘?
Posted
技术标签:
【中文标题】如何检测 iPad 上是不是存在外接键盘?【英文标题】:How can I detect if an external keyboard is present on an iPad?如何检测 iPad 上是否存在外接键盘? 【发布时间】:2011-02-23 00:14:30 【问题描述】:有没有办法检测外部(蓝牙或 USB)键盘是否连接到 iPad?
【问题讨论】:
【参考方案1】:一种间接且对 SDK 安全的方法是将文本字段设置为第一响应者。如果存在外部键盘,则不应发布UIKeyboardWillShowNotification
本地通知。
更新:自 ios 9 起不再适用,但您可以使用键盘尺寸来确定是否涉及硬件或软件键盘。详情请见How to reliably detect if an external keyboard is connected on iOS 9?。
您可以收听"GSEventHardwareKeyboardAttached"
(kGSEventHardwareKeyboardAvailabilityChangedNotification
) Darwin 通知,但这是一个私有 API,因此如果您使用它,您的应用可能会被拒绝。要检查外部硬件是否存在,请使用私有 GSEventIsHardwareKeyboardAttached()
函数。
UIKit 会监听这个并相应地设置UIKeyboardImpl.isInHardwareKeyboardMode
属性,但这又是私有 API。
【讨论】:
有没有办法通过公共电话做到这一点?让我的应用程序被拒绝有点违背了最初编写它的目的 =) 我想指出,当您设置了附件视图时,始终会调用 WillShowNotification。所以如果你试图测试是否显示一个附件视图,这将无法正常工作。 旧的cmets,但值得一提的是,在iOS 9上,即使连接了外部键盘,也会触发UIKeyboardWillShowNotification。这是因为 iOS 9 在虚拟键盘上引入了一个带有粘贴/撤消/重做操作的工具栏,即使连接了外部键盘也会显示。 那么现在iOS9怎么做呢? 请参阅***.com/q/31991873/1873374,了解如何为 iOS9 执行此操作。【参考方案2】:这还有另一个层次。
如果您没有 inputAccessoryView,您将不会收到上述说明所指出的通知。 但是,如果您为文本视图设置了 inputAccessoryView,那么当外部 kbd 存在时,您仍会收到 UIKeyboard 通知——逻辑是您需要将视图设置为正确的位置,以便您需要通知中包含的动画信息。幸运的是,事件中有足够的信息来确定是否会呈现 kbd,尽管它仍然有点涉及。
如果我们检查通知字典,我们会看到以下信息:
UIKeyboardFrameBeginUserInfoKey = NSRect: 0, 1024, 768, 308
UIKeyboardFrameEndUserInfoKey = NSRect: 0, 980, 768, 308
那是纵向的;如果我们将设备旋转到 PortraitUpsideDown 我们得到:
UIKeyboardFrameBeginUserInfoKey = NSRect: 0, -308, 768, 308
UIKeyboardFrameEndUserInfoKey = NSRect: 0, -264, 768, 308
类似地,在 LandscapeLeft 和 LandscapeRight 中,我们得到不同的开始和结束位置。
嗯……这些数字是什么意思?您可以看到 kbd 在屏幕外启动,但它确实移动了一点。更糟糕的是,根据设备方向,kbd 位置是不同的。
但是,我们确实有足够的信息来弄清楚发生了什么:
-
kbd 从设备物理底部的屏幕外移动到与 inputAccessoryView 相同的高度(但被它遮挡)
所以在 Portrait 情况下,它从 1024 移动到 980 - 我们必须有一个高度为 44 的 inputAccessoryView,确实如此。
所以在纵向中,如果结束 y + inputAccessoryView 高度 == 屏幕高度,则 kbd 不可见。您需要处理其他旋转,但这就是我们的想法。
【讨论】:
如果关联了 inputAccessoryView,这是实现它的唯一方法。这里值得注意的一点是从 iOS 8 开始,他们改变了在设备上设置原点的方式。每次任何方向左上角都是 (0,0)。【参考方案3】:以@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);
【讨论】:
【参考方案4】:即使在 UITextView 实例上使用 inputAccessoryView 设置为带有 CGRectZero 框架的 UIView 实例,也可以使用硬件键盘来传递键盘通知。
【讨论】:
【参考方案5】:这是我用来从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
语句会更简洁。【参考方案6】:
基于这个线程,我组装了两个静态方法,我可以轻松地从键盘通知方法调用它们,以在键盘出现时正确调整视图(通常是 UIScrollViews),无论类型如何(软件与硬件):
+ (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;
【讨论】:
【参考方案7】:由于之前答案中的大多数方法已在 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!
【讨论】:
【参考方案8】:您可以使用以下方法计算连接硬件键盘时键盘/工具栏高度的高度。您需要订阅 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 通知。这将在硬件和软件键盘的 firstResponder 时触发。
- (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];
【讨论】:
【参考方案9】:以下代码为您提供了所有方向的键盘框架,无论您使用的是全屏视图还是拆分视图的详细视图。
NSDictionary* info = [aNotification userInfo];
CGRect frame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect keyboardEndFrame = [self.view convertRect:frame fromView:nil]; // The raw frame values are physical device coordinate.
CGSize keyboardSize = keyboardEndFrame.size;
当iOS设备正常竖屏且home键位于底部时,通知传递的键盘框架始终以硬件坐标为原点,以屏幕右上角为原点。 -convertRect:fromView 方法将坐标从窗口坐标(=硬件)更改为本地视图坐标。
我发现使用蓝牙键盘时,您会在第一次出现屏幕旋转时获得一个 UIKeyboardDidShowNotification,但之后就没有了。使对接键盘与未对接/拆分键盘和 BT 键盘区分开来变得更加困难。
【讨论】:
【参考方案10】:这不是检测是否存在外部键盘的直接答案,但我这样做是为了检测在屏幕底部显示与键盘相关的视图所需的实际高度。
CGRect keyboardFrame = [[[notification userInfo] objectForKey:@"UIKeyboardFrameEndUserInfoKey"] CGRectValue];
CGFloat keyboardRelatedViewsHeight = self.view.window.frame.size.height - keyboardFrame.origin.y;
【讨论】:
【参考方案11】:对于任何使用 Xamarin.iOS 的人
NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillShowNotification, OnKeyboardNotification);
然后添加 OnKeyboardNotification 方法,
private void OnKeyboardNotification(NSNotification obj)
var window = UIApplication.SharedApplication.KeyWindow;
var view = window.RootViewController.View;
String eventName = (UIKeyboardExtensions.HardwareKeyboardConnected(obj, view)) ? "keyboard_hardware" : "keyboard_software";
【讨论】:
【参考方案12】:@philosophistry 的回答对我有用。该解决方案在 iOS 8 上不太复杂:
CGRect keyboardRect = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat deviceHeight = [UIScreen mainScreen].bounds.size.height;
CGFloat keyboardHeight = deviceHeight - keyboardRect.origin.y;
NSLog(@"actualKeyboardHeight = %f", keyboardHeight);
【讨论】:
方向呢?以上是关于如何检测 iPad 上是不是存在外接键盘?的主要内容,如果未能解决你的问题,请参考以下文章