Continuous press gesture on UIView in iOS - ios

Is there any way to get notified for continuous press on UIView? i.e. I keep pressing UIView with one finger and i want to keep calling particular method during that duration again and again.
I tried UILongPressGestureRecognizer but it just get notify of began, end, moved etc. Same as TouchesBegan.
Cheers

On TouchesBegan start a timer and perform a selector when you've reached a desired amount of time (long press).
On TouchesEnded invalidate the timer to prevent the selector from being performed.
I would also set up an extra flag detecting "fingerReleased":
Set fingerReleased = NO on TouchesBegan and fingerReleased = YES on TouchesEnded and put the code you want to execute in a:
if (!fingerReleased)
{
// Execute code
}

fire NSTimer in touchesBegan to call a selector that you want and invalidate it in touchesEnded method

What you can do is, use the combine functionality of UILongPressGestureRecognizer and NSTimer.
The following code should meet your requirement.
#property (strong, nonatomic) NSTimer *timer;
- (void) viewDidLoad
{
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPress:)];
[aView addGestureRecognizer: longPress];
}
- (void) longPress:(UILongPressGestureRecognizer*)gesture
{
if ( gesture.state == UIGestureRecognizerStateBegan )
{
self.timer=[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(callMethod) userInfo:nil repeats:YES];
[self.timer fire];
}
if ( gesture.state == UIGestureRecognizerStateEnded )
{
[self.timer invalidate];
}
}
- (void) callMethod
{
//will be called continuously within certain time interval you have set
}

Related

How to detect user inactivity since last touch on a particular viewcontroller in iOS?

I need to perform a particular action in a view controller when user don't touch a particular screen for 3 seconds.
How can I do that in iOS?
Override the view's touches began method and reset a timer when you get touches.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.timer invalidate];
self.timer = [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:#selector(inactivityTimerFired) userInfo:nil repeats:NO];
[super touchesBegan:touches withEvent:event];
}
You could target the view controller instead of self if you need to.
Add an NSTimer property/ivar to your view controller
Add a tap gesture recognizer to the view you want to monitor (or use touchesBegan:)
When the tap recognizer is hit, start the timer with an interval of 3.0s to call a function
If tap recognizer is hit before the function the timer calls, invalidate the timer and set it to nil
If the function that the timer was calling is hit, the user has been inactive for 3 seconds

How to delay a code to run after user touch the screen

I'm working on a Tap game, tap the screen to start the game.
I have this code and I need to make this animation to start after tap the screen. Now Is running when the game are loading (without tap the screen). What I need to change it to start this animation after the user tab (touch) on the screen? Thanks for your help.
[super viewDidLoad];
// Set Delay on Animations when Game Start - Animations Area ***** PERFORMS ****** 0.1
[self performSelector:#selector(Animation) withObject:nil afterDelay:0.1];
Add a tap gesture recognizer to your view and start the animation in the selector.
-(void) viewDidLoad
{
[super viewDidLoad];
UITapGestureRecognizer *tapGestureRg = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(beginAnimation)];
[self.view addGestureRecognizer:tapGestureRg];
}
-(void) beginAnimation
{
// Set Delay on Animations when Game Start - Animations Area ***** PERFORMS ****** 0.1
[self performSelector:#selector(Animation) withObject:nil afterDelay:0.1];
}
If you want to begin animation on touch begins then override -(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event and start the animation there.
You can add the Tap Gesture to screen.If you have UIViewController and you want to start animation on tap of UIViewController view tap then you can use this code as it is or you can change the view on which you want tap and start animation
-(void) viewDidLoad
{
[super viewDidLoad];
UITapGestureRecognizer *singleFingerTap =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(handleSingleTap:)];
[self.view addGestureRecognizer:singleFingerTap]; //Replace the self.view if you want to add single tap on another view with your view refrence
}
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer {
[self Animation]; //You should rename this method as animation.Follow naming conventions.
}

Recognize long press and pan gesture recognizers together

I have a view to which I've added both a pan and long press UIGestureRecognizer. The pan is used to move the view around. What I'd like to do is notice also that the touch has stopped moving (while remaining active) and trigger the long press.
What I find is that the long press is never triggered after the pan has begun. I've tried setting a delegate and implementing:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
NSLog(#"simultaneous %#", gestureRecognizer.class);
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
NSLog(#"require fail %#", gestureRecognizer.class);
return [gestureRecognizer isKindOfClass:[UIPanGestureRecognizer self]];
// also tried return YES;
// also tried return [gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer self]];
}
I've tried fooling with the pan gr's allowableMovement, also to no avail. I'm just about to give up and use a timer in the pan gr that get's invalidated and then reset on moves, but I was hoping that the SDK would do the state machine stuff for me.
In case anyone else needs it, here's the code that works for me. The goal is to have a view that is sensitive to both long press and pan, including a long press that isn't preceded by a pan and vice versa.
// setup
#property (strong,nonatomic) NSTimer *timer; // triggers the long press during pan
#property (strong,nonatomic) UIView *longPressView; // need this to track long press state
// view is the view we're interested in panning and long pressing
UIPanGestureRecognizer *panGR = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panGR:)];
[view addGestureRecognizer:panGR];
// this starts a long press when no pan has occurred
UILongPressGestureRecognizer *longGR = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressGR:)];
[view addGestureRecognizer:longGR];
When a pan begins or changes, start a timer. If the timer expires before the pan ends (touch releases), then we have a long press.
- (void)panGR:(UIPanGestureRecognizer *)gr {
if (gr.state == UIGestureRecognizerStateBegan) {
[self startTimer:gr.view];
} else if (gr.state == UIGestureRecognizerStateChanged) {
[self startTimer:gr.view];
// do whatever you want to do with pan state in this method
// in my case, I'm translating the view here
} else if (gr.state == UIGestureRecognizerStateEnded) {
if (self.longPressView) {
[self longPressEnded];
} else {
[self.timer invalidate];
}
}
}
We give the timer user info of the view. You might need to store other parts of the gesture state, like location, etc. Do it the same way, with the user info dictionary.
- (void)startTimer:(UIView *)view {
if (self.longPressView) return;
[self.timer invalidate];
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.8 target:self
selector:#selector(longPressTimer:)
userInfo:#{ #"view": view} repeats:NO];
}
-(void)longPressTimer:(NSTimer *)timer {
self.longPressView = timer.userInfo[#"view"];
[self longPressBegan];
}
Since the timer method won't have an associated gr, factor out all of the logic that we'd normally put in the gr handler so it can be called by both the timer handler and the gr handler.
- (void)longPressGR:(UILongPressGestureRecognizer *)gr {
if (gr.state == UIGestureRecognizerStateBegan) {
self.longPressView = gr.view;
[self longPressBegan];
} else if (gr.state == UIGestureRecognizerStateEnded) {
[self longPressEnded];
}
}
- (void)longPressBegan {
NSLog(#"long press began");
}
- (void)longPressEnded {
self.longPressView = nil;
NSLog(#"long press ended");
}
First of we have to register both long press and pag gesture events like,
let longPress = UILongPressGestureRecognizer()
longPress.delegate = self
longPress.addTarget(self, action: #selector(sendMessageLongPress(_:)))
let panGesture = UIPanGestureRecognizer()
panGesture.delegate = self
panGesture.addTarget(self, action: #selector(sendMessagePanGesture(_:)))
self.imgRecord.addGestureRecognizer(longPress)
self.imgRecord.addGestureRecognizer(panGesture)
then we have to setup to capture multiple touch events via delegate method. For this we have to extend UIGestureRecognizerDelegate and then use,
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
then we can implement events as we needed.
(in my case I wanted to cancel recording audio if user swipes, then I had to consider about touch started point and touch ended point as needed.)

How to check Pan gesture Not Moved

I have view where pan a view from one location to another. I can get the location of the points when moved, but I want to do some action when pan has not moved or is idle in certain point. I Don't see any state to show pan gesture is idle. The touch event I came across is UITouchPhaseStationary nut I don't know how to implement it.
There isn't a state for that, and UITouchPhaseStationary as explained in this post is for multi-touch, and lets you know if there is a second stationary finger on the screen while another finger is moving.
However, you can implement something like this yourself. Just make a timer with a time interval set to the timeout before the touch should be considered stationary and run it when the gesture's position changes. You'll want to invalidate the timer when the gesture ends as well as when the gesture changes to reset the timer. Here's an example.
- (void)gestureDidFire:(UIPanGestureRecognizer *)gesture
{
static NSTimer *timer = nil;
if (gesture.state == UIGestureRecognizerStateChanged) {
if (timer.isValid) {
[timer invalidate];
}
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timerDidFire:) userInfo:nil repeats:NO];
}else if (gesture.state == UIGestureRecognizerStateEnded) {
if (timer.isValid) {
[timer invalidate];
}
}
}
- (void)timerDidFire:(NSTimer *)timer
{
NSLog(#"Stationary");
}
You could implement touchesEnded:withEvent: to determine when touch has finished. Set a flag on your panGesture method and then check that value
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if (self.hasMoved) {
// Do something
self.hasMoved = NO;
}
}
Try this
- (void)panRecognized:(UIPanGestureRecognizer *)rec
{
CGPoint vel = [rec velocityInView:self.view];
if (vel.x == 0 && vel.y == 0)
{
// Not moved
}
else
// moved
}

Appropriate way to call and cancel a selector in Objective-c?

So I have an app that when a user touches a certain object, I kick-off a selector via delay. I am not sure I want or need the delay, but am not sure of best practice, maybe a queue? Anyway, here is what I need, regardless of what I have now.
WHAT I HAVE NOW
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(doSomething) object:self];
[self performSelector:#selector(doSomething) withObject:nil afterDelay:2.0];
When the user touches a certain object I need to kick-off a method, but if he/she touches the object again, I want to not call the method.
Use case #1:
User touches object
User does nothing for 2 seconds
Call selector
Use case #2:
User touches object then
User touches object .5 seconds later (so cancel selector call)
User touches object .3 seconds later (so cancel selector call)
User touches object .9 seconds later (so cancel selector call)
User doesn't touch anything for 2 seconds
Call selector
If feel like performSelector and cancelPrevious are hacky. Should I be using some sort of queue and then clearing out the queue every time the user touches again?
Or should I use a timer and just restart the timer each time the user touches it?
I wrote something quick, hopefully it'll help. Every time start is hit the timer resets
#interface ViewController ()
{
NSTimer *timer;
NSInteger seconds;
}
#end
- (IBAction)start:(id)sender
{
seconds = 5;
[timer invalidate];
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(execute) userInfo:nil repeats:YES];
}
- (void)execute
{
if(seconds > 0) {
NSLog(#"seconds: %li", (long)seconds);
seconds--;
}
else {
NSLog(#"fire");
[timer invalidate];
}
}

Resources