'UITextView link tap recognition is delayed
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?
Solution 1:[1]
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.
Solution 2:[2]
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)
}
}
Solution 3:[3]
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.
Solution 4:[4]
Swift 4 solution
- Handles links using tap gesture recognizer
- Whether
isSelectableistrueorfalse, it works
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleLinkTap(_:)))
textView.addGestureRecognizer(tapRecognizer)
@objc func handleLinkTap(_ recognizer: UITapGestureRecognizer) {
let tapLocation = recognizer.location(in: textView)
guard
let textPosition = textView.closestPosition(to: tapLocation),
let url = textView.textStyling(at: textPosition, in: .forward)?[NSAttributedStringKey.link.rawValue],
let urlString = (url as? String) ?? (url as? URL)?.absoluteString,
urlString == "myurl"
else { return }
let url = URL(string: urlString)!
// Do whatever you want with this URL, such as
UIApplication.shared.openURL(url)
}
Solution 5:[5]
Sometimes it's not working on the iOS Simulator or only work if you tap & hold.
You should test it on a real device.
And don't forget to set it selectable.
Solution 6:[6]
If there isn't any pressing reason to use a UITextView, such as there being other text being displayed, you could use a UILabel combined with a UITapGestureRecognizer to get the effect you're looking for.
Otherwise, you could go with an actual UIWebView instead.
Solution 7:[7]
Have you tried setting textview.delaysContentTouches = NO; ? Maybe that could help.
Solution 8:[8]
If you don't need editing, you can use this:
textView.isEditable = true
func textViewShouldBeginEditing(_ textView: UITextView) -> Bool
{
return false
}
Solution 9:[9]
Use this method
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | buxik |
| Solution 3 | Claus |
| Solution 4 | |
| Solution 5 | Laszlo |
| Solution 6 | dandan78 |
| Solution 7 | dezinezync |
| Solution 8 | El Horrible |
| Solution 9 | zaid afzal |
