如何从 iOS 应用程序上的自定义键盘检索击键?

2024-03-13

我需要为我的 iPhone 应用程序构建一个自定义键盘。之前的问题和答案 https://stackoverflow.com/questions/4643167/iphone-objc-how-to-create-a-custom-keyboard该主题的重点是自定义键盘的视觉元素,但我试图了解如何从此键盘检索击键。

Apple 提供了 inputView 机制,可以轻松地将自定义键盘与 UITextField 或 UITextView 关联起来,但它们不提供将生成的击键发送回关联对象的功能。根据这些对象的典型委托,我们期望三种功能:一种是普通字符,一种是退格键,一种是回车键。然而,似乎没有人清楚地定义这些功能或如何使用它们。

如何为我的 iOS 应用程序构建自定义键盘并从中检索击键?


格雷格的方法应该可行,但我有一种方法不需要告诉键盘有关文本字段或文本视图的信息。事实上,您可以创建键盘的单个实例并将其分配给多个文本字段和/或文本视图。键盘可以识别哪个是第一响应者。

这是我的方法。我不会展示任何用于创建键盘布局的代码。这是最简单的部分。此代码显示了所有管道。

编辑:这已更新以正确处理UITextFieldDelegate textField:shouldChangeCharactersInRange:replacementString: and UITextViewDelegate textView:shouldChangeTextInRange:replacementText:.

头文件:

@interface SomeKeyboard : UIView <UIInputViewAudioFeedback>

@end

实施文件:

@implmentation SomeKeyboard {
    id<UITextInput> _input;
    BOOL _tfShouldChange;
    BOOL _tvShouldChange;
}

- (id)init {
    self = [super init];
    if (self) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(checkInput:) name:UITextFieldTextDidBeginEditingNotification object:nil];
    }

    return self;
}

// This is used to obtain the current text field/view that is now the first responder
- (void)checkInput:(NSNotification *)notification {
    UITextField *field = notification.object;

    if (field.inputView && self == field.inputView) {
        _input = field;

        _tvShouldChange = NO;
        _tfShouldChange = NO;
        if ([_input isKindOfClass:[UITextField class]]) {
            id<UITextFieldDelegate> del = [(UITextField *)_input delegate];
            if ([del respondsToSelector:@selector(textField:shouldChangeCharactersInRange:replacementString:)]) {
                _tfShouldChange = YES;
            }
        } else if ([_input isKindOfClass:[UITextView class]]) {
            id<UITextViewDelegate> del = [(UITextView *)_input delegate];
            if ([del respondsToSelector:@selector(textView:shouldChangeTextInRange:replacementText:)]) {
                _tvShouldChange = YES;
            }
        }
    }
}

// Call this for each button press
- (void)click {
    [[UIDevice currentDevice] playInputClick];
}

// Call this when a button on the keyboard is tapped (other than return or backspace)
- (void)keyTapped:(UIButton *)button {
    NSString *text = ???; // determine text for the button that was tapped

    if ([_input respondsToSelector:@selector(shouldChangeTextInRange:replacementText:)]) {
        if ([_input shouldChangeTextInRange:[_input selectedTextRange] replacementText:text]) {
            [_input insertText:text];
        }
    } else if (_tfShouldChange) {
        NSRange range = [(UITextField *)_input selectedRange];
        if ([[(UITextField *)_input delegate] textField:(UITextField *)_input shouldChangeCharactersInRange:range replacementString:text]) {
            [_input insertText:text];
        }
    } else if (_tvShouldChange) {
        NSRange range = [(UITextView *)_input selectedRange];
        if ([[(UITextView *)_input delegate] textView:(UITextView *)_input shouldChangeTextInRange:range replacementText:text]) {
            [_input insertText:text];
        }
    } else {
        [_input insertText:text];
    }
}

// Used for a UITextField to handle the return key button
- (void)returnTapped:(UIButton *)button {
    if ([_input isKindOfClass:[UITextField class]]) {
        id<UITextFieldDelegate> del = [(UITextField *)_input delegate];
        if ([del respondsToSelector:@selector(textFieldShouldReturn:)]) {
            [del textFieldShouldReturn:(UITextField *)_input];
        }
    } else if ([_input isKindOfClass:[UITextView class]]) {
        [_input insertText:@"\n"];
    }
}

// Call this to dismiss the keyboard
- (void)dismissTapped:(UIButton *)button {
    [(UIResponder *)_input resignFirstResponder];
}

// Call this for a delete/backspace key
- (void)backspaceTapped:(UIButton *)button {
    if ([_input respondsToSelector:@selector(shouldChangeTextInRange:replacementText:)]) {
        UITextRange *range = [_input selectedTextRange];
        if ([range.start isEqual:range.end]) {
            UITextPosition *newStart = [_input positionFromPosition:range.start inDirection:UITextLayoutDirectionLeft offset:1];
            range = [_input textRangeFromPosition:newStart toPosition:range.end];
        }
        if ([_input shouldChangeTextInRange:range replacementText:@""]) {
            [_input deleteBackward];
        }
    } else if (_tfShouldChange) {
        NSRange range = [(UITextField *)_input selectedRange];
        if (range.length == 0) {
            if (range.location > 0) {
                range.location--;
                range.length = 1;
            }
        }
        if ([[(UITextField *)_input delegate] textField:(UITextField *)_input shouldChangeCharactersInRange:range replacementString:@""]) {
            [_input deleteBackward];
        }
    } else if (_tvShouldChange) {
        NSRange range = [(UITextView *)_input selectedRange];
        if (range.length == 0) {
            if (range.location > 0) {
                range.location--;
                range.length = 1;
            }
        }
        if ([[(UITextView *)_input delegate] textView:(UITextView *)_input shouldChangeTextInRange:range replacementText:@""]) {
            [_input deleteBackward];
        }
    } else {
        [_input deleteBackward];
    }

    [self updateShift];
}

@end

此类需要 UITextField 的类别方法:

@interface UITextField (CustomKeyboard)

- (NSRange)selectedRange;

@end

@implementation UITextField (CustomKeyboard)

- (NSRange)selectedRange {
    UITextRange *tr = [self selectedTextRange];

    NSInteger spos = [self offsetFromPosition:self.beginningOfDocument toPosition:tr.start];
    NSInteger epos = [self offsetFromPosition:self.beginningOfDocument toPosition:tr.end];

    return NSMakeRange(spos, epos - spos);
}

@end
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何从 iOS 应用程序上的自定义键盘检索击键? 的相关文章

随机推荐