简体   繁体   English

在UITextView中获取当前键入的单词

[英]Get currently typed word in a UITextView

I want to get the currently typed word in a UITextView . 我想在UITextView获取当前键入的单词。 A way to get a completely typed word can be found here UITEXTVIEW: Get the recent word typed in uitextview , however, I want to get the word while it is being typed (no matter where it is typed, beginning, middle, end of UITextView ). 获取完全输入单词的方法可以在这里找到UITEXTVIEW:获取最近uitextview中输入的单词,但是,我想在输入单词时输入单词(无论它在哪里输入, UITextView开头,中间,结尾)。

I guess what defines a word being typed is a whitespace character followed by alphanumeric characters, or if the current location (somehow to figure out with range.location ) is the beginning of the UITextView then whatever follows up should be considered as word as well. 我想定义一个单词的类型是一个空白字符后跟字母数字字符,或者如果当前位置(以某种方式用range.location )是UITextView的开头,那么后续的任何内容都应该被视为单词。 A word is finished being typed when another whitespace character follows. 当另一个空格字符跟随时,将完成一个单词的输入。

I tried with: 我尝试过:

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

but I have a problem finding the correct range. 但我找到正确的范围有问题。

In short again: I need to find the substring in a UITextView , where substring is the currently typed word. 简而言之:我需要在UITextView找到子字符串,其中substring是当前键入的字。

EDIT : Because the question came up. 编辑 :因为问题出现了。 I'm using NSLayoutManager and bind a NSTextContainer to it, which then is passed as layoutManager to a NSTextStorage . 我使用NSLayoutManager并绑定NSTextContainer它,然后作为的layoutManager传递给NSTextStorage

EDIT2 : The main problem with (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text is that the range.location is not the same as the indicator is, but always 1 less. EDIT2(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text的主要问题(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text是指range.location与指标不一样,但总是少1。 If the cursor is at position 1, after typing one letter, range.location would return 0. Any way around this? 如果光标位于位置1,键入一个字母后, range.location将返回0.有什么办法吗? In addition the text attribute of UITextView seems to be off by 1 as well. 此外, UITextViewtext属性似乎也是1。 When the text is foobar and I output the UITextView.text then I get fooba instead. 当文本是foobar并且我输出UITextView.text然后我得到fooba而不是。 Therefore range.location+1 returns the exception Range {0, 1} out of bounds; string length 0' 因此range.location+1返回异常Range {0, 1} out of bounds; string length 0' Range {0, 1} out of bounds; string length 0'

EDIT3 : Maybe it is easier to get my wanted result with the NSTextStorage instead of UITextView ? 编辑3 :也许用NSTextStorage而不是UITextView更容易获得我想要的结果?

The following is an example of a skeletal structure that uses UITextInput and UITextInputTokenizer to give the word that is being currently typed/edited. 以下是使用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]);
}

This will give you the entire word that is currently being typed. 这将为您提供当前正在输入的整个单词

For instance if you are typing input and have typed inp it will give you inp . 例如,如果您输入输入并输入inp ,它将为您提供inp Further if you are in the middle of the word middle and change it to midddle , it will give you midddle . 此外,如果您位于单词middle的中间并将其更改为midddle ,它将为您提供中等 The trick here is tokenizing . 这里的技巧是标记化

I have placed the code inside textViewDidChange: so that you get the word after it's been typed/edited. 我已将代码放在textViewDidChange:这样您就可以在输入/编辑获得该单词。 If you want it before the last character change (ie the word that is about to be changed), place the code in textView:shouldChangeTextInRange:replacementText: . 如果在最后一个字符更改之前需要它(即即将更改的单词),请将代码放在textView:shouldChangeTextInRange:replacementText: .

PS: You will have to handle edge cases like sentences and paragraphs being pasted/removed. PS:您必须处理边缘情况,例如句子和段落被粘贴/删除。 You can modify this code to get characters/sentences/paragraphs etc, instead of just words by changing the granularity. 您可以通过更改粒度来修改此代码以获取字符/句子/段落等,而不仅仅是单词。 Look into the declaration of UITextGranularity ENUM for more options: 查看UITextGranularity ENUM的声明以获取更多选项:

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

You can simply extend UITextView and use following method which returns the word around the current location of cursor: 您可以简单地扩展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!
    }
}

The UITextView delegate method : -textView:textView shouldChangeTextInRange:range replacementText:text what actaully does is that it asks whether the specified text should be replaced in the text view in the specified range of textView.text . 该UITextView的委托方法: -textView:textView shouldChangeTextInRange:range replacementText:text是什么actaully做的是,它会询问是否指定的文本应在指定范围内的文本视图替换textView.text

This method will be invoked each time when we type a charactor before updating that to the text view. 每次我们在将其更新到文本视图之前键入一个charactor时,都会调用此方法。 That is why you are getting the range.location as 0, when you type the very first character in the textView . 这就是为什么你所得到的range.location为0,当你键入的第一个字符textView

Only if the return of this method is true, the textView is getting updated with what we have typed in the textView . 只有当此方法的返回为真时, textView才会更新我们在textView输入的内容。

This is the definition of the parameters of the -textView:textView shouldChangeTextInRange:range replacementText:text method as provided by apple: 这是-textView:textView shouldChangeTextInRange:range replacementText:text的参数定义-textView:textView shouldChangeTextInRange:range replacementText:text apple提供的-textView:textView shouldChangeTextInRange:range replacementText:text方法:

range :- The current selection range. 范围 : - 当前选择范围。 If the length of the range is 0, range reflects the current insertion point. 如果范围的长度为0,则范围反映当前插入点。 If the user presses the Delete key, the length of the range is 1 and an empty string object replaces that single character. 如果用户按下Delete键,则范围的长度为1,空字符串对象将替换该单个字符。
text :- The text to insert. text : - 要插入的文本。

So this is what the explanation for the method and your requirement can meet like as follows: 这就是方法和您的要求的解释如下所示:

- (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;
}
- (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;
}

Your range parameter from the - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text delegate method points at the exact place you change the UITextView string value. 您的range参数来自- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text delegate方法指向您更改UITextView字符串值的确切位置。

The location property of the structure points to the changing string index, that's simple. 结构的location属性指向更改的字符串索引,这很简单。 The length not usually equals to text.length , be careful, user may select characters from the string to replace it with another string. length通常不等于text.length ,请注意,用户可以从字符串中选择字符以将其替换为另一个字符串。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM