【发布时间】:2011-02-23 00:14:30
【问题描述】:
有没有办法检测外部(蓝牙或 USB)键盘是否连接到 iPad?
【问题讨论】:
有没有办法检测外部(蓝牙或 USB)键盘是否连接到 iPad?
【问题讨论】:
一种间接且对 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。
【讨论】:
这还有另一个层次。
幸运的是,事件中有足够的信息来确定是否会呈现 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 位置是不同的。
但是,我们确实有足够的信息来弄清楚发生了什么:
【讨论】:
以@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);
【讨论】:
即使在 UITextView 实例上使用 inputAccessoryView 设置为带有 CGRectZero 框架的 UIView 实例,也可以使用硬件键盘来传递键盘通知。
【讨论】:
这是我用来从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 语句会更简洁。
基于这个线程,我组装了两个静态方法,我可以轻松地从键盘通知方法调用它们,以在键盘出现时正确调整视图(通常是 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;
}
【讨论】:
由于之前答案中的大多数方法已在 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!
}
【讨论】:
您可以使用以下方法计算连接硬件键盘时键盘/工具栏高度的高度。您需要订阅 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];
}
【讨论】:
以下代码为您提供了所有方向的键盘框架,无论您使用的是全屏视图还是拆分视图的详细视图。
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 键盘区分开来变得更加困难。
【讨论】:
这不是检测是否存在外部键盘的直接答案,但我这样做是为了检测在屏幕底部显示与键盘相关的视图所需的实际高度。
CGRect keyboardFrame = [[[notification userInfo] objectForKey:@"UIKeyboardFrameEndUserInfoKey"] CGRectValue];
CGFloat keyboardRelatedViewsHeight = self.view.window.frame.size.height - keyboardFrame.origin.y;
【讨论】:
对于任何使用 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";
}
【讨论】:
@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);
【讨论】: