Uitextview Link Tap Recognition Is Delayed

UITextView link tap recognition is delayed

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.

UITextView open link on tap

The example below only works on iOS 8+

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)
}

}

Swift 3 and no force unwraps:

func tappedTextView(tapGesture: UIGestureRecognizer) {
guard let textView = tapGesture.view as? UITextView else { return }
guard let position = textView.closestPosition(to: tapGesture.location(in: textView)) else { return }
if let url = textView.textStyling(at: position, in: .forward)?[NSLinkAttributeName] as? URL {
UIApplication.shared.open(url)
}
}

Detecting tap on a UITextView only detects cancellations

I did as Ralfonso said without success, but it helped me realize I had a gesture recognizer conflict. Turns out I didn't have to override all 4 methods of the UITextView, I just override the touchesBegan.
What I was doing in the viewDidLoad was add a gesture recognizer to dismiss the keyboard :

      var tapOutTextField: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "dismissKeyboard")
self.view.addGestureRecognizer(tapOutTextField)

What didn't make sense and sent me in a wrong direction was that touchesBegan was actually called after a delay (so was touchCancelled), and I could not get the same behaviour as a UIButton. All that because of this gesture recognizer conflict.

is there any chance to identify if empty space has been tapped in UITextView?

Ok, so I got it working like expected. The solution is to check if the tapped position is the end of the document:

if let position = textView.closestPosition(to: location) {
if position == textView.endOfDocument {
return true
}
}

The final code looks like that:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if let textView = textView, textView.text.count > 0 {
var location = touch.location(in: textView)
location.x -= textView.textContainerInset.left
location.y -= textView.textContainerInset.top
if let position = textView.closestPosition(to: location) {
if position == textView.endOfDocument {
return true
}
}

let characterIndex = textView.layoutManager.characterIndex(for: location, in: textView.textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
if (textView.attributedText?.attribute(.link, at: characterIndex, effectiveRange: nil) as? URL) != nil {
return false
}
}
return true
}

How can I click a link in the UITextView and get the position of the 'tap' at the same time

override hitTest:withEvent method

signature

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

Above method is supplied with CGPoint which is the location of touch.Create a CGPoint class property and store the touch-location in it.

@property (nonatomic,assign) CGPoint previousTouch;

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
self.previousTouch = point;
return [super hitTest:point withEvent:event];
}

UITextView appearance delayed

Replace [self fetchData]; with

[self performSelectorInBackground:@selector(fetchData) withObject:nil];

Then inside your fetchData method, after your fetch is done, add

[self performSelectorOnMainThread:@selector(fetchDataFinished) withObject:nil waitUntilDone:NO];

where the new method fetchDataFinished is defined as

-(void) fetchDataFinished{
[self.tableView reloadData];
[self.textView removeFromSuperview];
}

Note that you have to make textView a property so it is accessible in this method.

I have not tried out the above for your particular case, but I have used similar constructs successfully in many similar situations.



Related Topics



Leave a reply



Submit