How to replace TouchesBegan with UIGestureRecognizer - objective-c

here's the problem:
I'd like to move to using UIGestureRecognizer in my Apps.
For this reason I'd like to ditch TouchBegan/TouchEnded event's from my views.
However I don't understand how to manage when the touch began (user puts its finger on the screen) with UIGestureRecognizers.
The simplest one is UITapGestureRecognizer but the selector associated gets fired only when the TapGesture is completed (Well... it makes completely sense of course). But still the problem remains: how can I stop using touchesBegan and get that event anyway from UIGestureRecognizer?
Thanks!

Here is an example:
//Pan gesture
recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
((UIPanGestureRecognizer *)recognizer).minimumNumberOfTouches = 3; //number of fingers
recognizer.delegate = self;
[self.view addGestureRecognizer:recognizer];
[recognizer release];
- (void)handlePan:(UIPanGestureRecognizer *)recognizer
{
if (recognizer.state == UIGestureRecognizerStateBegan)
{
//do something
} else if (recognizer.state == UIGestureRecognizerStateEnded)
{
//do something
}
}
Also implement UIGestureRecognizerDelegate in .h file. May be you need to do self.view.userInteractionEnabled = YES depending on the view you're using. e.g., if it's UIImageView, the you need to set userInteractionEnabled = YES, default is NO

For what you are tryin ti do you can't. The gesture recoginizers are for high level gestures so they behaive the same across all apps (think swipes, the timing required for a double tap, etc). For low level control and to do things that the recognizers can't you will still have to implement logic in touchesbegan, touchesEnded, etc.

Why not implement your own touchesBegan in a UIGestureRecognizer subclass -- intercept the message, extract the information you'd like, and then pass the message along to super's touchesBegan?

Related

iOS10 UITextView touch event crash

I have a very strange issue, UITextView touch event crash on double tap whereas same code works with < iOS10 version. (It means below iOS10 version there is no crash for press gesture recognizer)
Actually, I am adding the double tap and log press gesture based on permission. If the user has permission to comment then add gestures in viewDidLoad methods. Comment is allowed only with double tap or long press
singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapGestureAction:)];
singleTapGesture.numberOfTapsRequired = 1;
// adding gesture to open window for commenting only when he has writing access
if (canComment) {
longPressgesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressGestureAction:)];
longPressgesture.minimumPressDuration = 0.2;
doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doDoubleTap:)];
doubleTap.numberOfTapsRequired = 2;
}
On single tap
-(void)singleTapGestureAction:(UITapGestureRecognizer*)tapGestureRecognizer{
if (isSingleTapped) {
isSingleTapped = NO;
return;
}
isSingleTapped = YES;
UITextView *textView = (UITextView *)tapGestureRecognizer.view;
[self.commentView becomeFirstResponder]; // becomeFirstResponder
}
On double tap
-(void)doDoubleTap:(UITapGestureRecognizer*)tapGestureRecognizer
{
UITextView *textView = (UITextView *)tapGestureRecognizer.view;
[self.commentView becomeFirstResponder]; // becomeFirstResponder
// To show the UIMenuController menu
[self setCommentMenuToolTipWithRect:completeRect];
}
NOTE: I am adding [self.commentView becomeFirstResponder]; on every gesture action
UITextView delegate methods
- (void)textViewDidBeginEditing:(UITextView *)inView
{
[self.commentView becomeFirstResponder];
range=[self.commentView selectedRange];
}
USE CASE:
When I double tap to select any word then APP CRASH and UIMenuController does not appear,
but if I add the following line app does not crash
- (void)textViewDidChangeSelection:(UITextView *)textView{
[textView resignFirstResponder];
} // app does not crash
and UIMenuController appears with comment menu items that's great. I was happy that I have fixed the crash issue.
But there is another problem, when I press outside, menu hides and
select any word AGAIN then It does not appear SECOND time.
I have tried all the possible way to show the menu for returns
YES/TRUE to canBecomeFirstResponder. I know, there has to be a view
that claims firstResponder for the menu to show. but how ?
On second time touch, not even calling any gesture recognizer method
From the logs it is clear that when double tap is recognized, same touch update is also sent to another gesture recognizer, which fails.
So, a simple solution would be to avoid detection of other gestures on double tap.
This can simply be achieved by making all other gestures on commentView require doubleTap to fail using requireGestureRecognizerToFail. just add the condition in addGestureToTextView method as shown below.
if (withDoubleTap && self.canScreenPlayEdit) {
[self.commentView removeGestureRecognizer:singleTapGesture];
[self.commentView addGestureRecognizer:doubleTap];
[self.commentView addGestureRecognizer:longPressgesture];
for (UIGestureRecognizer *recognizer in self.commentView.gestureRecognizers) {
[recognizer requireGestureRecognizerToFail:doubleTap];
}
}
This does solve the crash and also shows the menu without calling resignFirstResponder in textViewDidChangeSelection.
However, there seem to be many issues in your code. PLSceneDetailsVC is too complicated and you need to simplify the code. You need to streamline the gesture management or you will end up facing many more such issues.
longPressgesture.minimumPressDuration = 0.2;
My guess the problem is here. 0.2s is way too small to be used for longPress. Probably both were triggered (longPress and double tap).
Change it to higher like 1.5s.

Recognizing UIScrollView movement by pixel

I need to change UIScrollView subviews according to their place on the screen, so that they will get smaller while moving up and bigger while moving down.
Is there any way to know the contentOffset with the change of every pixel?
I catch the scrollViewDidScroll: method, but whenever the movement is fast there might be some 200pxls change between two calls.
Any ideas?
You have basically two approaches:
subclass UIScrollView and override touchesBegan/Moved/Ended;
add you own UIPanGestureRecognizer to your current UIScrollView.
set a timer, and each time it fires, update your view reading _scrollview.contentOffset.x;
In the first case, you would do for the touch handling methods:
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
UITouch* touch = [touches anyObject];
_initialLocation = [touch locationInView:self.view];
_initialTime = touch.timestamp;
<more processing here>
//-- this will make the touch be processed as if your own logics were not there
[super touchesBegan:touches withEvent:event];
}
I am pretty sure you need to do that for touchesMoved; don't know if you also need to so something specific when the gesture starts or ends; in that case also override touchesMoved: and touchesEnded:. Also think about touchesCancelled:.
In the second case, you would do something like:
//-- add somewhere the gesture recognizer to the scroll view
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panView:)];
panRecognizer.delegate = self;
[scrollView addGestureRecognizer:panRecognizer];
//-- define this delegate method inside the same class to make both your gesture
//-- recognizer and UIScrollView's own work together
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return TRUE;
}
The third case is pretty trivial to be implemented. Not sure if it will give better results that the other two.

UIButton responds to UIPanGestureRecognizer on top of it

I just added a UIView with UIPanGestureRecognizer on top of my view.
The view has several UIButtons which respond to touchUpInside events.
What's weird is that ever since I brought the UIPanGestureRecognizer, when panning, if the UIButton is right underneath the "Panning view", the button would trigger which is not what I am after.
Of course I could make a BOOL flag for "panning", so that the button won't fire, but it seems to me like bad engineering and surely something I am missing. I guess after the first touch, both views intercept the event.
Is it possible to overcome this?
Thanks
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch {
if ([touch.view isKindOfClass:[UIButton class]]) {
return NO;
}
return YES;
}
use this method to differentiate GestureRecognizer and Button Acton.
Hope this helps you.
Not sure if this help
[[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanned:)];
- (void)handlePanned:(UIPanGestureRecognizer*)thePanner{
if (thePanner.state == UIGestureRecognizerStateChanged ){
//disable button
}else if (thePanner.state == UIGestureRecognizerStateEnded) {
//enable button
}else if ( thePanner.state == UIGestureRecognizerStateFailed ){
//enable button
}
}
you can sort-out your problem by following two tactics:-
1)When adding the gesture in your required view, be sure that it will not added with un-required view, in this case is your UIButton, &;
2)At the method/delegate where you handle the case of detecting the gesture, ie what gesture do, before the implementation you assure that this is not the UIButton.
If you insist, I'll try for sample code.
Have a good day ahead. :)

How to recognize rubbing gesture?

I was wondering how to create some type of recognizer for a rubbing gesture.
You can see this gesture in Talking Tom Cat app and I would love to have this gesture in my clone of the app. Could you please guide me a little?
As I understand it, it is probably a swipe up and swipe down, however I don't know how to implement it the same as in the Talking Tom Cat - that means, playing animation and sound while I am rubbing the character. (I know how to play sound and animation, just don't know how to implement it with this gesture)
Also I am not sure, if it is better done by using UIGestureRecognizer or touchesBegan, Moved, Ended etc.
You could try something like this:
first add gesture recognizer where you are setting up the view.
[myView addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)]];
Then add code to handle gestures.
-(void)handlePan:(UIGestureRecognizer *)sender
{
if(sender.state == UIGestureRecognizerStateBegan) {
[self startAnimation];
} else if (sender.state == UIGestureRecognizerStateEnded) {
[self stopAnimation];
}
}
hope that helps.

How to stop UIPanGestureRecognizer from recognizing taps

On one of the UIViewControllers of my iPhone app, I have a UIPanGestureRecognizer attached so that when the user swipes to the left or to the right the app advances or goes back one screen. However, the problem is that when the user taps (rather than swipes) on the screen it still advances. How can I stop this from happening. I have pasted the relevant code below:
-(void) addGestureRecognizer
{
UIPanGestureRecognizer *pan;
pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(swipeRecognized:)];
[pan setMinimumNumberOfTouches:1];
[self.view addGestureRecognizer:pan];
}
-(void) swipeRecognized: (UIPanGestureRecognizer *) recognizer
{
if(recognizer.state != UIGestureRecognizerStateBegan) return;
CGPoint velocity = [recognizer velocityInView:self.view];
if(velocity.x > 0)
{
[self.navigationController popViewControllerAnimated:YES];
}
else
{
#try
{
[self performSegueWithIdentifier:NEXT_STEP_SEGUE sender:self];
}
#catch (NSException *exception)
{
//Silently die...muhaha
}
}
}
I would suggest using a UISwipeGestureRecognizer for swipes. Is there a particular reason you're use a pan?
With a UISwipeGestureRecognizer you can specify for which direction it should recognize the gesture.
It's also better for your users, to use the appropriate gestures. That way, they'll feel right at home :)
You should either use a UISwipeGestureRecognizer, as suggested by fguchelaar, or use the translationInView: method to figure out how far the user has actually moved the finger (for taps, that should be near zero).
You also shouldn't return early if the state is not UIGestureRecognizerStateBegan (first line in your method), otherwise your method will only get called once, when the gesture begins, but not during the gesture. If that's what you actually want, a UISwipeGestureRecognizer is all you need. The benefit of the pan gesture recognizer is mostly that you can track the user's finger and provide direct feedback (like moving the view while the finger is still down).