I have a textView that is layered on a tableView cell. I need the user to click on the tableView row to open a viewController but if the textView has a link it must be click
One possible (and complex) solution would be using UIView
s with UITapGestureRecogniser
inside your UITextView
.
Firstly, you will need to find the NSRange
of your link.
Convert NSRange
to UITextRange
Use code similar to the following to add a UITapGestureRecogniser
right on top of the link-text in your UITextView
.
UITextPosition *pos = textView.endOfDocument;// textView ~ UITextView
for (int i = 0; i < words*2 - 1; i++){// *2 since UITextGranularityWord considers a whitespace to be a word
UITextPosition *pos2 = [textView.tokenizer positionFromPosition:pos toBoundary:UITextGranularityWord inDirection:UITextLayoutDirectionLeft];
UITextRange *range = [textView textRangeFromPosition:pos toPosition:pos2];
CGRect resultFrame = [textView firstRectForRange:(UITextRange *)range ];
UIView* tapViewOnText = [[UIView alloc] initWithFrame:resultFrame];
[tapViewOnText addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(targetRoutine)]];
tapViewOnText.tag = 125;
[textView addSubview:tapViewOnText];
[tapViewOnText release];
pos = pos2;
}
What I have done in this code is, got the UITextRange
of relevant text, get it's firstRectForRange
and added a transparent tap-able UIView
right on top of it.
You would have to get the range of your link using some regEx
, convert it to UITextRange
, and add tap-able UIViews
over them. In case, there might be more than one link in a single textView
you might add a tag
to each view corresponding to their 'link', and open that link in the target method by checking it's tag.
NOTE: If your UITextView
s are universally un-editable, you might want to try TTTAttributedLabel instead. That is what I do in my UITableViewCells
If you are trying to add a UITextView with links to a cell, and want didSelectRow to be called when the user taps on anything but the link, then you should use hitTest:withEvent:
Swift 3
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// location of the tap
var location = point
location.x -= self.textContainerInset.left
location.y -= self.textContainerInset.top
// find the character that's been tapped
let characterIndex = self.layoutManager.characterIndex(for: location, in: self.textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
if characterIndex < self.textStorage.length {
// if the character is a link, handle the tap as UITextView normally would
if (self.textStorage.attribute(NSLinkAttributeName, at: characterIndex, effectiveRange: nil) != nil) {
return self
}
}
// otherwise return nil so the tap goes on to the next receiver
return nil
}
I wrote an article about this with a bit more details.
Maybe it will be ok for your purposes:
textView.editable=NO;
textView.userInteractionEnabled=YES;
textView.dataDetectorTypes =UIDataDetectorTypeLink;
But in fact UITextView uses UIWebView parts internally, and this way may be slowly in table cells.
I can advice to use CoreText
or NSAttributedString
's with touches. For example you may read this SO post.