I think I solved it, using a subclass of UITextView
called which has a rangeOfLink
property.
First, in my UIViewController
viewDidLoad:
, I add
self.textView.dataDetectorTypes = UIDataDetectorTypeLink; // change for other link types
self.textView.selectable = YES;
self.textView.userInteractionEnabled = YES;
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector(handleTap:)];
tapGesture.cancelsTouchesInView = YES;
[self.textView addGestureRecognizer: tapGesture];
[self.textView setNeedsDisplay]; // force a redraw so that drawRect is called
Then in handleTap
, I do this:
MyTextViewWithLink *aTextView = (IDTextViewWithLink *) recognizer.view;
if (aTextView != self.textView)
return;
if (recognizer.state == UIGestureRecognizerStateEnded)
{
CGPoint location = [recognizer locationInView: aTextView];
// this returns an NSTextCheckingResult if location is inside a link
NSTextCheckingResult *result = [self textCheckingResultAtPoint: location inTextView: aTextView];
if (result)
{
aTextView.rangeOfLink = result.range;
[aTextView setNeedsDisplay]; // this will force the color change
// open url
}
}
Finally I override drawRect
in my UITextView
subclass:
self.linkTextAttributes = [NSDictionary dictionary];
NSError *error = nil;
NSDataDetector *dataDetector = [NSDataDetector dataDetectorWithTypes: NSTextCheckingTypeLink error: &error]; // change for other link types
if (!error && dataDetector)
{
NSArray* resultString = [dataDetector matchesInString: self.text
options: NSMatchingReportProgress
range: NSMakeRange(0, [self.text length])];
if (resultString.count > 0)
{
NSMutableAttributedString *mas = [self.attributedText mutableCopy];
for (NSTextCheckingResult* result in resultString)
{
if (result.resultType == NSTextCheckingTypeLink)
{
NSRange intersection = NSIntersectionRange(result.range, self.rangeOfLink);
if (intersection.length <= 0) // no match
[mas addAttribute: NSForegroundColorAttributeName
value: [UIColor blueColor]
range: self.rangeOfLink];
else
[mas addAttribute: NSForegroundColorAttributeName
value: [UIColor redColor]
range: self.rangeOfLink];
}
}
self.attributedText = mas;
}
}
[super drawRect: rect];
Now if the textView has more than one link, only the selected one will change color.