【问题标题】:Get currently typed word in a UITextView在 UITextView 中获取当前输入的单词
【发布时间】:2014-12-09 13:11:16
【问题描述】:

我想在UITextView 中获取当前输入的单词。在这里UITEXTVIEW: Get the recent word typed in uitextview 可以找到一种获取完全键入的单词的方法,但是,我想在键入时获取该单词(无论在何处键入,开头,中间,UITextView 的结尾)。

我猜想定义输入的单词是空格字符后跟字母数字字符,或者如果当前位置(以某种方式用range.location 找出)是UITextView 的开头,那么应该考虑后续的任何内容也一样。当另一个空白字符跟随时,一个单词被完成。

我试过了:

 - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text`

但我无法找到正确的范围。

再次简而言之:我需要在UITextView 中找到子字符串,其中子字符串是当前键入的单词。

编辑:因为问题出现了。我正在使用NSLayoutManager 并将NSTextContainer 绑定到它,然后将其作为layoutManager 传递给NSTextStorage

EDIT2(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text 的主要问题是range.location 与指标不一样,但总是少 1。如果光标位于位置 1,在键入一个字母后,range.location 将返回 0。有什么办法解决这个问题吗?此外,UITextViewtext 属性似乎也减少了 1。当文本为foobar 并且我输出UITextView.text 时,我得到fooba。因此range.location+1 返回异常Range {0, 1} out of bounds; string length 0'

EDIT3: 也许使用NSTextStorage 而不是UITextView 更容易获得我想要的结果?

【问题讨论】:

  • - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text 方法将一次为您提供单个字符的范围,您必须一一接受并制作最新的单词
  • 你想在哪里显示它?在标签还是?这对我有帮助
  • 您应该查看UITextInputProtocol。如果您在实现代码时遇到任何问题,请告诉我。
  • @The-Rooster 这是一个有点“复杂”的设置。我正在使用NSLayoutManager并将NSTextContainer绑定到它,然后将其作为layoutManager传递给NSTextStorage
  • @n00bProgrammer 我只是看了UITextInputProtocol 并真的不知道它应该如何提供帮助。 UITextInputProtocol 中的任何细节我应该仔细看看?

标签: ios objective-c uitextview substring nsrange


【解决方案1】:

以下是使用UITextInputUITextInputTokenizer 给出当前正在输入/编辑的单词的骨架结构示例。

- (void) textViewDidChange:(UITextView *)textView {

    NSRange selectedRange = textView.selectedRange;

    UITextPosition *beginning = textView.beginningOfDocument;
    UITextPosition *start = [textView positionFromPosition:beginning offset:selectedRange.location];
    UITextPosition *end = [textView positionFromPosition:start offset:selectedRange.length];

    UITextRange* textRange = [textView.tokenizer rangeEnclosingPosition:end withGranularity:UITextGranularityWord inDirection:UITextLayoutDirectionLeft];

    NSLog(@"Word that is currently being edited is : %@", [textView textInRange:textRange]);
}

这将为您提供当前正在输入的整个单词

例如,如果您正在输入 input 并输入了 inp,它会给您 inp。此外,如果您在单词 middle 的中间并将其更改为 middle,它会给您 middle强>。这里的技巧是标记化

我已将代码放在textViewDidChange: 中,以便您在输入/编辑后得到单词。如果您希望它最后一个字符更改(即即将更改的单词)之前,请将代码放在textView:shouldChangeTextInRange:replacementText:

PS:您将不得不处理诸如句子和段落被粘贴/删除的边缘情况。您可以修改此代码以获取字符/句子/段落等,而不仅仅是通过更改粒度来获取单词。查看UITextGranularity ENUM 的声明以获取更多选项:

typedef NS_ENUM(NSInteger, UITextGranularity) {
    UITextGranularityCharacter,
    UITextGranularityWord,
    UITextGranularitySentence,
    UITextGranularityParagraph,
    UITextGranularityLine,
    UITextGranularityDocument
};

【讨论】:

  • 很好,但它不适用于 UITextGranularityWord 和特殊字符:例如:“blabla #myCurrentlyEditedWord blabla”,即返回“myCurrentlyEditedWord”没有 "#".
  • @Ded77 你说得对。那是因为粒度适用于语言(我认为,但我不确定)。 #,这里是一个特殊字符。正如答案的 PS 部分所述,您将不得不处理边缘情况,这是一种边缘情况。您可以做的一件事是保留对检测到的最后一个单词的范围的引用,并将其 location+range 与当前检测到的单词的 location 进行比较。如果它们相同,则意味着它是一个词(标签、句柄等)。更简单的方法是从主字符串中提取检测到的单词并检查它是否前面有 #/@.
  • 这很好,但它不适用于符号?太糟糕了,因为这几乎是完美的。话虽如此,我使用了它,它似乎工作正常,捕获特殊字符 let range = tokenizer.rangeEnclosingPosition(end, with: .sentence, inDirection: .layout(.left))let sentence = textView.text(in: range)let currentWord = sentence.sentence.components(separatedBy: .whitespaces).last
【解决方案2】:

您可以简单地扩展UITextView 并使用以下方法返回光标当前位置周围的单词:

extension UITextView {

    func editedWord() -> String {

        let cursorPosition = selectedRange.location
        let separationCharacters = NSCharacterSet(charactersInString: " ")

        // Count how many actual characters there are before the cursor.
        // Emojis/special characters can each increase selectedRange.location
        // by 2 instead of 1

        var unitCount = 0
        var characters = 0
        while unitCount < cursorPosition {

            let char = text.startIndex.advancedBy(characters)
            let int = text.rangeOfComposedCharacterSequenceAtIndex(char)
            unitCount = Int(String(int.endIndex))!
            characters += 1
        }


        let beginRange = Range(start: text.startIndex.advancedBy(0), end: text.startIndex.advancedBy(characters))
        let endRange = Range(start: text.startIndex.advancedBy(characters), end: text.startIndex.advancedBy(text.characters.count))

        let beginPhrase = text.substringWithRange(beginRange)
        let endPhrase = text.substringWithRange(endRange)

        let beginWords = beginPhrase.componentsSeparatedByCharactersInSet(separationCharacters)
        let endWords = endPhrase.componentsSeparatedByCharactersInSet(separationCharacters)

        return beginWords.last! + endWords.first!
    }
}

【讨论】:

  • 这与表情符号一起崩溃,如何解决?
  • 不错的解决方案。涵盖用户输入下划线/点的情况。谢谢!
【解决方案3】:

UITextView 委托方法:-textView:textView shouldChangeTextInRange:range replacementText:text 实际做的是询问是否应该在textView.text 的指定范围内的文本视图中替换指定的文本。

每次我们在更新到文本视图之前输入一个字符时都会调用这个方法。这就是为什么当您在textView 中键入第一个字符时,您将range.location 设为0。

仅当此方法的返回值为 true 时,textView 才会更新为我们在 textView 中键入的内容。

这是苹果提供的-textView:textView shouldChangeTextInRange:range replacementText:text方法的参数定义:

范围 :- 当前选择范围。如果范围的长度为 0,则范围反映当前插入点。如果用户按下 Delete 键,则范围的长度为 1,并且一个空字符串对象替换该单个字符。
text :- 要插入的文本。

所以这就是方法的解释和您的要求可以满足如下:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
        //Un-commend this check below :If you want to detect the word only while new line or white space charactor input 
        //if ([text isEqualToString:@" "] || [text isEqualToString:@"\n"])
        //{
            // Getting the textView text upto the current editing location
            NSString * stringToRange = [textView.text substringWithRange:NSMakeRange(0,range.location)];

            // Appending the currently typed charactor
            stringToRange = [stringToRange stringByAppendingString:text];

            // Processing the last typed word 
            NSArray *wordArray       = [stringToRange componentsSeparatedByString:@" "];
            NSString * wordTyped     = [wordArray lastObject];

            // wordTyped will give you the last typed object
            NSLog(@"\nWordTyped :  %@",wordTyped);
        //}
        return YES;
}

【讨论】:

  • 我以前见过这个解决方案,但不幸的是它不太有效。当你输出wordTyped 时,你得到的词总是短一个字符。所以,假设你刚刚输入了foobarNSLog 将输出foobawordTyped
  • 这也是我一开始的想法,但这根本不起作用,并且当 UITextView 之前为空并给出越界异常时,应用程序也会崩溃
  • 那不行,因为它只会输出wordTyped,如果收到空格字符或换行符,但我想一直得到输出。
  • 问题描述在这个问题stackoverflow.com/questions/9155381/…。这个想法是在之前进行替换。
【解决方案4】:
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
    NSString *string = [textView.text stringByReplacingCharactersInRange:range withString:text];

    if ([text isEqualToString:@"\n"] || [text isEqualToString:@" "])
    {
        NSString *currentString = [string stringByReplacingOccurrencesOfString:@"\n" withString:@" "];

        NSLog(@"currentWord==> %@",currentString);
    }

    return YES;
}

【讨论】:

  • 我已经有了这个解决方案。然而,问题是在检测到空格或换行符后,您会得到整个字符串。您不会返回当前输入的单词,而是 NSTextContainer 中的整个字符串。
【解决方案5】:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text 委托方法中的 range 参数指向您更改 UITextView 字符串值的确切位置。

结构的location属性指向变化的字符串索引,很简单。 length 通常不等于text.length,请注意,用户可能会从字符串中选择字符以将其替换为另一个字符串。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-20
    • 2012-02-07
    • 2013-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多