Get currently entered word in UITextView

I want to get the currently entered word in UITextView

. A way to get a fully typed word can be found here UITEXTVIEW: Get the last word typed in uitextview , however I want to get the word as it is typed (no matter where it is typed, starting in the middle, at the end UITextView

).

My guess is that the definition of the word being entered is a space character followed by alphanumeric characters, or if the current location (somehow figured out with help range.location

) is the beginning UITextView

, then anything that follows it should be counted as the word. The word is terminated when the next space character is typed.

I tried:

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

      

but I have a problem finding the correct range.

In short: I need to find a substring in UITextView

, where substring is the currently entered word.

EDIT . Because the question arose. I use NSLayoutManager

and bind to it NSTextContainer

, which is then passed as layoutManager to NSTextStorage

.

EDIT2 . The main problem with (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text

is that it range.location

does not match the indicator, but always 1 less. If the cursor is at position 1, after entering one letter, range.location

will return 0. Any way around this? Also, the attribute text

UITextView

seems to be off at 1. When text foobar

and I output UITextView.text

, then I get fooba

. Therefore it range.location+1

returns an exceptionRange {0, 1} out of bounds; string length 0'

EDIT3 : Maybe it is easier to get my desired result using NSTextStorage

instead UITextView

?

+3


source to share


5 answers


UITextView Delegate Method: -textView:textView shouldChangeTextInRange:range replacementText:text

What actaully does is that it asks if the specified text should be replaced in the text view in the specified range textView.text

.

This method will be called every time we enter charactor before updating this text. This is why you get range.location

as 0 when you type the very first character into textView

.

Only if the return of this method is true is it textView

updated with what we typed in textView

.

This is the parameter definition of the method -textView:textView shouldChangeTextInRange:range replacementText:text

provided by apple:



range : - Current selection range. If the length of the range is 0, the range reflects the current insertion point. If the user presses the Delete key, the range length is 1 and an empty string object replaces that single character.
text : - text to insert.

So the explanation of the method and your requirement could be 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;
}

      

+1


source


Below is an example of a skeleton structure that uses UITextInput

and UITextInputTokenizer

to give the word that is currently being typed / edited.

- (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 complete word that is currently being typed.

For example, if you type input and type inp , it will give you inp . Next, if you are in the middle of the word middle and change it to midddle , it will give you a midddle . The trick here is tokenizing .



I put the code inside textViewDidChange:

so that you get the word after typing / editing it. If you want it before the last sign change (i.e. the Word to be changed), put the code in textView:shouldChangeTextInRange:replacementText:

.

PS: You will have to handle edge cases like sentences and paragraphs inserted / deleted. You can change this code to get characters / sentences / paragraphs etc, not just words by changing the verbosity. Review the declaration UITextGranularity

ENUM

for more options:

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

      

+8


source


You can simply extend UITextView

and use the following method, which returns the word around the current cursor location:

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!
    }
}

      

+2


source


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

      

+1


source


Your parameter range

from the delegate method - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text

specifies the exact place where you are changing the string value UITextView

.

The location

structure property points to the changing index of the row, which is simple. length

usually not equal text.length

, be careful, the user can select characters from the string to replace it with another string.

0


source







All Articles