I need to get the coordinates of a link tapped on in a UIWebView. I tried using TapGestureRecognizer but it doesn't trigger when the link is tapped. I thought I might be able to get the coordinates from Javascript via stringByEvaluatingJavaScriptFromString and I couldn't get that to work either. How can I accomplish this?
A Swift equivalent of this answer: How to detect touch on UIWebView.
import UIKit
class ViewController: UIViewController, UIGestureRecognizerDelegate {
let myWebView = UIWebView()
override func viewDidLoad() {
super.viewDidLoad()
myWebView.frame = self.view.frame
self.view.addSubview(myWebView)
myWebView.loadRequest(NSURLRequest(URL: NSURL(string: "https://stackoverflow.com")!))
let webViewTapped = UITapGestureRecognizer(target: self, action: "tapAction:")
webViewTapped.numberOfTouchesRequired = 1
webViewTapped.delegate = self
myWebView.addGestureRecognizer(webViewTapped)
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func tapAction(sender: UIGestureRecognizer) {
let point = sender.locationInView(self.view)
println(point.x)
println(point.y)
}
UIView hitTest might be of use.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event ;
Overriding it (or using a category) in your web view might allow you to process tap coordinates of the web view's contained items which wouldn't be passed to the superview by a gesture recognizer
Related
I have this Setup in my Storyboard.
In my first ViewController Scene I have a MapView from MapBox. In there I have put a TextField (AddressTextField). On that TextField when touching the view, i'm running self.addressTextField.resignFirstResponder(), but after that neither the mapview, nor any other element in there or in the Embedded Segues react on a touch or click. Probably this is because I didn't completely understand the system of the First Responder. I'm thankful for every help.
Edit 1:
I think I know what's going on now, but I don't know how to fix it. When I add the Gesture Recognizer to the View (or to the mapView, that doesn't matter), the other UIViews and the MapView do not recognize my Tap-Gestures anymore. When I am not adding the Recognizer everything works fine. It seems as if the Gesture Recognizer is recognizing every tap I make on either the UIViews or the MapView and therefore other gestures are not recognized.
Edit 2:
I just added a print() to dismissKeyboard(). As soon as any Touch Event gets recognized on the MapView or the other UIViews, dismissKeyboard() gets called. So I think my thought of Edit 1 was correct. Does anyone know how I can solve this, so that it's not only dismissKeyboard() that gets called ?
Some Code:
func dismissKeyboard(){
self.addressTextField.resignFirstResponder()
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
dismissKeyboard()
return true
}
//Class (only partially)
class ViewController: UIViewController, MGLMapViewDelegate, CLLocationManagerDelegate, UITextFieldDelegate {
override func viewDidLoad(){
mapView.delegate = self
addressTextField.delegate = self
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
self.mapView.addGestureRecognizer(tap)
}
}
Others are just #IBActions linked to the Buttons, or other elements.
try this:
func dismissKeyboard(){
view.endEditing(true)
}
hope it helps!
After I knew the real issue I was able to solve the problem. I declared a var keyboardEnabled. Then I added these lines to my class.
class ViewController: UIViewController, UIGestureRecognizerDelegate {
var keyboardEnabled = false
override func viewDidLoad(){
super.viewDidLoad()
//Looks for single tap
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
self.mapView.addGestureRecognizer(tap)
}
/* Setting keyboardEnabled */
//Editing Target did end
#IBAction func editingTargetDidEnd(_ sender: Any) {
keyboardEnabled = false
}
//Editing TextField Started
#IBAction func editingAdressBegin(_ sender: Any) {
keyboardEnabled = true
}
//Call this function when the tap is recognized.
func dismissKeyboard() {
self.mapView.endEditing(true)
keyboardEnabled = false
}
//Implementing the delegate method, so that I can add a statement
//decide when the gesture should be recognized or not
//Delegate Method of UITapGestureRecognizer
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
return keyboardEnabled
}
}
With this solution keyboardEnabled takes care of deciding wether my UIGestureRecognizer should react or not. If the Recognizer doesn't react, the Gesture is simply passed on to the UIViews or other Elements that are in my MapView.
Thanks for all your answers!
I'm trying to add a UIPanGestureRecognizer to my mapView but I don't why the action method is never called (Swift 2.1).
import UIKit
import MapKit
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate, UIGestureRecognizerDelegate
{
override func viewDidLoad()
{
super.viewDidLoad()
// other stuff...
let gestureRecognizer = UIPanGestureRecognizer(target: self, action: "didDragMap:")
gestureRecognizer.delegate = self
self.mapView.addGestureRecognizer(gestureRecognizer)
}
func didDragMap(sender: UIPanGestureRecognizer)
{
// never enter here
}
}
What's wrong here? I have the same Objective-C corresponding and it's work.
Since an MKMapView already handles it's own gestures you need to enable it to also listen to your gestures.
Implement shouldRecognizeSimultaneouslyWithGestureRecognizer and return true like so:
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
Source
I got a gesture related issue, somewhat similar to: gesture-problem-uiswipegesturerecognizer-uislider
But I code in swift and so I need a solution in swift.
What happens in my project is this:
I have a ViewController on which a tap on the screen by the user, will perform a segue. But I have a UISlider on this viewcontroller, and when a user 'releases' the slider it is sometimes (why sometimes and not always, confuses me) recognized as a tap on the screen.
So I understand I have to prevent that the gesture recognizer 'sees/recognizes' touches on the UIslider.
But how do I prevent this? (in swift 2.0, using Xcode7. but I also understand it if u use earlier swift coding in an answer)
I am fairly new to coding in swift. I hope someone can help!
Here is the code in the viewcontroller:
// The slider
#IBOutlet weak var sliderValue: UISlider!
#IBAction func sliderValueChanged(sender: UISlider) {
// Do stuff
}
// UITapGestureRecognizer
override func viewDidLoad() {
super.viewDidLoad()
let touch = UITapGestureRecognizer(target: self, action: "touched:")
self.view.addGestureRecognizer(touch)
}
// Perform Segue
func touched (_: UIGestureRecognizer) {
//Segue to another viewcontroller
performSegueWithIdentifier("nice", sender: self)
}
(EDIT:) I updated my code with information I have found here on stackoverflow. I have added UIGestureRecognizerDelegate:
class LampOn: UIViewController, UIGestureRecognizerDelegate {...}
And I have added shouldReceiveTouch:
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
gestureRecognizer.delegate = self
if (touch.view == sliderValue){
print("touching slider")
return false
}
else{
print("touching elsewhere")
return true
}
}
But the 'shouldReceiveTouch` func never gets called from the console. So what am I missing? Did I set up the delegate correctly?
The "shouldReceiveTouch" function is never called because you set the delegate inside the "shouldReceiveTouch" function. The delegate needs to be set to self BEFORE the function can be called.
What you need to do is to set the delegate inside the viewDidLoad() and everything should be fine.
override func viewDidLoad() {
super.viewDidLoad()
let touch = UITapGestureRecognizer(target: self, action: "touched:")
self.view.addGestureRecognizer(touch)
touch.delegate = self
}
How can I add a UITapGestureRecognizer to a UITextView but still have the touches getting through to the UITextView as normal?
Currently as soon as I add a custom gesture to my textView it blocks the tap for UITextView default actions like positioning the cursor.
var tapTerm:UITapGestureRecognizer = UITapGestureRecognizer()
override func viewDidLoad() {
tapTerm = UITapGestureRecognizer(target: self, action: "tapTextView:")
textView.addGestureRecognizer(tapTerm)
}
func tapTextView(sender:UITapGestureRecognizer) {
println("tapped term – but blocking the tap for textView :-/")
…
}
How can I process taps but keep any textView behaviour like cursor positioning as is?
To do that make your view controller adopt to UIGestureRecognizerDelegate and override should recognize simultaneously with gesture recognizer method like:
override func viewDidLoad() {
tapTerm = UITapGestureRecognizer(target: self, action: "tapTextView:")
tapTerm.delegate = self
textView.addGestureRecognizer(tapTerm)
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
In case anyone came here looking for #Zell B.'s answer in Objective C, here's the code:
- (void)viewDidLoad {
[super viewDidLoad];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(textViewTapped:)];
tap.delegate = self;
tap.numberOfTapsRequired = 1;
[self.textView addGestureRecognizer:tap];
}
- (void)textViewTapped:(UITapGestureRecognizer *)tap {
//DO SOMTHING
}
#pragma mark - Gesture recognizer delegate
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
PS: Don't forget < UIGestureRecognizerDelegate >
Swift 4.2
The following steps allows me to escape a full-screen UITextView with a tap, whilst allowing to scroll the contents of the UITextView:
Disconnected the UIGestureRecognizer from the UITableView.
Made a CustomTextView: UITextView.
Added a 'sender' var to the particular UIViewController with the CustomTextView.
Trap for 'Touches Ended...'
Call for an excape function within the sender UIViewController.
class CustomTextView: UITextView {
var sender: DocViewController?
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let controller = sender {
controller.handleSwipeGesture()
}
}
}
I can either scroll the contents of the UITextView or merely tap to exit.
The 'sender' is set from the hosting UIViewController at creation.
Somehow I could not assign a delegate inside UIView so I came up with a button solution.
I insert a button that completely covers the textView and when tapped on textViewButton I do resign the button so textView shows.
#IBAction func textViewTapped(_ sender: UIButton) {
self.placeholderLabel.isHidden = true
self.textViewButton.resignFirstResponder()
}
I am creating an app using swift. In one of my ViewController, I have a GMSMapView that I create programatically. I want user to have the capability triggering an action when clicking on the map.
What I have done :
import UIKit
class MapViewController: UIViewController, GMSMapViewDelegate {
let mapView = GMSMapView()
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
mapView.settings.scrollGestures = false
mapView.frame = CGRectMake(0, 65, 375, 555)
view.addSubview(mapView)
var tap = UITapGestureRecognizer(target: self, action: "tap:")
mapView.addGestureRecognizer(tap)
}
func tap(recogniser:UITapGestureRecognizer)->Void{
println("it works")
}
}
I have tried to override touchesBegan, didnt work. I have tried to insert mapView.userInteractionEnabled = true, didnt work...
Any idea?
I managed to do it with
func mapView(mapView: GMSMapView!, didTapAtCoordinate coordinate: CLLocationCoordinate2D) {
println("It works")
}
But if someone could explain to me why the other solution didn't work, it would be great!
You can use default MapVIew LongPress event
/**
* Called after a long-press gesture at a particular coordinate.
*
* #param mapView The map view that was pressed.
* #param coordinate The location that was pressed.
*/
- (void)mapView:(GMSMapView *)mapView
didLongPressAtCoordinate:(CLLocationCoordinate2D)coordinate;
The map view already has its own gesture recognizers for panning, zooming etc.
So you probably need to tell the system that it should take care on multiple gesture recognizers.
As part of the UIGestureRecognizerDelegate protocol:
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}