Как надежно определить, подключена ли внешняя клавиатура на iOS 9?
Ранее для iOS 9 самым надежным методом определения того, подключена ли внешняя клавиатура, является прослушивание UIKeyboardWillShowNotification
и создание текстового поля первым ответчиком, как описано в this вопрос. Уведомление будет срабатывать при использовании виртуальной клавиатуры, но не будет срабатывать при использовании внешней клавиатуры.
Однако это поведение теперь изменилось с iOS 9. UIKeyboardWillShowNotification
также срабатывает, когда подключена внешняя клавиатура, так как теперь отображается новая панель инструментов клавиатуры.
По-прежнему можно определить высоту клавиатуры и принять решение, будет ли отображаться меньшая панель инструментов или большая виртуальная клавиатура. Однако этот метод не является надежным, поскольку высота клавиатуры изменилась между различными бета-версиями и не может считаться неизменной с течением времени.
Есть ли более надежный метод, который можно использовать с iOS 9?
Ответы
Ответ 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;
}
}
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
}
}
Ответ 2
Этот код поддерживает iOS 8 и iOS 9, inputAccessoryView, имеет двойную защищенную константу, которая будет готова для новых изменений в будущих версиях iOS и для поддержки новых устройств:
#define gThresholdForHardwareKeyboardToolbar 160.f // it 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;
}
Примечание. Аппаратная клавиатура может присутствовать, но не использоваться.
Ответ 3
Я использую вариант ответа Сары Элан. У меня возникли проблемы с ее подходом в определенных взглядах. Я так и не добрался до сути проблемы. Но вот еще один способ определить, является ли это клавиатурой "undo" внешней клавиатуры 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;
}
Ответ 4
Частное API-решение: (нужно захватить частный заголовочный файл - использовать RuntimeViewer).
Хорошо работает для корпоративных приложений, где у вас нет ограничений AppStore.
#import "UIKit/UIKeyboardImpl.h"
+ (BOOL)isHardwareKeyboardMode
{
UIKeyboardImpl *kbi = [UIKeyboardImpl sharedInstance];
BOOL externalKeyboard = kbi.inHardwareKeyboardMode;
NSLog(@"Using external keyboard? %@", [email protected]"YES":@"NO");
return externalKeyboard;
}
Ответ 5
Если вы сделаете панель инструментов неактуальной, клавиатура не появится. Сделайте это, отключив левую и правую группы (по крайней мере, в 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)
}
Ответ 6
Вы можете подписаться на уведомление при подключении внешнего устройства:
[[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);
}
Ответ 7
Вы можете попробовать проверить периферийные устройства, которые являются рекламными услугами, используя 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{
}