In an iOS 7 app, I have a UITextView
with a link in it, but tapping the link doesn't fire. It only responds to an awkward "tap and hold". I want it to respond as soon as a user taps on it, like how a UIWebView
link tap works. Here is my setup:
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:@"Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."];
[text addAttribute:NSLinkAttributeName value:@"myurl://tapped" range:NSMakeRange(6, 16)];
self.textView.attributedText = text;
self.textView.editable = NO;
self.textView.delaysContentTouches = NO;
}
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange
{
if ([[URL scheme] isEqualToString:@"myurl"])
{
// Handle tap
return NO;
}
return YES;
}
The Apple Documentation for the shouldInteractWithURL
method states: "The text view calls this method if the user taps or long-presses the URL link". The long-press is working, but the tap doesn't seem to work.
Does anyone know how to get this to respond immediately?
If you still want to go with a native UITextView
, you can add a tap recognizer to your textview and get the string attributes at the tap location. When you find a link, you can open it immediately.
I wrote a Gist that solves this for iOS 7/8. It's a lightweight extension of UITextView
that also forwards -[UITextViewDelegate textView:shouldInteractWithURL:inRange:]
and exposes the internal tap gesture recognizer.
https://gist.github.com/benjaminbojko/c92ac19fe4db3302bd28
Here's a quick example:The example below only works on iOS 8. See the gist above for iOS 7 + 8 support.
Add your tap recognizer:
// ...
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tappedTextView:)];
[myTextView addGestureRecognizer:tapRecognizer];
myTextView.selectable = YES; // otherwise the gesture won't recognize
// ...
And add your callback:
- (void)tappedTextView:(UITapGestureRecognizer *)tapGesture {
if (tapGesture.state != UIGestureRecognizerStateEnded) {
return;
}
UITextView *textView = (UITextView *)tapGesture.view;
CGPoint tapLocation = [tapGesture locationInView:textView];
UITextPosition *textPosition = [textView closestPositionToPoint:tapLocation];
NSDictionary *attributes = [textView textStylingAtPosition:textPosition inDirection:UITextStorageDirectionForward];
NSURL *url = attributes[NSLinkAttributeName];
if (url) {
[[UIApplication sharedApplication] openURL:url];
}
}
And swift version:
Tap recognizer:
let tapRecognizer = UITapGestureRecognizer(target: self, action: Selector("tappedTextView:"))
myTextView.addGestureRecognizer(tapRecognizer)
myTextView.selectable = true
Callback:
func tappedTextView(tapGesture: UIGestureRecognizer) {
let textView = tapGesture.view as! UITextView
let tapLocation = tapGesture.locationInView(textView)
let textPosition = textView.closestPositionToPoint(tapLocation)
let attr: NSDictionary = textView.textStylingAtPosition(textPosition, inDirection: UITextStorageDirection.Forward)
if let url: NSURL = attr[NSLinkAttributeName] as? NSURL {
UIApplication.sharedApplication().openURL(url)
}
}
Is the UITextView
selectable?. Try with:
self.textView.selectable = YES;
Edit:
I'm starting to think that maybe a long-press is the only way to fire it contrary to what apple says. Check this link, maybe it will help.
As nnarayann mentioned, CCHLinkTextView avoids the problem of delayed tap recognition. This library implements its own gesture recognizer and is now available in version 1.0.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With