GestureRecognizer doesnt work dynamic/in extra class swift - ios

I want to implement a gesture recognizer for every Viewcontroller.
The following code is working:
class ViewController: UIViewController {
func draggedView(sender:UIPanGestureRecognizer){
// do stuff
}
override func viewDidLoad() {
super.viewDidLoad()
let panRec = UIPanGestureRecognizer()
panRec.addTarget(self, action: #selector(ViewController.draggedView(_:)))
self.view.addGestureRecognizer(panRec)
self.view.userInteractionEnabled = true
}
}
Now I want give this pan gesture a little bit more dynamic/abstraction
class Tools : NSObject {
var currentViewController: UIViewController?
func addPanToViewController(viewcontroller: UIViewController) {
currentViewcontroller = viewcontroller
if let currentViewcontroller = currentViewcontroller {
let panRec = UIPanGestureRecognizer()
panRec.addTarget(self, action: #selector(draggedView(_:)))
currentViewcontroller.view.addGestureRecognizer(panRec)
currentViewcontroller.view.userInteractionEnabled = true
}
}
func draggedView(sender:UIPanGestureRecognizer) {
//never called
}
}
I call the code in the ViewController-Class like this:
let sidemenu = Tools()
sidemenu.addPanToViewController(self)
But nothing happens. No Error, but no gestures are working. With the debugger I see that the gesture was added. So every code get executed.
I want execute the draggedView inside my Tools class.
I really don't understand where my problem is?

I think if you want this to work, you have to make the Tools class become singleton with:
static let sharedInstance = Tools()
then in the addPanToViewController:
panRec.addTarget(Tools.sharedInstance, action:#selector(draggedView(_:)))
In your viewController:
Tools.sharedInstance.addPanToViewController(self)

currentViewcontroller.delegete = your gesturerecogniser delegate

Related

iOS Swift: Send action (control event) from subclass of UIImageView

How can I send action (similar kind of tap event) from sub class of UIImageView to View Controller.
Here is reference answer, that I want to apply for UIImageView. (UIImageView does not have UIControl in its super class hierarchy)
Following is my code but not working as I don't know, how to implement, what I need.
class TappableImageView: UIImageView {
// Initializer methods.....
//------------------------------------------
private func addTapGesture(){
let tapOnImage = UITapGestureRecognizer(target: self, action: #selector(TappableImageView.handleTapGesture(tapGesture:)))
self.isUserInteractionEnabled = true
self.addGestureRecognizer(tapOnImage)
}
//----------------------------------------------
#objc func handleTapGesture(tapGesture: UITapGestureRecognizer) {
// how can I send tap/click event to all view controllers, where I've used this image from this point.
}
}
Is there any alternate/other solution that may work for me?
I will recommend you to use a closure to handle taps:
class TappableImageView: UIImageView {
var handleTap: (() -> Void)? = nil
//------------------------------------------
private func addTapGesture(){
let tapOnImage = UITapGestureRecognizer(target: self, action: #selector(TappableImageView.handleTapGesture(tapGesture:)))
self.isUserInteractionEnabled = true
self.addGestureRecognizer(tapOnImage)
}
//----------------------------------------------
#objc func handleTapGesture(tapGesture: UITapGestureRecognizer) {
handleTap?()
}
}
And then I your view controller you can use this i.e. in viewDidLoad method:
override func viewDidLoad() {
super.viewDidLoad()
yourTappableImageView.handleTap = {
print("an image view was tapped")
}
}
It assumes that your TappableImageView is stored in variable named yourTappableImageView.

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.

I am defining gesture recognizers in an ImageView subclass but calling a method in the ViewController crashes with "unrecognized selector"

I am defining gesture recognizers in an ImageView subclass but calling a method in the ViewController crashes with "unrecognized selector". The gesture works if I define the gestures in the VC, but I'm trying to put the code in the custom view class to cut down on clutter.
Is this possible? This is what I'm doing:
class DetailPhotoImageView: UIImageView {
func setupGestures(){
let tapped = UITapGestureRecognizer(target: self, action: #selector(TripDetailVC.tapped(_:)))
tapped.numberOfTapsRequired = 2
addGestureRecognizer(tapped)
}
}
And then in the VC I have this function and call imageView.setupGestures()
func tapped(_ gesture:UIGestureRecognizer) {
if let tapGesture = gesture as? UITapGestureRecognizer {
switch tapGesture.numberOfTapsRequired {
case 2:
print("Worked")
default:
break
}
}
}
So going off of Randall Wang's suggestion, they are right, you should could also go about this another, space-saving way. Pass the viewController as a parameter and set the it as the target:
class DetailPhotoImageView: UIImageView, UIGestureRecognizerDelegate {
func setupGestures(_ target: UIViewController) {
guard let aTarget = target as? MyViewController else { return }
let tap = UITapGestureRecognizer(target: aTarget, action: #selector(aTarget.someAction(_:)))
tap.delegate = self
tap.numberOfTapsRequired = taps
addGestureRecognizer(tap)
}
}
And in your viewController class:
detailPhoto.setupGestures(self)
Make a protocol for your UIImageView that takes in the gesture and set your ViewController as the delegate (having the ImageView as a parameter as well is just good practice.. or common):
protocol DetailPhotoDelegate {
func detailPhoto(_ detailPhoto: DetailPhotoImageView, actionFor gesture: UITapGestureRecognizer)
}
Add a delegate variable to you UIImageView subclass and preform your protocol function when the view is tapped. Also, make sure your UIImageView is a UIGestureRecognizerDelegate.
class DetailPhotoImageView: UIImageView, UIGestureRecognizerDelegate {
var delegate: DetailPhotoDelegate?
func setupGestures() {
let tap = UITapGestureRecognizer(target: self, action: #selector(someAction(_:)))
tap.delegate = self
tap.numberOfTapsRequired = taps
addGestureRecognizer(tap)
}
func someAction(_ guesture: UITapGestureRecognizer) {
print("tap - subclass func")
guard let aDelegate = aDelegate else { assertionFailure(); return }
delegate.detailPhoto(self, actionFor: gesture)
}
}
Then add the protocol to your ViewController, set it as your DetailPhotoImageView's delegate somewhere prior to the action being implemented (I did it in viewDidLoad() for the example), and implement the protocol method however you wish:
class ViewController: UIViewController, DetailPhotoDelegate {
#IBAction weak var detailPhoto: DetailPhotoImageView!
override func viewDidLoad() {
super.viewDidLoad()
detailPhoto.delegate = self
}
// Mark: DetailPhotoDelegate
func detailPhoto(_ detailPhoto: DetailPhotoImageView, actionFor guesture: UITapGestureRecognizer) {
switch guesture.numberOfTapsRequired {
case 1: print("1 tap"); return
case 2: print("2 taps"); return
default: print("\(guesture.numberOfTapsRequired) taps"); return
}
}
I think target shouldn't be self

unrecognized selector sent to instance Error when trying to setTarget action [duplicate]

I've searched for solutions to this problem but couldn't find anything that seems to address it in my case. I'm getting the above exception from a UITapGestureRecognizer.
Here's the simplified code:
import UIKit;
class ViewController : UIViewController, UIScrollViewDelegate
{
#IBOutlet weak var scrollView:UIScrollView!;
var imageView:UIImageView!;
override func viewDidLoad()
{
super.viewDidLoad();
... set up imageView/scrollView here ...
let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: "onScrollViewDoubleTapped");
doubleTapRecognizer.numberOfTapsRequired = 2;
doubleTapRecognizer.numberOfTouchesRequired = 1;
scrollView.addGestureRecognizer(doubleTapRecognizer);
}
func onScrollViewDoubleTapped(recognizer:UITapGestureRecognizer)
{
}
}
Can anyone tell what is wrong with this code? It seems all correct to me. I suspect that it has to do with assigning ViewController as delegate to scrollView (or vice versa)? However the ViewController is set as the delegate to scrollView. But maybe it's something else that causes this error?
Try adding a colon to your selector string.
let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: "onScrollViewDoubleTapped:");
As cabellicar123 mentioned, this indicates that the selector takes an argument.
Swift 4 using #selector.
let tapGesture: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didSelectItem(sender:)))
#objc func didSelectItem(sender: AnyObject?) {
print("didSelectItem: \(sender)")
}
Also try adding a parameter to your method:
...(target: self, action: "yourMethodName:")
func yourMethodName(sender: AnyObject?)
{
println("Clicked yourMethodName")
}
Maybe could help someone: I had this error because I declared private the selector method:
func setup() {
let singleFingerTap = UITapGestureRecognizer(target: self, action: "didTapOnViewInteraction:")
singleFingerTap.numberOfTapsRequired = 1
self.viewInteraction.addGestureRecognizer(singleFingerTap)
}
private func didTapOnViewInteraction(recognizer: UITapGestureRecognizer) {
}
Removing "private" keyword, all works great!
for swift 4
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(SignUpViewController.click))
userImage.addGestureRecognizer(tap)
userImage.isUserInteractionEnabled = true
}
#objc func click()
{
print("Tapped on Image")
}
where SignUpViewController is your viewcontroller name
In my case, with Swift 4, the selector was correctly defined, but since the item with the gesture was inside a collection view, it was causing the selector not to be found. I implemented a custom cell and configured the selector inside of it
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "recent", for: indexPath) as! MyCollectionViewCell
cell.configureGestureWith(entry: recentEntries.sorted(by: {$0.createdAt > $1.createdAt})[indexPath.row], sender: self)
Hope this helps some one.

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