UIView tap recognizer not working - ios

Trying to make work tap recognition on a UIView:
#IBOutlet weak var mapView_: GMSMapView!
#IBOutlet weak var viewInfo: UIView!
override func viewDidLoad() {
super.viewDidLoad()
/* MapView inital values & dependencies */
let initialLocation = CLLocationCoordinate2DMake(37.78, -122.41)
let camera = GMSCameraPosition.cameraWithTarget(initialLocation, zoom: 10)
/* Set up MapView */
mapView_.camera = camera
mapView_.myLocationEnabled = true
mapView_.delegate = self
var selfTap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "viewTapped:")
viewInfo.addGestureRecognizer(selfTap)
viewInfo.userInteractionEnabled = true
mapView_.insertSubview(viewInfo, aboveSubview: mapView_)
}
func viewTapped(recognizer: UIGestureRecognizer) -> Void{
NSLog("tapped")
}
It doesn't recognize the tap.
Any idea ?
Thanks

Problem "solved"
The problem occurred due to the viewInfo being a subView of googlemaps view.
I couldn't delegate tap event to viewcontroller, so I assumed the event "tap" was being sending to mapview.
I took out the viewInfo from mapview and played a little with constraints to place where I wanted it. So the parent of viewInfo now is viewcontroller and the tap event is recognized without any problem.
I guess this is not the better solution as we need to be very careful when moving the subview in the storyboard to not let it becomes part of mapview, but for while I can't figure out how delegate the subview to viewcontroller.
Thank you all for your help.

You need to remove the : from handleTap: or you need to change your method to handleTap(sender: UITapGestureRecognizer)

use:
func handleTap(sender: UITapGestureRecognizer) {
NSLog("tapped")
}

Related

UITapGesture doesn't work as expected on Popup class

I create a Popup class to use in my app and I want to add a UITapGestureRecognizer to the black layer opacity, when the user touch outside of the popup this close automatically. But the gesture was not recognized. I show you my code of the Popup class
class Popup {
let supView : UIView!
let blackVoile = UIView()
init(superView viewToInsert : UIView){
self.supView = viewToInsert
build()
}
private func build(){
blackVoile.frame = supView.bounds
blackVoile.layer.backgroundColor = UIColor.black.cgColor
blackVoile.isUserInteractionEnabled = true
let closeGesture = UITapGestureRecognizer(target: self, action: #selector(self.close))
blackVoile.addGestureRecognizer(closeGesture)
}
func show(){
supView.addSubview(blackVoile)
}
#objc func close(){
print("close function")
self.blackVoile.removeFromSuperview()
}
}
The close func was never called. And there is no other over layer above the blackVoile UIView
This is when I called my class :
let newPopup = Popup(superView : self.view)
newPopup.show()
I'm beginner so, maybe we can't add gesture to a class who are not have an UIView instance?
Problem with your opacity. If we make any opacity to zero then that view consider as a hidden. So, your tapGesture not working.
Update
var newPopup : Popup!
override func viewDidLoad() {
super.viewDidLoad()
newPopup = Popup(superView : self.view)
newPopup.show()
}
Your supView also needs to be userInteractionEnabled.

GestureRecognizer taking all Touch Input

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!

UIGestureRecognizer on only one part of the screen

If you look into the image, you'll see a textview, button and a slider.
I have completed all the steps except for the last one which is, when I tap on the slider, it constantly disappear. I know it disappear because I implemented UITapGestureRecognizer.
I guess my question is, I want the slider to disappear everytime I tap anywhere on the screen but when I am using the slider, I dont want the slider to disappear which is happening now every time I release my tap.
I have tried implementing one more UITapGestureRecognizer in sizeRefont with a function to keep sizeRefont.isHidden false but when I do that, the slider will not disappear whenever I tap on the screen.
I tried putting sizeRefont.isHidden = false in sizeRefont action and it doesnt work either.
class ResizeController: UIViewController {
#IBOutlet weak var sizeRefont: UISlider!
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissRefontSize(_:)))
view.addGestureRecognizer(tap)
}
#IBAction func sizeRefont(_ sender: AnyObject) {
let fontSize = CGFloat(sizeRefont.value)
textView.font = UIFont(name: textView.font!.fontName, size: fontSize * 30.0)
}
#IBAction func showSlider(_ sender: Any) {
sizeRefont.isHidden = false
}
func dismissRefontSize(_ sender: UITapGestureRecognizer) {
if sender.location(in: sizeRefont){
sizeRefont.isHidden = false
} else {
sizeRefont.isHidden = true
}
}
}
There is an error on if sender.location(in: sizeRefont) where it says CGPoint is not convertible to Bool
Image
First thing you need to do is you need to Adjust the dismissRefontSize() method to to the following :
func dismissRefontSize(_ sender: UITapGestureRecognizer) {
let location = sender.location(in: view)
If sizeReFont.frame.contains(location) {
// do nothing
}else {
sizeReFont.isHidden = true
}
}
The other thing you need to adjust is creating the tap recognized in your viewDidLoad() to the following:
let tap = UITapGestureRecognizer(target: self, action : #selector(dismissRefontSize(_:)))

How to implement a tap gesture recognizer for multiple subviews with Swift

I have a tableView with 3 Static Cells, each cell contains a subview with labels displaying the desired text, what I want to accomplish is to have the orientation programmatically rotate from Portrait to Landscape when the user taps on any of the 3 subviews.
I got it working for the first subview. However, when I try to add the same Tap gesture recognizer to the other subviews it still only works for one subview. Please advise, what am I missing? Thanks for any input.
Portrait:
Landscape
Table View Controller code:
import UIKit
class SampleTableViewController: UITableViewController {
#IBOutlet weak var newYorkViewWrapper: UIView!
#IBOutlet weak var sanFranciscoViewWrapper: UIView!
#IBOutlet weak var chicagoViewWrapper: UIView!
//Vars
let tapRec = UITapGestureRecognizer()
override func viewDidLoad() {
super.viewDidLoad()
print("Sample view rendered.")
//Tap gesture
tapRec.addTarget(self, action: "tappedView")
newYorkViewWrapper.addGestureRecognizer(tapRec)
newYorkViewWrapper.userInteractionEnabled = true
sanFranciscoViewWrapper.addGestureRecognizer(tapRec)
sanFranciscoViewWrapper.userInteractionEnabled = true
chicagoViewWrapper.addGestureRecognizer(tapRec)
chicagoViewWrapper.userInteractionEnabled = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//--------------------------------------------------------
// MARK: Hide status bar
//--------------------------------------------------------
override func prefersStatusBarHidden() -> Bool {
return true
}
func tappedView(){
let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
}
//--------------------------------------------------------
// MARK: View Orientation method
//--------------------------------------------------------
override func shouldAutorotate() -> Bool {
return true
}
}
when I try to add the same Tap gesture recognizer to the other subviews it still only works for one subview
You've answered your own question beautifully. You can't do this:
newYorkViewWrapper.addGestureRecognizer(tapRec)
sanFranciscoViewWrapper.addGestureRecognizer(tapRec)
chicagoViewWrapper.addGestureRecognizer(tapRec)
Instead, you must make three different tap gesture recognizers and add one to each view.
As Matt says, a gesture recognizer only works with a single view. The only way you can make a tap gesture recognizer work with multiple views would be to attach it to the superview, and then write extra code that figures out which of your subviews (if any) it hit. This is usually more trouble than it's worth, though, and you're better off simply creating a separate gesture recognizer for each view.
On case I've found where it is useful to attach a gesture recognizer to the superview is letting the user tap on a view who's position is being animated. Gesture recognizers don't handle position animation, so you have to do that yourself.
I resolved my own issue by defining 4 separate UITapGestureRecognizer() variables as shown below and then adding the appropriate gesture recognizer to the appropriate view.
//Vars
let tapRecForHistoryView = UITapGestureRecognizer()
let tapRecForMissionView = UITapGestureRecognizer()
let tapRecForServiceAreaView = UITapGestureRecognizer()
let tapRecForServiceAreaMapView = UITapGestureRecognizer()
override func viewDidLoad() {
super.viewDidLoad()
//Tap gesture
tapRecForHistoryView.addTarget(self, action: "tappedView")
tapRecForMissionView.addTarget(self, action: "tappedView")
tapRecForServiceAreaView.addTarget(self, action: "tappedView")
tapRecForServiceAreaMapView.addTarget(self, action: "tappedView")
historyViewWrapper.addGestureRecognizer(tapRecForHistoryView)
historyViewWrapper.userInteractionEnabled = true
missionViewWrapper.addGestureRecognizer(tapRecForMissionView)
missionViewWrapper.userInteractionEnabled = true
serviceAreaViewWrapper.addGestureRecognizer(tapRecForServiceAreaView)
serviceAreaViewWrapper.userInteractionEnabled = true
serviceAreaMapWrapper.addGestureRecognizer(tapRecForServiceAreaMapView)
serviceAreaMapWrapper.userInteractionEnabled = true
}
//--------------------------------------------------------
// MARK: Local Methods
//--------------------------------------------------------
func tappedView(){
let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
}
I am now able to tap each individual view and the orientation will programmatically rotate from Portrait to Landscape.

UITapGestureRecognizer interferes with UISlider

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
}

Resources