How to make a gestureRecogniser selector work in swift 3? - swift

Below is the code I have been using to try and add a gesture recogniser to something. I am getting the yellow error: "No method declared with objective C selector dragging" and then the program is crashing when I go to pan on it. The code and way of using a selector seems to work in all tutorials but it's the problem here.
class GameViewController: UIViewController, UIGestureRecognizerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let p = UIPanGestureRecognizer(target: self, action: #selector("dragging"))
p.delegate = self
characterGridView!.addGestureRecognizer(p)
}
func dragging(p: UIPanGestureRecognizer) {
print("works")
}

Your selector is incorrect.
Change
let p = UIPanGestureRecognizer(target: self, action: #selector("dragging"))
to
let p = UIPanGestureRecognizer(target: self, action: #selector(dragging(p:)))

Related

Where do you declare UITapGestureRecognizer() in relation to addGestureRecognizer() in Swift?

Problem
centerLabelGesture is being defined outside of the scope of viewDidLoad(), so at the time that .addGestureRecognizer(centerLabelGesture) is called, centerLabelGesture is not defined yet.
import UIKit
import SnapKit
class ViewController: UIViewController {
var screen: UIView!
var centerLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
screen = UIView()
centerLabel = UILabel()
view.addSubview(screen)
screen.addSubview(centerLabel)
screen.backgroundColor = .white
screen.snp.makeConstraints { (make) in
make.top.equalTo(view)
make.right.equalTo(view)
make.left.equalTo(view)
make.bottom.equalTo(view)
}
centerLabel.text = "I hope I'm centered."
centerLabel.snp.makeConstraints { (make) in
make.center.equalTo(screen)
}
centerLabel.isUserInteractionEnabled = true
centerLabel.addGestureRecognizer(centerLabelGesture)
}
let centerLabelGesture = UITapGestureRecognizer(target: self, action: #selector(centerLabelTapped))
#objc func centerLabelTapped() {
centerLabel.text = "Ouch, you tapped me!"
}
}
Update 1/19/2019
matt pointed out that centerLabelGesture needs to be declared prior to centerLabel.addGestureRecognizer(centerLabelGesture), inside viewDidLoad()
This is a subtle mistake. The problem is the place you've put this line:
let centerLabelGesture = UITapGestureRecognizer(target: self, action: #selector(centerLabelTapped))
Move that line into the viewDidLoad code:
centerLabel.isUserInteractionEnabled = true
let centerLabelGesture = UITapGestureRecognizer(target: self, action: #selector(centerLabelTapped))
centerLabel.addGestureRecognizer(centerLabelGesture)
The reason is that where you've got that line, it's an instance property, and when you say self as the target in an instance property initializer, it doesn't mean what you think it does (it means the class, not the instance), so the message when you tap is misdirected and does nothing.
I have filed a bug on this issue; in my opinion the compiler should at least warn you that you're making a potential mistake.
Try adding this line in viewDidLoad
centerLabelGesture.numberOfTapsRequired = 1

selector with argument gestureRecognizer

Argument of '#selector' does not refer to an '#objc' method, property,
or initializer
Problem : Above Error when i try to pass argument with selector
code snippet:
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(labelPressed(i: 1)))
func labelPressed(i: Int){
print(i)
}
You cannot pass a parameter to a function like that. Actions - which this is, only pass the sender, which in this case is the gesture recognizer. What you want to do is get the UIView you attached the gesture to:
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(labelPressed())
func labelPressed(_ recognizer:UITapGestureRecognizer){
let viewTapped = recognizer.view
}
A few more notes:
(1) You may only attach a single view to a recognizer.
(2) You might want to use both the `tag` property along with the `hitTest()` method to know which subview was hit. For example:
let view1 = UIView()
let view2 = UIView()
// add code to place things, probably using auto layout
view1.tag = 1
view2.tag = 2
mainView.addSubview(view1)
mainView.addSubview(view2)
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(mainViewTapped())
func mainViewTapped(_ recognizer:UITapGestureRecognizer){
// get the CGPoint of the tap
let p = recognizer.location(in: self)
let viewTapped:UIView!
// there are many (better) ways to do this, but this works
for view in self.subviews as [UIView] {
if view.layer.hitTest(p) != nil {
viewTapped = view
}
}
// if viewTapped != nil, you have your subview
}
You just should declare function like this:
#objc func labelPressed(i: Int){ print(i) }
Update for Swift 3:
Using more modern syntax, you could declare your function like this:
#objc func labelTicked(withSender sender: AnyObject) {
and initialize your gesture recognizer like this, using #selector:
UITapGestureRecognizer(target: self, action: #selector(labelTicked(withSender:)))

Unable to add swipe recognizer to SKScene in Swift playground?

Ok, Im still a little new to how swift playgrounds work but I am trying to add a swipe gesture recognizer in Swift 3 to my swift playground. Following this http://www.spritekitlessons.com/gesture-recognizer-with-sprite-kit-and-swift/ I now have:
func swipedRight(sender:UISwipeGestureRecognizer){
print("swiped right")
}
func swipedLeft(sender:UISwipeGestureRecognizer){
print("swiped left")
}
func swipedUp(sender:UISwipeGestureRecognizer){
print("swiped up")
}
func swipedDown(sender:UISwipeGestureRecognizer){
print("swiped down")
}
let degree = CGFloat(M_PI_2) / 90
class GameScene: SKScene {
var selectedNode: SKNode?
var shakeAction: SKAction?
override func didMove(to view: SKView) {
/* Setup your scene here */
let swipeRight:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: Selector(("swipedRight:")))
swipeRight.direction = .right
view.addGestureRecognizer(swipeRight)
let swipeLeft:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: Selector(("swipedLeft:")))
swipeLeft.direction = .left
view.addGestureRecognizer(swipeLeft)
let swipeUp:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: Selector(("swipedUp:")))
swipeUp.direction = .up
view.addGestureRecognizer(swipeUp)
let swipeDown:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: Selector(("swipedDown:")))
swipeDown.direction = .down
view.addGestureRecognizer(swipeDown)
}
let frame = CGRect(x: 0, y: 0, width: 1000, height: 600) //view size
let view = SKView(frame: frame)
let scene = GameScene(size: frame.size)
view.presentScene(scene)
PlaygroundPage.current.liveView = view
This compiles, however when I swipe I get an unrecognized selector error even though I did include the functions with the selector:
I have tried placing the functions within the class as well. How can I add a swipe recognizer to a Swift playground SKScene?
You have selectors passed as strings, something is definitely wrong with them as said in error log
Try to use new selector syntax - #selector(methodName).
Example:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
/* Swift 3 */
let swipe = UISwipeGestureRecognizer(target: self, action:#selector(handleSwipe))
view.addGestureRecognizer(swipe)
}
func handleSwipe() {
print("Swiped!")
}
}
Using strings for selectors has been deprecated.
Using new selector syntax if the methodName() method doesn't exist, you'll get a compile error – your app won't crash because of "unrecognized selector".

Add tap gesture recognizer from another file

Currently I have 2 classes: ViewController and class A. My objective is to add tap gesture recognizer to a UIView from class A. My ViewController:
class ViewController {
#IBOutlet var area: UIView!
func enterClassA() {
let classA = A(self.area)
//some processing
}
}
class A : UIGestureRecognizerDelegate {
private var currView: UIView!
init(newView: UIView) {
self.currView = newView
self.addTapGesture()
}
func addTapGesture() {
let tap = UITapGestureRecognizer(target: self.currView, action:#selector(tapDetected(_:)))
tap.delegate = self.currView as! UIGestureRecognizerdelegate? //***
self.currView.addGestureRecognizer(tap)
}
#objc func tapDetected(_ tapGesture: UIGesturerecognizer) {
print ("Tap detected!")
}
}
But now it's giving error: could not cast value of type 'UIView' to "UIGestureRecognizerDelegate". I tried modifying line *** to
tap.delegate = self
But it's still not working, showing error: unrecognized selector sent to instance. Deleting the line gives the same error. May I know if this is even doable or not (adding gesture recognizer from a different class)? If so then how should I approach it?
In this line
let tap = UITapGestureRecognizer(target: self.currView, action:#selector(tapDetected(_:)))
You say the gesture recognizer to search the tapDetected() function in the self.currView object. Try to change the target:
let tap = UITapGestureRecognizer(target: self, action:#selector(tapDetected(_:)))
And yes, it should be like that:
tap.delegate = self
Since your A class conforms to UIGestureRecognizerDelegate.

Pass a function to a #selector

I get a function as function parameter and want to set this in a #selector.
But I get the error message:
Argument of '#selector' cannot refer to a property
I have the following function:
private func addGestureRecognizerToItem(selector: () -> ()) {
let labelGesture = UITapGestureRecognizer(target: self, action: #selector(selector))
let imageGesture = UITapGestureRecognizer(target: self, action: #selector(selector))
label.addGestureRecognizer(labelGesture)
imageView.addGestureRecognizer(imageGesture)
}
Any ideas how to handle this?
How about this?
class ViewController: UIViewController {
let label = UILabel()
let imageView = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
addGestureRecognizerToItem(#selector(test))
}
func test() {
}
private func addGestureRecognizerToItem(selector: Selector) {
let labelGesture = UITapGestureRecognizer(target: self, action: selector)
let imageGesture = UITapGestureRecognizer(target: self, action: selector)
label.addGestureRecognizer(labelGesture)
imageView.addGestureRecognizer(imageGesture)
}
}
It's not possible, rather you can call your desired function from the return of the first function whose data you want to pass to the other one.
And your scenario may change according to your requirement.