Add tap gesture recognizer from another file - swift

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.

Related

iOS SwiftUI: How to detect location of UITapGesture on view?

Using Mapbox, the map struct conforms to the UIViewRepresentable protocol. In my makeUIView() function, I create a tap gesture recognizer and add it to the map view.
struct Map: UIViewRepresentable {
private let mapView: MGLMapView = MGLMapView(frame: .zero, styleURL: MGLStyle.streetsStyleURL)
func makeUIView(context: UIViewRepresentableContext<Map>) -> MGLMapView {
mapView.delegate = context.coordinator
let gestureRecognizer = UITapGestureRecognizer(target: context.coordinator, action: Selector("tappedMap"))
mapView.addGestureRecognizer(gestureRecognizer)
return mapView
}
func updateUIView(_ uiView: MGLMapView, context: UIViewRepresentableContext<Map>) {
print("Just updated the mapView")
}
func makeCoordinator() -> Map.Coordinator {
Coordinator(appState: appState, self)
}
// other functions that make the struct have functions required by Mapbox
func makeCoordinator() -> Map.Coordinator {
Coordinator(self)
}
final class Coordinator: NSObject, MGLMapViewDelegate {
var control: Map
//a bunch of delegate functions
#objc func tappedMap(sender: UITapGestureRecognizer) {
let locationInMap = sender.location(in: control)
let coordinateSet = sender.convert(locationInMap, toCoordinateFrom: control)
}
}
}
Neither of the lines in the tappedMap function compile properly...also, when I have 'sender: UITapGestureRecognizer' in the parameters of tappedMap, I causes the application to crash when I tap the mapView--If I remove the parameter, then the function is at least called properly without crashing. Please help
OK, the first problem is your selector definition in the tapGesture declaration. Change it to this:
let gestureRecognizer = UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.tappedMap(sender:)))
Also, you need to manage the priority of gesture recognizers, since MGLMapView includes UIGestureRecognizer logic internally. I found a discussion of here:
Adding your own gesture recognizer to MGLMapView will block the
corresponding gesture recognizer built into MGLMapView. To avoid
conflicts, define which gesture takes precedence.
You can try this code in your project (pretty much just copied from the linked page above) EDIT: I needed to change the order of lines from my original answer:
let gestureRecognizer = UITapGestureRecognizer(target: context.coordinator, action: Selector("tappedMap"))
// HERE'S THE NEW STUFF:
for recognizer in mapView.gestureRecognizers! where recognizer is UITapGestureRecognizer {
gestureRecognizer.require(toFail: recognizer)
}
mapView.addGestureRecognizer(gestureRecognizer)
EDIT: I was able to test this.
I'm a little confused by the logic inside your tappedMap selector: maybe you meant something like this?
#objc func tappedMap(sender: UITapGestureRecognizer) {
let locationInMap = sender.location(in: control.mapView)
let coordinateSet = sender.view?.convert(locationInMap, to: control.mapView)
}

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

How to add a tap gesture to multiple UIViewControllers

I'd like to print a message when an user taps twice on the remote of the Apple TV. I got this to work inside a single UIViewController, but I would like to reuse my code so that this can work in multiple views.
The code 'works' because the app runs without any problems. But the message is never displayed in the console. I'm using Swift 3 with the latest Xcode 8.3.3. What could be the problem?
The code of a UIViewController:
override func viewDidLoad() {
super.viewDidLoad()
_ = TapHandler(controller: self)
}
The code of the TapHandler class
class TapHandler {
private var view : UIView?
required init(controller : UIViewController) {
self.view = controller.view
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.message))
tapGesture.numberOfTapsRequired = 2
self.view!.addGestureRecognizer(tapGesture)
self.view!.isUserInteractionEnabled = true
}
#objc func message() {
print("Hey there!")
}
}
Your TapHandler just getting released. Try This:
var tapHandler:TapHandler? = nil
override func viewDidLoad() {
super.viewDidLoad()
tapHandler = TapHandler(controller: self)
}
I have tested the code and is working.

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:)))

How to make a gestureRecogniser selector work in swift 3?

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:)))