I'm trying to do syntax highlighting to mark errors on user input.
Following the suggestions in Get X and Y coordinates of a word in UITextView my code works perfectly for UITextView
However, I'm now trying to do a similar thing for a UITextField and having trouble.
Both UITextField and UITextView conform to UITextInput and all the methods required to locate a rect should work for both.
Here's a func for UITextView
func findRect(forTextMatching match:String, in textView:UITextView) -> CGRect? {
if let text = textView.text,
let range = text.range(of: match)
{
let offset1 = text.distance(from: text.startIndex, to: range.lowerBound)
let offset2 = text.distance(from: text.startIndex, to: range.upperBound)
if let pos1 = textView.position(from: textView.beginningOfDocument, offset: offset1),
let pos2 = textView.position(from: textView.beginningOfDocument, offset: offset2)
{
if let textRange = textView.textRange(from: pos1, to: pos2)
{
textView.selectedTextRange = textRange
let rect = textView.firstRect(for: textRange)
return rect
}
}
}
return nil
}
Run this in a test app and highlight the return rect looks like this:
Now try to do the same thing with UITextField:
func findRect(forTextMatching match:String, in textField:UITextField) -> CGRect? {
if let text = textField.text,
let range = text.range(of: match)
{
let offset1 = text.distance(from: text.startIndex, to: range.lowerBound)
let offset2 = text.distance(from: text.startIndex, to: range.upperBound)
// beginningOfDocument is nil unless textField is first responder
textField.becomeFirstResponder()
defer {
textField.resignFirstResponder()
}
if let pos1 = textField.position(from: textField.beginningOfDocument, offset: offset1),
let pos2 = textField.position(from: textField.beginningOfDocument, offset: offset2)
{
if let textRange = textField.textRange(from: pos1, to: pos2)
{
textField.selectedTextRange = textRange
var rect = textField.firstRect(for: textRange)
return rect
}
}
}
return nil
}
The first problem is that UITextField.beginningOfDocument is nil unless the field is first responder. That's easy enough to deal with, hence the extra lines to becomeFirstResonder and resignFirstResponder.
Run this in a test app and highlight the returned rect looks like this:
The returned rect is slightly offset.