【问题标题】:Hide inputAccessoryView if hardware keyboard is connected如果连接了硬件键盘,则隐藏 inputAccessoryView
【发布时间】:2015-03-06 00:29:39
【问题描述】:

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

但是,如果硬件键盘(例如蓝牙键盘)连接到设备,则软件键盘不会按预期显示,但由于某种原因,inputAccessoryView 仍然可见 em> 在屏幕底部。此外,即使使用硬件键盘进行输入,这似乎也会导致触发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,这可能会导致我的应用被拒绝。

【问题讨论】:

  • 是您根本不希望使用硬件键盘输入附件视图的问题,还是屏幕元素像软件键盘一样移动的问题,即使只有附件视图?如果是后者,请确保仅将视图移动到足够的附件视图,而不是软件键盘的某些硬编码高度。
  • 我不希望输入附件视图完全带有硬件键盘,第二点无论如何都应该变得无关紧要,因为不再发送通知
  • @RobK 实际上,当连接外接键盘时,通知仍然发送。当UITextView/Field 激活时,您会收到通知,只是系统将键盘保持在屏幕外。
  • @mbm29414 你是对的,这是我的错误假设。尽管如此,我现在使用不同的方法来计算键盘的 可见 部分,方法是使用 origin.yUIKeyboardFrameEndUserInfoKey
  • @RobK 这是个好方法。

标签: ios keyboard


【解决方案1】:

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

为了正确调整视图大小(无论是否存在硬件键盘),我改编了来自 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
}

【讨论】:

    【解决方案2】:

    这只是@arlomedia 对答案的增强。我所做的是观看 willShow 和 didShow。

    我用来将我的文本视图移动到适当位置的 willShow,以便它以与键盘相同的速度移动。

    我使用的 didShow 使用上述技术检查键盘的表观大小并相应地隐藏/显示附件输入视图。

    重要的是我还将该视图设置为默认隐藏,并且当接收到 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 添加更改,以便隐藏的附件视图不吃水龙头。

    【讨论】:

    • UIKeyboardDidShowNotificationUIKeyboardWillShowNotification 之间切换对我没有任何影响。您提供的默认隐藏附件视图的解决方案实际上只是解决了我的问题,即输入附件视图现在将不会显示根本,因为`self.textField.inputAccessoryView.hidden = NO; ` 在UIKeyboardDidShowNotification(分别为UIKeyboardWillShowNotification)已经发送后将不适用
    • 您是否像我一样将您对隐藏属性的更改包装在 UIView 动画中。这样做的时机可能有助于它工作,因为这段代码正是我在当前项目中所拥有的。
    • 我发现它实际上与动画没有任何关系(可以省略),但是您的解决方案暗示了我导致所有这些问题的错误。它与我动态创建 inputAccessoryView (并且无意中在每个 getter 调用中重新创建它)有关。我会写一个新的答案来解决这个问题。
    【解决方案3】:

    我最后解决这个问题的方法是简单地为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 方法将被调用多次,因此视图一直被覆盖 在我的任务之后。 重用视图通过将其保存到实例变量来解决此问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-20
      • 2019-06-17
      • 2013-06-14
      • 2012-09-16
      • 2011-11-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多