Swift: Getting target of UIPanGestureRecognizer - ios

In my ViewController.swift, I have an array that contains custom UIViews. Every time one is created, I add a UIPanGestureRecognizer to it like this:
var panRecognizer = UIPanGestureRecognizer(target: self, action: "detectPan:")
newCard.gestureRecognizers = [panRecognizer]
This links to my detectPan(recognizer: UIPanGestureRecognizer) function, which handles movement. However, since I have multiple objects linked to the function, I'm not sure how I determine from which one the input is coming.
Is there anything like (the nonexistent) recognizer.target that I could use? Should I just handle the panning from within each of the custom UIViews instead?
Any help would be appreciated!

First of all, you should declare your panRecognizer with let.
let panRecognizer = UIPanGestureRecognizer(target: self, action: "detectPan:")
Second of all, you should not set the gestureRecognizers property of any UIView. This is bad practice because UIKit may have already added its own gesture recognizers to that view behind the scenes. If you subsequently remove those recognizers by assigning [panRecognizer] to that property, you may get unexpected behavior. To add your pan gesture recognizer, do this:
newCard.addGestureRecognizer(panRecognizer)
Then, in your detectPan(recognizer: UIPanGestureRecognizer) method you can detect which UIView was panned with the following code:
func detectPan(recognizer: UIPanGestureRecognizer) {
switch recognizer.view {
case self.customViewArray[0]:
// do something
case self.customViewArray[1]:
// do something else
case ... :
// ...
}

Related

Need to find mapView region changed by user manually dragged or automatically focusing

I have mapView with some annotations. I need to do separate action for both user dragged region change and set region(Automatically focus). Is there any way to find user dragged manually or not while regionDidChangeAnimated method calling.
I have checked all default properties for MKMapView, MKVisibleRect, region. There is no property related to finding for detect map view changed with user dragged manually or not
Unfortunately, you have to do this using a UIPanGestureRecognizer.
I have used, with success, A UIPanGestureRecognizer like the following:
lazy var mapPanGestureRecognizer: UIPanGestureRecognizer = {
let gr = UIPanGestureRecognizer(target: self, action: #selector(draggedMap))
gr.delegate = self
return gr
}()
You will also have to add the UIPanGestureRecognizer to the map with
yourMap.addGestureRecognizer(mapPanGestureRecognizer)
You can then manage what happens in the #selector function by checking the state of the gesture, like so
#objc func draggedMap(panGestureRecognizer: UIPanGestureRecognizer) {
// Check to see the state of the passed panGestureRocognizer
if panGestureRecognizer.state == UIGestureRecognizer.State.began {
// Do something
}
}

Custom UICollectionView Cell Swipe Animation

Does anyone know the best way how to implement this animation or library with similar functionality? I assume that it can be done via affine transforms. But maybe somebody knows some examples.
on tableView, you can use editActionForRowAt function(you can read more about it here https://developer.apple.com/documentation/uikit/uitableviewdelegate/1614956-tableview) but because you are using a collection view you have to do it by yourself.
Add every cell a UIPanGestureRecognizer and make the animation according to the to the pan gesture.
like this:
func setupSwipeGesture() {
swipeGesture = UIPanGestureRecognizer(target: self, action:#selector(swiped(_:)))
swipeGesture.delegate = self
self.addGestureRecognizer(swipeGesture)
}
func swiped(_ gestureRecognizer: UIPanGestureRecognizer) {
let xDistance:CGFloat = gestureRecognizer.translation(in: self).x
// do your animation
}

How do I listen to tap gestures on different controls in the same view?

I have two copies of a control (its called a RatingControl). How do I write handlers that can be invoked on the correct object when somebody double taps on them?
I have:
#IBOutlet weak var ratingControl: RatingControl!
#IBOutlet weak var ratingControl2: RatingControl!
inside a TableViewController and then
override func viewDidLoad() {
super.viewDidLoad()
let tapGR = UITapGestureRecognizer(target: ratingControl, action: #selector(RatingControl.doubleTap(_:)))
tapGR.numberOfTapsRequired = 2
self.view.addGestureRecognizer(tapGR)
let tapGR2 = UITapGestureRecognizer(target: ratingControl2, action: #selector(RatingControl.doubleTap(_:)))
tapGR2.numberOfTapsRequired = 2
self.view.addGestureRecognizer(tapGR2)
}
RatingControl.doubleTap(_) is an innocuous event handler.
When there is a double tap on the second rating control, the doubleTap method is called but is dispatched on the first rating control object!
I have tried setting two targets on a single UITapGestureRecognizer but it runs into the same problem.
Thanks much!
You need to add the gesture recognizers to the two rating controls instead of to self.view.
Try this:
let tapGR = UITapGestureRecognizer(target: self, action: #selector(RatingControl.doubleTap(_:)))
tapGR.numberOfTapsRequired = 2
ratingControl.addGestureRecognizer(tapGR) // ratingControl, not self.view
let tapGR2 = UITapGestureRecognizer(target: self, action: #selector(RatingControl.doubleTap(_:)))
tapGR2.numberOfTapsRequired = 2
ratingControl2.addGestureRecognizer(tapGR2) // ratingControl2, not self.view
There are 2 parts to hooking up a gesture recognizer: the target, which determines which object gets notified when the recognizer is triggered, and the view it's attached to, which determines from which view the recognizer recognizes the gesture.
You've got 2 gesture recognizes configured the same way, both attached to self.view. They are therefore going to respond to taps on self.view (which I assume is the view controller's content view.) I don't think it's clear which gesture recognizer is going to be triggered when you tap in that case.
You should have 2 different views and attach a different gesture recognizer to each one. If ratingControl1 and ratingControl2 are view objects, perhaps you meant to attach the gesture recognizers directly to them, rather than to self.view?

iOS: implement one-finger pan and two-finger pan gestures?

The goal is to implement two pan gestures against the same SCNScene: one with one finger and the other with two fingers.
This code below isn't working. The one-finger pan function never gets invoked even though the gesture is assigned a distinct selector. One-finger pans and two-finger pans both invoke sceneViewPannedTwoFingers.
From reading other questions it seemed like shouldRecognizeSimultaneouslyWithGestureRecognizer might be the answer, but these pans are not happening simultaneously. It should be either a one-finger pan or a two-finger pan, never both at once.
Is it possible to have two pan gestures as described above? If so, what's the right way to do this?
// Handle one-finger pans
let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(sceneViewPannedOneFinger))
sceneView.addGestureRecognizer(panRecognizer)
// Handle two-finger pans
let twoFingerPanRecognizer = UIPanGestureRecognizer(target: self, action: #selector(sceneViewPannedTwoFingers))
sceneView.addGestureRecognizer(twoFingerPanRecognizer)
func sceneViewPannedTwoFingers(sender: UIPanGestureRecognizer) {
print("two finger pan!!!")
}
func sceneViewPannedOneFinger(sender: UIPanGestureRecognizer) {
print("one finger pan!!!")
}
You need to add UIGestureRecognizerDelegate to your view controller and set the gesture recognizers delegate to self inside your view controller. Add the shouldRecognizeSimultaneouslyWithGestureRecognizer method returning true for them. Make sure you set minimum and maximum number of touches also for them.
code like this
class MyClass : UIGestureRecognizerDelegate --< Delegate
func gestureRecognizer(_: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWithGestureRecognizer:UIGestureRecognizer) -> Bool
{
return true
}

UITapGestureRecognizer not detecting CALayer in UIScrollView

I am trying to detect a tap gesture event on CALayers that are a sublayer of a UIScrollView. Here is how I've got the recognizer set up:
In viewDidLoad:
self.tapRecognizer = UITapGestureRecognizer(target: self, action: "tapHandler:")
self.tabScroller.addGestureRecognizer(tapRecognizer)
Tap handling method:
func tapHandler(recognizer: UITapGestureRecognizer) {
var touchLocation = recognizer.locationInView(recognizer.view)
if recognizer.state == UIGestureRecognizerState.Ended {
if let touchedLayer: CALayer = self.tabScroller.layer.hitTest(touchLocation) {
println("\(touchedLayer.name)")
}
}
}
I know that tapHandler is working because it will execute a basic println("") String placed anywhere inside of it.
But println("(touchedLayer.name)") continuously prints nil and I cannot figure out why it is not recognizing a tap above a CALayer.
The CALayers were added to tabScroller using
tabScroller.layer.addSublayer(brushB)
What am I doing wrong?
Your CALayer and UIView with the tap are on two different layers of the view hierarchy. Core Animation is lower level than view’s in fact, UIViews are made up of layers. Therefore, you have to use hitTest, or a like function to determine the layer which you are tapping.

Resources