Reputation: 19792
I'm working on an autocompletion component and I have one problem that I would like to solve in some easy way.
I want to support edits to autocompleted text, for example:
blablabl @usertag blablabl
If user goes back and edits @usertag string, I would like to start autocompletion when it's edited.
Question is, how to get currently edited word from textfield. I thought about taking cursor position, seperate nsstring to word by " " (space) and count letters from first word to cursor position.
Is there any easier way for doing that?
Upvotes: 5
Views: 3410
Reputation: 535
Here is a solution for the efficiency nerds. Written in Swift 4.2. I'm claiming that is is more efficient because other answers are calling the String method String.component
which unnecessarily converts the entire substring to an array. Also those solutions are always grabbing words before the cursor which may not necessarily be the current word.
extension UITextView {
/// Returns the current word that the cursor is at.
func currentWord() -> String {
guard let cursorRange = self.selectedTextRange else { return "" }
func getRange(from position: UITextPosition, offset: Int) -> UITextRange? {
guard let newPosition = self.position(from: position, offset: offset) else { return nil }
return self.textRange(from: newPosition, to: position)
}
var wordStartPosition: UITextPosition = self.beginningOfDocument
var wordEndPosition: UITextPosition = self.endOfDocument
var position = cursorRange.start
while let range = getRange(from: position, offset: -1), let text = self.text(in: range) {
if text == " " || text == "\n" {
wordStartPosition = range.end
break
}
position = range.start
}
position = cursorRange.start
while let range = getRange(from: position, offset: 1), let text = self.text(in: range) {
if text == " " || text == "\n" {
wordEndPosition = range.start
break
}
position = range.end
}
guard let wordRange = self.textRange(from: wordStartPosition, to: wordEndPosition) else { return "" }
return self.text(in: wordRange) ?? ""
}
}
Upvotes: 4
Reputation: 16426
Swift 4 Solution
func getCurrentEditingWord() ->String? {
let selectedRange: UITextRange? = self.textView.selectedTextRange
var cursorOffset: Int? = nil
if let aStart = selectedRange?.start {
cursorOffset = self.textView.offset(from: self.textView.beginningOfDocument, to: aStart)
}
let text = self.textView.text
let substring = (text as NSString?)?.substring(to: cursorOffset!)
let editedWord = substring?.components(separatedBy: " ").last
return editedWord
}
Upvotes: 5
Reputation: 19792
Ok I found a way to do it quite easily. Maybe someone will find it useful:
UITextRange* selectedRange = [textField selectedTextRange];
NSInteger cursorOffset = [textField offsetFromPosition:0 toPosition:selectedRange.start];
NSString* text = textField.text;
NSString* substring = [text substringToIndex:cursorOffset];
NSString* editedWord = [[substring componentsSeparatedByString:@" "] lastObject];
Upvotes: 8
Reputation: 6468
Have you tried using UITextFieldDelegate available methods? If access to the word as you edit it is what you want, you can simply use the -textField:shouldChangeCharactersInRange:replacementString:
Inside this method you have access to the string as it's entered, and you can also do any string manipulation here for every new character entered.
Upvotes: 0