如果连接了硬件键盘,则隐藏 inputAccessoryView

Hide inputAccessoryView if hardware keyboard is connected

与这个问题类似:iPad: Detecting External Keyboard,我正在开发一个 iPad 应用程序,它使用带有自定义 inputAccessoryView 的文本字段来为虚拟键盘提供额外的功能。

但是,如果 硬件键盘(例如蓝牙键盘)连接到设备,则软件键盘不会按预期显示,但由于某些原因,inputAccessoryView 仍然可见 在屏幕底部。此外,这似乎会导致触发 UIKeyboardDidShowNotification(因此向上移动我的视图以避免被实际上不存在的键盘遮挡),即使使用硬件键盘进行输入也是如此。

我找到了几种检测硬件键盘是否已连接的解决方案,但它们都在接收到 UIKeyboardDidShowNotification 后检查状态 ,此时 inputAccessoryView 是已经可见(例如 How can I detect if an external keyboard is present on an iPad?)。

我正在寻找一种仅在未连接硬件键盘时显示 inputAccessoryView 的方法。因此,我需要知道在 触发 UIKeyboardDidShowNotification 之前 是否连接了硬件键盘。

这里提供的可接受的解决方案 How can I detect if an external keyboard is present on an iPad? 对我来说不是选择,因为它们使用私有 API,这可能会导致我的应用程序被拒绝。

IIRC,出现软件键盘时视图不会自行调整大小。我正在通过 UIKeyboardDidShow 通知触发的 keyboardDidShow 方法调整视图大小。因此,在该方法中检测硬件键盘与软件键盘应该就足够了,然后您可以跳过 table 调整大小并隐藏输入附件视图(或调整 table 调整大小以适应输入附件视图,如果你想让它可见的话)。

无论是否存在硬件键盘,为了正确调整视图大小,我改编了 this answer:

中的代码
- (void)keyboardDidShow:(NSNotification *)aNotification {
    CGRect keyboardBeginFrame = [[[aNotification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
    CGRect keyboardEndFrame = [[[aNotification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    float keyboardHeight = ABS(keyboardBeginFrame.origin.y - keyboardEndFrame.origin.y); // the keyboard will move by an amount equal to its height when it appears; ABS is needed for upside-down orientations

    // now you can resize your views based on keyboardHeight
    // that will be the height of the inputAccessoryView if a hardware keyboard is present
}

如果您想让 inputAccessoryView 保持可见,这就是您所需要的。为了也隐藏它,我认为您需要设置一个实例变量,以便您可以在 keyboardDidShow:

中访问它
UIView *currentInputAccessoryView;

- (void)textFieldDidBeginEditing:(UITextField *)textField {
    self.currentInputAccessoryView = textField.inputAccessoryView;
}

- (void)textViewDidBeginEditing:(UITextView *)textView {
    self.currentInputAccessoryView = textView.inputAccessoryView;
}

- (void)keyboardDidShow:(NSNotification *)aNotification {
    CGRect keyboardBeginFrame = [[[aNotification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
    CGRect keyboardEndFrame = [[[aNotification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    float keyboardHeight = ABS(keyboardBeginFrame.origin.y - keyboardEndFrame.origin.y); // the keyboard will move by an amount equal to its height when it appears; ABS is needed for upside-down orientations

    if (keyboardHeight == 44) {
        self.currentInputAccessoryView.hidden = YES;
        keyboardHeight = 0;
    }

    // now you can resize your views based on keyboardHeight
    // that will be 0 if a hardware keyboard is present
}

这只是@arlomedia 回答的增强版。我所做的是监视 willShow 和 didShow。

我使用 willShow 将我的 textview 移动到位,以便它以与键盘相同的速度移动。

我使用的 didShow 使用上述技术检查键盘的外观尺寸,hide/show 相应地使用 accessoryInputView。

重要的是我还设置了默认隐藏视图,当收到 willHide 事件时,它会再次隐藏。

- (void) addKeyboardObserver {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardHidden:) name:UIKeyboardWillHideNotification object:nil];
}

- (void) removeKeyboardObserver {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

- (void)keyboardWillShow:(NSNotification*)notification {
    CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    // If we're on iOS7 or earlier and landscape then the height is in the
    // width.
    //
    if ((IS_LANDSCAPE == YES) && (IS_IOS8_OR_LATER == NO)) {
        keyboardSize.height = keyboardSize.width;
    }

    NSNumber *rate = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey];

    CGRect textFieldFrame = self.textField.frame;
    textFieldFrame.origin.y = ([Util screenHeight] - keyboardSize.height) - textFieldFrame.size.height - [Util scaledHeight:10.0];

    // Move the text field into place.
    //
    [UIView animateWithDuration:rate.floatValue animations:^{
        self.answerTextField.frame = textFieldFrame;
    }];

    keyboardShown = YES;
}

- (void)keyboardDidShow:(NSNotification*)notification {
    CGRect keyboardBeginFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
    CGRect keyboardEndFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
    CGSize keyboardSize = keyboardBeginFrame.size;

    // If we're on iOS7 or earlier and landscape then the height is in the
    // width.
    //
    if ((IS_LANDSCAPE == YES) && (IS_IOS8_OR_LATER == NO)) {
        keyboardSize.height = ABS(keyboardBeginFrame.origin.x - keyboardEndFrame.origin.x); // the keyboard will move by an amount equal to its height when it appears; ABS is needed for upside-down orientations
    } else {
        keyboardSize.height = ABS(keyboardBeginFrame.origin.y - keyboardEndFrame.origin.y); // the keyboard will move by an amount equal to its height when it appears; ABS is needed for upside-down orientations
    }

    NSNumber *rate = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey];

    [UIView animateWithDuration:rate.floatValue animations:^{
        if (keyboardSize.height <= self.accessoryBar.frame.size.height) {
            self.textField.inputAccessoryView.hidden = YES;
            self.answerTextField.inputAccessoryView.userInteractionEnabled = NO;
        } else {
            self.textField.inputAccessoryView.hidden = NO;
            self.answerTextField.inputAccessoryView.userInteractionEnabled = YES;
        }
    }];

    keyboardShown = YES;
}

- (void)keyboardHidden:(NSNotification*)notification {

    NSNumber *rate = notification.userInfo[UIKeyboardAnimationDurationUserInfoKey];

    // Remove/hide the accessory view so that next time the text field gets focus, if a hardware
    // keyboard is used, the accessory bar is not shown.
    //
    [UIView animateWithDuration:rate.floatValue animations:^{
        self.textField.inputAccessoryView.hidden = YES;
        self.answerTextField.inputAccessoryView.userInteractionEnabled = NO;
    }];

    keyboardShown = NO;
}

注意 编辑以添加对 userInteractionEnabled 的更改,以便隐藏的 accessoryView 不会占用水龙头。

我解决这个问题的最终方法是简单地为 UIKeyboardWillShowNotification 添加一个观察者 ...

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

.. 并隐藏之前存储在实例变量中的 inputAccessoryView

// Called when the UIKeyboardWillShowNotification is sent.
- (void)keyboardWillShow:(NSNotification*)notification
{
    NSLog(@"keyboardWillShow");

    // get the frame end user info key
    CGRect kbEndFrame = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];

    // calculate the visible portion of the keyboard on the screen
    CGFloat height = [[UIScreen mainScreen] bounds].size.height - kbEndFrame.origin.y;

    // check if there is a input accessorry view (and no keyboard visible, e.g. hardware keyboard)
    if (self.activeTextField && height <= self.activeTextField.inputAccessoryView.frame.size.height) {

        NSLog(@"hardware keyboard");

        self.activeTextField.inputAccessoryView.hidden = YES;
    } else {

        NSLog(@"software keyboard");

        self.activeTextField.inputAccessoryView.hidden = NO;
    }
}

原来问题是我在 getter 方法中动态创建自定义 UITextField 子类的 inputAccessoryView 引起的。我在每次调用 getter 时无意中 重新创建了 视图,而不是通过惰性实例化重用实例变量。这导致我对视图的所有分配都被忽略,因为显然 getter 方法将被调用 多次 当访问文本字段并显示键盘并因此显示视图时在我完成作业后一直被覆盖通过将视图保存到实例变量来重用视图解决了这个问题。