Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting tap on a UITextView only detects cancellations [duplicate]

I have been struggling to detect a tap on a UITextView with Swift.

My UITextViews are in a table, I must be able to detect links and press them, those links length are unknown.

Also if I tap on the cell, that I don't tap on a link, I want push a new UIViewController on my navigation controller stack.

I tried to create my own textview to overwrite the touchesCancelled, but it wasn't a success. It detects the cancellation which isn't considered a tap on the real device.

The bug doesn't occur in the simulator, but it seems I can't tap on the real device, only long press will work.

class LinkTextView: UITextView {
    override func touchesCancelled(touches: Set<NSObject>!, withEvent event: UIEvent!) {
         self.textViewWasTapped(self) 
    }
}

I tried adding directly a gesture recognizer. I didn't have any success there either. It doesn't call the gesture recognizer at all.

I added the UIGestureReconizer delegate to my UIViewController and those lines in my

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
     var singleTap : UIGestureRecognizer = UIGestureRecognizer(target: self, action: "tapTextView:")
        singleTap.delegate = self
             cell.mTextOutlet.attributedText = myMutableString
             cell.mTextOutlet.addGestureRecognizer(singleTap) 

In my LinkTextView class :

class LinkTextView: UITextView {
    func tapTextView(stapGesture: UIGestureRecognizer){
        println("TAPPING1")
    }
}

I looked the forums and found this post : post. It suggests to use CHHLinkTextView. I tried to use it but what I want is to detect the link automatically, which normal uitextview actually does.

I did try using the checkBox in interface builder to parse links with the CHHLinkTextView, but it doesn't work. I didn't see anything in the documentation suggesting it could be done.

How should I proceed ?

like image 858
Swift Rabbit Avatar asked Jul 07 '15 14:07

Swift Rabbit


2 Answers

You're very close with your first attempt in subclassing UITextView, but instead of overriding only touchesCancelled, you'll want to override all of the touches* methods, i.e.

  • touchesBegan
  • touchesMoved
  • touchesEnded
  • touchesCancelled

In the overridden methods, send the touch down the responder chain by getting the textView's nextResponder(), check that it isn't nil, and call the method on the next responder.

The reason this works is because the UITextView will intercept the touch if it's on a URL and the UIResponder methods will never be called -- try it for yourself by implementing textView:shouldInteractWithURL:inRange in your tableViewCell or wherever, and you'll see that it's called before any touch events are passed along.

This should be the minimum for what you're trying to do (it's repetitive but short):

class PassThroughTextView: UITextView {

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let next = next {
            next.touchesBegan(touches, with: event)
        } else {
            super.touchesBegan(touches, with: event)
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let next = next {
            next.touchesEnded(touches, with: event)
        } else {
            super.touchesEnded(touches, with: event)
        }
    }

    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let next = next {
            next.touchesCancelled(touches, with: event)
        } else {
            super.touchesCancelled(touches, with: event)
        }
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let next = next {
            next.touchesMoved(touches, with: event)
        } else {
            super.touchesMoved(touches, with: event)
        }
    }
}

// In case you want to do anything with the URL or textView in your tableViewCell...
class TableViewCell: UITableViewCell, UITextViewDelegate {
    @IBOutlet var textView: PassThroughTextView!

    func textView(textView: UITextView, shouldInteractWithURL URL: NSURL, inRange characterRange: NSRange) -> Bool {
        println("textView shouldInteractWithURL")
        return true
    }

}
like image 109
Ralfonso Avatar answered Nov 20 '22 01:11

Ralfonso


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.

like image 8
Swift Rabbit Avatar answered Nov 20 '22 01:11

Swift Rabbit