Subclass of GestureRecognizer does not work - objective-c

I am trying to subclass gesture recognizer to detect touch down , and not touch up .
The subclassing is working , only when i don't put this row to enable scrolling :
[touchDown requireGestureRecognizerToFail:scroller.panGestureRecognizer];
the subclass implementation :
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (self.state == UIGestureRecognizerStatePossible)
{
self.state = UIGestureRecognizerStateRecognized;
}
}
problem is , that i need that row ,otherwise the scroller will not move..
implementation :
TouchDownGestureRecognizer *touchDown = [[TouchDownGestureRecognizer alloc] initWithTarget:self action:#selector(handleTouchDown:)];
[scroller addGestureRecognizer:touchDown];
[touchDown requireGestureRecognizerToFail:scroller.panGestureRecognizer];//not work with it.

solution to enable scroller :
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}

Related

UISlider inside UIPageViewController

I have a PageViewController which is initialized like this:
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll
navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
On one of the pages, there's a UISlider.
My problem is that when I have transitionstyle set to UIPageViewControllerTransitionStyleScroll, it takes 150-200 ms before beginTrackingWithTouch is invoked on the slider.
This behavior is not seen when I use UIPageViewControllerTransitionStylePageCurl, where the UISlider is selected instantly.
This means that unless the user waits a bit before dragging the slider (a video progress), the page will turn instead, which is far from ideal.
The Page curl animation does not meet the demands of the app, so any explanation or workaround is appreciated.
Since with UIPageViewControllerTransitionStyleScroll gesture recognizers isn't available, you can use this:
for (UIView *view in pageViewController.view.subviews) {
if ([view isKindOfClass:[UIScrollView class]]) {
UIScrollView *scrollView = (UIScrollView *)view;
scrollView.delaysContentTouches = NO;
}
}
I solved this issue by add a pan gesture on UISlider and set:
self.sliderGesture.cancelsTouchesInView = NO; // make touch always triggered
and implement delegate method like:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return otherGestureRecognizer.view.superview == self.parentViewController.view;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// only receive touch in slider
CGPoint touchLocation = [touch locationInView:self.view];
return CGRectContainsPoint(self.slider.frame, touchLocation);
}
You can try to set the delegate of the page view controller gestures to the root view controller:
for (UIGestureRecognizer* gestureRecognizer in self.pageViewController.gestureRecognizers) {
gestureRecognizer.delegate = self;
}
And then prevent the touch of the gestures if it appears inside UISlider which is a subclass of UIControl:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
return ([touch.view isKindOfClass:[UIControl class]] == NO);
}
What helped me was to add pan-gesture-recognizer to UIView which holds UISlider, so in the end I have
UIPageViewController->UIScrollView->...->MyView->UISlider
The 'MyView' thing had pan gesture registered to it which did nothing, but served just to NOT propagate events to scroll view.

Custom gesture not sending action message

I have created a UIGestureRecognizer subclass called LongPressGestureRecognizer to simulate a long press gesture. (Yes, I know about the concrete subclass which already exists, I'm just learning objective-c and experimenting a bit.)
I have overridden only the following methods :
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
self.state = UIGestureRecognizerStatePossible;
[self performSelector:#selector(setState:) withObject:[NSNumber numberWithInt:UIGestureRecognizerStateRecognized] afterDelay:2];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[NSObject cancelPreviousPerformRequestsWithTarget:self];
self.state = UIGestureRecognizerStateCancelled;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[NSObject cancelPreviousPerformRequestsWithTarget:self];
self.state = UIGestureRecognizerStateFailed;
}
and in my view controller with a property recognizer to store the gesture recogniser, i have the following code:
- (LongPressGestureRecognizer *)recognizer
{
if (!_recognizer) {
_recognizer = [[LongPressGestureRecognizer alloc] init];
}
return _recognizer;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.recognizer addTarget:self action:#selector(log:)];
[self.view addGestureRecognizer:self.recognizer];
}
- (IBAction)log:(LongPressGestureRecognizer *)recognizer
{
//blah blah blah
}
My problem is that log: is not getting called at all... By logging the UIGestureRecogniserStates in the console, I know that the gesture recogniser is working as expected as far as states are concerned...
What am I doing wrong here?
There may be other issues as well, but I can tell you that your -performSelector:withObject:afterDelay: call isn't going to work the way you're hoping; you'll end up passing a pointer to an NSNumber object instead of an int, so state will be set to some kind of junk value. Create a method that will do self.state = UIGestureRecognizerStateRecognized and call that instead.

Disable pan in UIScrollView on pinch by 2 fingers

I want to stop scrolling after detect second touch and handle touches with my own pinch gesture.
I've tryed this in scroll view:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if(event.allTouches.count > 2)self.panGestureRecognizer.enabled = NO;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if(event.allTouches.count > 2)self.panGestureRecognizer.enabled = YES;
}
But it's doesnt works.
Try this:
scroll.panGestureRecognizer.maximumNumberOfTouches = 1;
But nothing
I find solution. I redefined UIScrollView, and add:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
And disable\enable pan gesture:
if(pinch.state == UIGestureRecognizerStateBegan) scroll.panGestureRecognizer.enabled = NO;
if(pinch.state == UIGestureRecognizerStateEnded) scroll.panGestureRecognizer.enabled = YES;
Now my pinch gesture works.
You can disable scroll View using like this :
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if ([touches count] == 2) {
//Disable scrollview
}
}
Set delayContentTouches property of UIScrollView to NO (instead of the default YES). This will allow the touch to propagate to the subviews of the scroll view immediately.
I found that setting the enabled property of UIPangestureRecognizer didn't work, at least in my code. However, setting the UIScrollView's scrollEnabled property has worked for me.
scrollView.scrollEnabled = false;
scrollView.scrollEnabled = true;

Why won't my Cocos2d test app fire "touchesBegan" events?

In my app delegate, I made sure I have the line:
[glView setMultipleTouchEnabled: YES];
And I have a simple layer meant only to figure out how multi touch works. The .mm file looks like:
#import "TestLayer.h"
#implementation TestLayer
-(id) init
{
if( (self=[super init])) {
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
}
return self;
}
-(void) draw{
[super draw];
glColor4f(1.0, 0.0, 0.0, 0.35);
glLineWidth(6.0f);
ccDrawCircle(ccp(500,500), 250,CC_DEGREES_TO_RADIANS(360), 60,YES);
}
-(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"got some touches");
}
-(void) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"some touches moved.");
}
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
NSLog(#"a touch began");
return FALSE;
}
#end
When I touch the screen, I always see "a touch began", but no matter how I touch it (simulator or actual device), I never see "some touches moved" or "got some touches".
Is there something further I need to do to make multi touch work?
Specifically, I'm just trying to do basic pinch-to-zoom functionality... I heard there is some sort of gesture recognizer for iPhone...does it work for Coco2ds? Would it work even if I can't get simple multi touch events to fire?
UIGestureRecognizers absolutely work for Cocos2D, I personally used them, you just need to add them to the correct view by using:
[[[CCDirector sharedDirector] openGLView] addGestureRecognizer:myGestureRecognizer];
Regarding your touches, I guess you enabled them for the scene you are working in?
scene.isTouchEnabled = YES;
In any case you shouldn't use the addTargetDelegate method, take a look here
add self.isTouchEnabled = YES; to your init
and for the gesture recognizers look at the other answer

ccTouchesBegan vs ccTouchBegan - Touch Detection & SIGABRT crash

I'm currently doing an application where I'm trying to detect touch-positions of the user.
I changed from ccTouchBegan to ccTouchesBegan in the process of implementing the "detect touch-position" function.
But I can't get it to work. I changed from ccTouchBegan to ccTouchesBegan:
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
instead of using:
-(BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event{
When i did this the whole thing crashes when I click the screen. Generating a SIGABRT error higlighting:
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
NSAssert(NO, #"Layer#ccTouchBegan override me");
return YES;
}
#endif
#end
So my questions are:
Why do you think it crashes?
What's the difference between ccTouchBegan & ccTouchesBegan? Multi touch abilities?
For further help, this is my code:
-(id) init
{
if( (self=[super init])) {
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA8888];
self.isTouchEnabled = YES;
// Set up background
background = [CCSprite spriteWithFile:#"Image.png"];
background.scaleX = 1;
background.scaleY = 1;
background.position = ccp(0,0);
[self addChild:background];
[[CCTouchDispatcher sharedDispatcher]addTargetedDelegate:self
priority:0
swallowsTouches:YES];
// Preload sound effect
soundFile = [SimpleAudioEngine sharedEngine];
if (soundFile != nil) {
[soundFile preloadBackgroundMusic:#"sound.wav"];
}
}
return self;
}
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
NSLog(#"ccTouchesBegan");
// Sets the sound variable to YES
ifOne = YES;
prevPos = [touch locationInView:[touch self]];
prevPos = [[CCDirector sharedDirector] convertToGL:[touch locationInView:touch.self]];
[self schedule:#selector(timerUpdate:) interval:0.1];
//return YES;
}
It's a nice feature in cocos2d which lets you swallow touches in cases that you want to handle only a single touch event.
Try adding this function to your class:
- (void) registerWithTouchDispatcher {
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MIN+1 swallowsTouches:YES];
}
ccTouchesBegan happens the second you tap the screen
ccTouchesEnded happens the second you let go after tapping on the screen
and instead of
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
instead of using:
-(BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event{
try using
-(void)ccTouchesBegan:(UITouch *)...
or
-(void)ccTouchesBegan:(NSSet *)...
or
-(BOOL)ccTouchesBegan:(NSSet *)...
your problem may just be invalid data types or some crap like that, my advice is just try switching the types of touches around.
I would give more info but you didn't provide alot of information to work with, so this is the best I can do.