web-dev-qa-db-fra.com

Comment détecter si un clavier externe est présent sur un iPad?

Existe-t-il un moyen de détecter si un clavier externe (Bluetooth ou USB) est connecté à l'iPad?

50
carloe

Un moyen indirect et compatible SDK est de transformer un champ de texte en premier répondant. Si le clavier externe est présent, la notification locale UIKeyboardWillShowNotification ne doit pas être publiée. 

Update: Cela n'est plus vrai depuis iOS 9. Toutefois, vous pouvez utiliser les dimensions du clavier pour déterminer si un clavier matériel ou logiciel est impliqué. Voir Comment détecter de manière fiable si un clavier externe est connecté sur iOS 9? pour plus de détails.

Vous pouvez écouter la notification Darcode "GSEventHardwareKeyboardAttached" (kGSEventHardwareKeyboardAvailabilityChangedNotification), mais il s'agit d'une API privée. Il est donc possible que votre application soit rejetée si vous l'utilisez. Pour vérifier si le matériel externe est présent, utilisez la fonction privée GSEventIsHardwareKeyboardAttached().

UIKit écoute cela et définit la propriété UIKeyboardImpl.isInHardwareKeyboardMode en conséquence, mais là encore, il s'agit d'une API privée.

33
kennytm

Il y a un autre niveau à cela. 

  • Si vous ne disposez pas de inputAccessoryView, vous ne recevrez pas la notification comme le soulignent les explications ci-dessus.
  • Cependant, si vous avez configuré inputAccessoryView pour la vue texte, vous recevrez toujours une notification UIKeyboard lorsque le kbd externe est présent - la logique étant qu'il vous faudra animer votre vue au bon emplacement afin de vous demander l'animation informations contenues dans la notification. 

Heureusement, l'événement contient suffisamment d'informations pour savoir si le kbd sera présenté, même si cela reste un peu compliqué.

Si nous examinons le dictionnaire de notification, nous voyons ces informations:

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

C'était dans Portrait; si nous faisons pivoter l'appareil vers PortraitUpsideDown, nous obtenons:

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

De même, dans LandscapeLeft et LandscapeRight, nous obtenons des emplacements de début et de fin différents.

Hmm ... que signifient ces chiffres? Vous pouvez voir que le kbd est hors écran pour commencer, mais il bouge un peu. Pour aggraver les choses, en fonction de l'orientation du périphérique, les emplacements de kbd sont différents. 

Cependant, nous avons suffisamment d'informations pour savoir ce qui se passe:

  1. Le kbd se déplace juste hors écran, en bas physique du périphérique, à la même hauteur que le paramètre inputAccessoryView (mais masqué par celui-ci).
  2. Ainsi, dans le cas de Portrait, il passe de 1024 à 980 - nous devons avoir un inputAccessoryView d’une hauteur de 44, ce qui est effectivement le cas.
  3. Donc, dans Portrait si la fin y + la hauteur inputAccessoryView == hauteur de l'écran, le kbd n'est pas visible. Vous devez gérer les autres rotations, mais c'est l'idée.
28
user721239

En s'appuyant sur @ user721239, la condition if détermine si le bas du clavier est hors du cadre de self.view. "convertRect" normalise le cadre pour toute orientation.

- (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);
8
T.J.

Même en utilisant un inputAccessoryView sur votre instance UITextView définie sur une instance d'un UIView avec frame CGRectZero fonctionne pour obtenir la livraison des notifications de clavier fonctionnant avec un clavier matériel.

5
user1172004

C'est le code que j'utilise pour obtenir la hauteur du clavier userInfo dans UIKeyboardWillShowNotification. Fonctionne avec les claviers physiques et virtuels.

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;
4
philipkd

Sur la base de ce fil, j'ai assemblé deux méthodes statiques que je peux facilement appeler à partir de méthodes de notification du clavier pour gérer correctement les vues de redimensionnement (généralement UIScrollViews) lorsqu'un clavier apparaît, quel que soit le type (logiciel et matériel):

+ (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;
}
3
Mark

Vous pouvez utiliser ce qui suit, qui calcule également la hauteur de la hauteur du clavier/de la barre d’outils lorsque le clavier matériel est connecté. Vous devrez vous abonner à la notification KeyboardWillShow:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];

puis gérer la notification comme suit:

- (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 
    //  ....
}

Vous pouvez également gérer la notification KeyboardWillHide. Ce sera le feu quand le premier répondeur pour le clavier matériel et logiciel.

- (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 
    //  ...
}

De plus, n'oubliez pas de retirer l'observateur:

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

Comme la plupart des méthodes des réponses précédentes sont obsolètes avec iOS 8 et 9, je croise la trame rapportée du clavier avec la fenêtre actuelle pour obtenir la trame de clavier visible. Ensuite, vous pouvez simplement vérifier si la hauteur a changé.

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
Rivera

Le code suivant vous donne le cadre du clavier pour toutes les orientations, que vous utilisiez une vue plein écran ou la vue détaillée d'une vue fractionnée.

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;

La trame de clavier fournie par la notification est toujours en termes de coordonnées matérielles avec Origin comme coin supérieur droit de l'écran lorsque l'appareil iOS en mode portrait normal avec le bouton d'accueil en bas. La méthode -convertRect: fromView modifie les coordonnées des coordonnées de la fenêtre (= matériel) aux coordonnées de la vue locale.

J'ai constaté qu'avec un clavier Bluetooth, la première fois qu'il y avait une rotation de l'écran, il y avait une seule UIKeyboardDidShowNotification. Rend plus difficile la distinction entre le clavier ancré et les claviers non amarré/divisé et BT.

0
Paul Linsay

Ce n'est pas une réponse directe pour détecter si un clavier externe est présent, mais je le fais pour détecter la hauteur réelle nécessaire pour afficher la ou les vues associées au clavier en bas de l'écran.

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