I want to customize my long press gesture recognizer in the following ways:
1) When I hold down on an object for 0.5 seconds, the object darkens, and
2) When I continue to hold down on the object for another second (total of 1.5 seconds), some action happens (e.g. the object disappears).
Essentially, by holding down on an object for a minimum of 1.5 seconds, two actions happened at two separate times. I also have a tap gesture recognizer, which might affect things.
The answer from #nathan is essentially fine but a detail is missing you need to implement the UIGestureRecognizerDelegate to allow both gestures works simultaneously, so this is my code
class ViewController: UIViewController, UIGestureRecognizerDelegate{
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//this for .5 time
let firstGesture = UILongPressGestureRecognizer(target: self, action: #selector(firstMethod))
//this for 1.5
let secondGesture = UILongPressGestureRecognizer(target: self, action: #selector(secondMethod))
secondGesture.minimumPressDuration = 1.5
firstGesture.delegate = self
secondGesture.delegate = self
self.view.addGestureRecognizer(firstGesture)
self.view.addGestureRecognizer(secondGesture)
}
func firstMethod() {
debugPrint("short")
}
func secondMethod() {
debugPrint("long")
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool{
return true
}
}
Hope this help
See Reinier's solution as it's the correct one. This one adds a delay to satisfy require(toFail:)
You can set the timing using the property minimumPressDuration (in seconds, default is 0.5)
let quickActionPress = UILongPressGestureRecognizer(target: self, action: #selector(ViewController.zeroFiveSecondPress(gesture:))) // 0.5 seconds by default
let laterActionPress = UILongPressGestureRecognizer(target: self, action: #selector(ViewController.oneFiveSecondPress(gesture:)))
laterActionPress.minimumPressDuration = 1.5
someView.addGestureRecognizer(quickActionPress)
someView.addGestureRecognizer(laterActionPress)
// If 1.5 detected, only execute that one
quickActionPress.require(toFail: laterActionPress)
#objc func zeroFiveSecondPress(gesture: UIGestureRecognizer) {
// Do something
print("0.5 press")
}
#objc func oneFiveSecondPress(gesture: UIGestureRecognizer) {
zeroFiveSecondPress(gesture: gesture)
// Do something else
print("1.5 press")
}
Related
I have an app that manipulates view with gestures. View is connected to pan, pinch and rotate gestures. If I start interacting with the view with two fingers all gestures are working simultaneously (expected behavior). But if you start with one finger pan, pinch and rotate gestures do not work. None of the methods in the delegate are called when I am trying to start pinch or rotate while panning.
shouldRecognizeSimultaneouslyWith
is always true.
Expected behavior,
one finger pan on the view
add second finger and start pinch/rotate interaction
(similar to instagram stories editor)
From a clean project I added the following:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
view.addGestureRecognizer({
let gesture = UIPanGestureRecognizer(target: self, action: #selector(onPan))
gesture.delegate = self
return gesture
}())
view.addGestureRecognizer({
let gesture = UIPinchGestureRecognizer(target: self, action: #selector(onPinch))
gesture.delegate = self
return gesture
}())
view.addGestureRecognizer({
let gesture = UIRotationGestureRecognizer(target: self, action: #selector(onRotate))
gesture.delegate = self
return gesture
}())
}
#objc private func onPan(_ sender: UIPanGestureRecognizer) {
print("Did pan to \(sender.translation(in: sender.view))")
}
#objc private func onPinch(_ sender: UIPinchGestureRecognizer) {
print("Did pinch to \(sender.scale)")
}
#objc private func onRotate(_ sender: UIRotationGestureRecognizer) {
print("Did rotate to: \(sender.rotation)")
}
}
extension ViewController: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { true }
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { true }
}
All 3 methods report changes after the scenario you described:
I started with single touch drag gesture
I pressed another finger and started rotating gesture and pinching gesture
Does already this example not work for you? What is the behavior you are experiencing and what is expected behavior?
I've seen other people ask similar questions but I didn't really understand the answers. I'm new to iOS design and relatively new to Swift, so I apologize for my ignorance. My goal is to have the user swipe in a given direction and have the program repeat a certain action until the user lifts his finger from the screen. From what I can tell, I need to implement the gestureRecognizer function.
I think I probably implemented things in a weird way:
In my GameViewController.swift file, I implemented the following, as a separate function under the UIViewController class.
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer)-> Bool {
return true
}
Which I understand to mean that any two UIGestureRecognizer actions can occur at the same time.
In my GameScene.swift file, under the SKScene class, I have:
override func didMove(to view: SKView) {
...
swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(swipeR))
swipeLeft = UISwipeGestureRecognizer(target: self, action: #selector(swipeL))
longPress = UILongPressGestureRecognizer(target: self, action: #selector(printLongPress))
longPress.delegate = self as? UIGestureRecognizerDelegate
swipeRight.delegate = self as? UIGestureRecognizerDelegate
swipeLeft.delegate = self as? UIGestureRecognizerDelegate
swipeRight.direction = .right
swipeLeft.direction = .left
view.addGestureRecognizer(swipeRight)
view.addGestureRecognizer(swipeLeft)
view.addGestureRecognizer(longPress)
}
#objc func swipeR() {
print("Right")
}
#objc func swipeL() {
print("Left")
}
#objc func printLongPress() {
print("Long press")
}
Ideally in the example code described here, if you were to swipe right and hold onto the screen, you'd see "Swipe Right" "Long Press" "Long Press" etc.
Any ideas what I'm doing wrong here?
Thanks in advance!
I have one main view and 4 subviews of the mainview they all have their UITapGestureRecognizer, when I tap on one subview how can it be triggered both views. Example below,
if I tap to subview 1 desired log would be:
subview1 is clicked
MainView is clicked
My Code
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let mainGesture = UITapGestureRecognizer(target: self, action: #selector(mainGestureActivated))
self.view.addGestureRecognizer(mainGesture)
let subGesture = UITapGestureRecognizer(target: self, action: #selector(subViewGestureActivated))
self.subview1.addGestureRecognizer(subGesture)
}
#objc func mainGestureActivated(){
print("MainView Clicked")
}
#objc func subViewGestureActivated(){
print("Subview Clicked")
}
it prints only subview clicked! Is it possible to trigger both gestureRecognizers since main is encapsulating other.
First you should conform to UIGestureRecognizerDelegate in your VC, and then implement the delegate func of shouldRecognizeSimultaneouslyWith. Inside the function, you should detect if the gestureRecognizer, and the otherGestureRecognizer are the wants you want, and if so, you should allow them to work simultaneously,
Conform to delegate, and Declare gesture recognizers outside of viewDidLoad (because you need to access them in the delegate method later.)
var mainGestureRecognizer = UITapGestureRecognizer()
var subGestureRecognizer = UITapGestureRecognizer()
Initialize your recognizers, and set your VC as their delegate:
mainGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(mainGestureActivated))
subGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(subViewGestureActivated))
mainGestureRecognizer.delegate = self
subGestureRecognizer.delegate = self
Implement the delegate function mentioned above to allow simultaneous recognition for subView and mainView:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer == subGestureRecognizer && otherGestureRecognizer == mainGestureRecognizer {
return true
}
return false
}
And if you want it to work for 4 different subviews, then you should check with else if statements inside the delegate method.
I just made a simple app to try out any type of gestures. I got to the tap gesture. So I thought, what if I made a fast tap game kind of application that counts the amount of taps the user performed. But soon enough I ran into some issues.
It did not count all the taps. If I began to tap as fast as possible, but it skipped taps.
The idea is I programmatically created a view in the superview and added a tapGestureRecognizer on the view. And simply put the “taps” into a label in the app.
It seems to fail to receive a system gesture on time.
The code:
let tap = UITapGestureRecognizer(target: self, action: #selector(tapped(sender:)));
tap.numberOfTapsRequired = 1;
animationView.isUserInteractionEnabled = true;
animationView.addGestureRecognizer(tap);
The function:
#objc func tapped (sender :UITapGestureRecognizer) {
self.counter += 1;
self.lblScore.text = String(self.counter);
}
I have an animationView that I made "tappable" and it works. Every time I tap the animationView it increments the value of 'counter' that works! but every time I get this error if I tap too fast:
<_UISystemGestureGateGestureRecognizer: 0x1c01c4b00>: Gesture: Failed to receive system gesture state notification before next touch
Create Gesture :
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:)))
panGesture.delegate = self
Button.addGestureRecognizer(panGesture)
let longTapGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongTapGesture(_:)))
Button.addGestureRecognizer(longTapGesture)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture(_:)))
Button.addGestureRecognizer(tapGesture)
let rotateGesture = UIRotationGestureRecognizer(target: self, action: #selector(handleRotateGesture(_:)))
rotateGesture.delegate = self
Button.addGestureRecognizer(rotateGesture)
let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(handlePinchGesture(_:)))
pinchGesture.delegate = self
Button.addGestureRecognizer(pinchGesture)
Gesture click Event :
extension AddTextVC: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
#IBAction func handlePanGesture(_ recognizer: UIPanGestureRecognizer) {
}
#IBAction func handlePinchGesture(_ recognizer: UIPinchGestureRecognizer) {
}
#IBAction func handleRotateGesture(_ recognizer: UIRotationGestureRecognizer)
{
}
#IBAction func handleTapGesture(_ recognizer: UITapGestureRecognizer) {
}
#IBAction func handleLongTapGesture(_ recognizer: UITapGestureRecognizer) {
}
}
I want to add a gesture recognizer to one of my views to detect taps,
here is my code:
class DateTimeContainer: UIView, UIGestureRecognizerDelegate {
override func awakeFromNib() {
let gesture = UITapGestureRecognizer(target: self, action: #selector(self.onTap))
gesture.delegate = self
self.addGestureRecognizer(gesture)
}
func onTap(_ gestureRecognizer: UITapGestureRecognizer) {
openDatePicker()
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if touch.view?.tag != self.datePickerTag && !self.isDatePickerOpen() {
return true
}
return false
}
}
When I tap on my view the code enters into the gestureRecognizer shouldReceive method and enters the condition for which it returns true.
But the onTap method is never called, can someone tell me why?
EDIT
Adding self.isUserInteractionEnabled = true I managed to get it working.
But it had a strange behaviour: it was like it received the tap only in the main view and not in subviews.
So to make it simple I solved by adding a button inside my view and by using:
self.button?.addTarget(self, action: #selector(onTap), for: .touchUpInside)
You may have forgotten to do this:
self.isUserInteractionEnabled = true
Also, typically the UIViewController subclass is usually the target and contains the method used for the tap gesture.
You don't need to set a delegate for a tap gesture (usually)
You can You Gesture
let recognizer = UITapGestureRecognizer(target: self,action:#selector(self.handleTap(recognizer:)))
userImage.isUserInteractionEnabled = true
recognizer.delegate = self as? UIGestureRecognizerDelegate
userImage.addGestureRecognizer(recognizer)
And method call selector
func handleTap(recognizer:UITapGestureRecognizer) {
// do your coding here on method
}
Try Writing the codes
let gesture = UITapGestureRecognizer(target: self, action: #selector(self.onTap))
gesture.delegate = self
self.addGestureRecognizer(gesture)
inside init block not in awakeFromNib()
I noticed that you are adding your gesture on a UIView. As such, at that ViewController where you have added this ContainerView, inside the viewDidLoad method, add this line of code.
override func viewDidLoad() {
super.viewDidLoad()
self.YourContainerView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(YourCurrentViewController.openDatePicker())))
self.YourContainerView.isUserInteractionEnabled = true
}
Note. Do not forget to add isUserInteractionEnabled to true.