I want to get the currently typed word in a UITextView
. A way to get a completely typed word can be found here UITEXTVIEW: Get the recent word typed in uitextvi
- (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;
}
The following is an example of a skeletal structure that uses UITextInput
and UITextInputTokenizer
to give the word that is being currently 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 entire word that is currently being typed.
For instance if you are typing input and have typed inp it will give you inp. Further if you are in the middle of the word middle and change it to miffffdle, it will give you miffffdle. The trick here is tokenizing.
I have placed the code inside textViewDidChange:
so that you get the word after it's been typed/edited. If you want it before the last character change (i.e. the word that is about to be changed), place the code in textView:shouldChangeTextInRange:replacementText:
.
PS: You will have to handle edge cases like sentences and paragraphs being pasted/removed. 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:
typedef NS_ENUM(NSInteger, UITextGranularity) {
UITextGranularityCharacter,
UITextGranularityWord,
UITextGranularitySentence,
UITextGranularityParagraph,
UITextGranularityLine,
UITextGranularityDocument
};
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
.
This method will be invoked each time when we type a charactor before updating that to the text view. That is why you are getting the range.location
as 0, when you type the very first character in the textView
.
Only if the return of this method is true, the textView
is getting updated with what we have typed in the textView
.
This is the definition of the parameters of the -textView:textView shouldChangeTextInRange:range replacementText:text
method as provided by apple:
range :- The current selection range. If the length of the range is 0, range reflects the current insertion point. If the user presses the Delete key, the length of the range is 1 and an empty string object replaces that single character.
text :- The text to insert.
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;
}
You can simply extend UITextView
and use following method which returns the word around the current location of cursor:
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!
}
}
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.
The location
property of the structure points to the changing string index, that's simple. The length
not usually equals to text.length
, be careful, user may select characters from the string to replace it with another string.