hot to disable touch handling in CCLayer in cocos2d - iphone

I've got a CCLayer subclass i'm using to display some sprites and to show some animations. Also it has a CCMenu with some items. When user selects some of the menu item i want to run an animation and then to show another scene. But i want user not to be able to touch anything on the screen while animation is running.
Of course, i can just disable handling touches in my callbacks, but maybe there is more simple way - just to disable all touch handling for a while ?

Disable touch dispatcher before animation running and enable touch dispatcher after animation stopped. Here is the code snippet:
[[CCDirector sharedDirector] touchDispatcher].dispatchEvents = NO;
CCAnimation* animation = [CCAnimation animationWithFrame:#"numberexplode" frameCount:5 delay:0.2];
CCAnimate* animate = [CCAnimate actionWithAnimation:animation];
CCCallBlock* completion = [CCCallBlock actionWithBlock:^{
[[CCDirector sharedDirector] touchDispatcher].dispatchEvents = YES;
}];
CCSequence* sequence = [CCSequence actions:animate, completion, nil];
[self runAction:sequence];

You want to look at the CCTouchDispatcher singleton class. If you add a targeted touch handler that swallows touches (and does nothing) then you won't get any touches handled. As far as I can tell there's no way to totally disable touches.
Alternatively you can make a new CCLayer that's on top of everything else (I think z order really high will do this), and make it clear, and have it do nothing with touches.
hope that helps.

Related

Adding a scene above Uiview on CCDirector

I want to add a UiView on the CCDirector and a scene on that UIView.
Actually i have some custom Cocoa touch control and that i want to use in my game and above that i need a scene.
Currently i am doing like this.
[[[CCDirector sharedDirector] openGLView] addSubview:gridViewController.view];
and after that line i do
[[CCDirector sharedDirector] runWithScene:[MYScene scene]];
[MYScene scene] returns a CCScene instance.
But what happens that on my custom cocoa touch control is visible but my scene is not visible at all.
Can any one guide me for that how i can accomplish my task.
You should try the opposite approach and have the UIView handled as a CCNode, then add the node to the scene.
There is even a class that makes this easily possible: CCUIViewWrapper.
If you use it, you can do the following:
CCUIViewWrapper* wrapper = [CCUIViewWrapper wrapperForUIView:view];
wrapper.contentSize = view.frame.size;
[self addChild:wrapper z:1 tag:LayerTagUILayer];
and you will get both the UIView and the scene added to CCDirector.glView. The UIView can be any, you can even define it in IB.

cocos2d iphone wait for action to complete/finish

I'm trying to force an action to complete before another action can be called. I've looked all over for different examples of timing delays and CCSequencing and this is what i have so far, but it still isn't working. I need the full .5 to come out before another action (whether it be left, right or down) can be called. Everything is working as long as you don't hit the buttons before an action finishes, thats when the previous action is cut short.
-(void) moveup{
CCNode *mySprite = [self getChildByTag:kTagSprite];
//moves sprite up 30 pixels in 1/2 second
[mySprite runAction: [CCMoveTo actionWithDuration:.5 position:ccp(mySprite.position.x, mySprite.position.y+30)]];
}
//this is inside CCtouchesbegan. upsprite is a box that gets clicked to move the object up
if(CGRectContainsPoint([upsprite boundingBox], point)){
//should call CCSequence of events, method moveup, and then CCDelayTime, forcing a pause
[self runAction:[CCSequence actions:[CCCallFuncND actionWithTarget:self selector:#selector(moveup) data:nil], [CCDelayTime actionWithDuration:.5], nil]];
}
I've tried pausing using [[CCDirector sharedDirector] pause] but this freezes everything, including the sprite I'm moving. The CCDelayTime doesn't seem to be working in my current example.
I know this question is kind of a duplicate of a couple others about ccsequencing, so if it needs to be marked as a dupe, that's fine, but I'd really like some help as it's really holding me back.
You could make a BOOL "lock" variable, and only run the action on "mySprite" :
if(lock) {[mySprite runAction:...];}
set the BOOL lock to NO in the beginning and everytime you begin the action set it to "YES"...then add a CCCallFunc to the CCSequence to set the lock back to "NO"

Always animate UIView to user's tap location

I have a UIView that I want to animate to the position where the user's finger currently is.
So, when the touch begins, it has to animate to the tap location.
When the touch moves, it has to adjust its destination to the new location.
When the touch ends, it has to finish the animation to the last tap location.
I tried several things but none worked out. I tried creating a new animation with beginsFromCurrentState in touchesMoved, but that didn't work too.
It seems you're complicating this a little bit. If you are implementing touchesMoved, you are wanting a drag. If you are more interested in a tap, then touchesEnded is where you want to implement this. Either way, you just need to get the location in view and update your UIView's center based upon that location. If you wrap the change to the center value in a UIView animation block, it will animate to the position as it updates.
If you are using iOS 3.2 or later, you can use gesture recognizers which makes this pretty trivial:
// In viewDidLoad or some such
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(viewWasTapped:)];
[[self view] addGestureRecognizer:tapRecognizer];
Then declare your action selector:
- (void)viewWasTapped:(UITapGestureRecognizer*)recognizer
{
CGPoint location = [recognizer locationInView:[self view]];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0f]; // take a second to animate
[viewToMove setCenter:location];
[UIView commitAnimations];
}
This will animate the view from one location to another. This is referred to as implicit animation. Using Core Animation classes such CAKeyframeAnimation or CABasciAnimation would be explicit animation.
Need a few more details for this.. But are you sure you are using touchesBegan, touchesMoved and touchesEnded from the appropriate view (ie not the view you want to move)? Also make sure the userInteractionEnabled = YES. Try logging the touches from touchesBegan to make sure you are receiving the touches.
Or you might want to just check out Apples MoveMe sample code at https://developer.apple.com/library/ios/#samplecode/MoveMe/Introduction/Intro.html where they have solved all those problems for you.

Is it possible to differentiate between a long press and a tap on a button?

Can we call different actions / delegates in response to the two different events of
A tap on a UIButton
A tap-and-hold on a UIButton
?
Yes, it's reasonably easy to implement this using a UILongPressGestureRecognizer (on iPhone OS 3.2+). A long press will be handled by the gesture recognizer, and a short tap will pass through to the button's normal action.
For example, I subclassed UIButton and added the following method for specifying a long touch action to go along with a tap (longPressGestureRecognizer is an instance variable):
- (void)setLongTouchAction:(SEL)newValue
{
if (newValue == NULL)
{
[self removeGestureRecognizer:longPressGestureRecognizer];
[longPressGestureRecognizer release];
longPressGestureRecognizer = nil;
}
else
{
[longPressGestureRecognizer release];
longPressGestureRecognizer = nil;
longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:[[self allTargets] anyObject] action:newValue];
[self addGestureRecognizer:longPressGestureRecognizer];
}
}
I then could do the following to set both short tap and long press actions that will be handled by the same target:
[undoButton addTarget:self action:#selector(performUndo:) forControlEvents:UIControlEventTouchUpInside];
[undoButton setLongTouchAction:#selector(showUndoOptions:)];
As you can see, this is useful for the undo buttons you see in title bars of many iPad applications.
Brad Larson's answer looks pretty good but here's another one that might give you a bit more flexibility/control of what you want or might want to do.
You subclass UIButton, you override the touchesBegan and touchesEnded methods so that when the user starts a touch you call
[self performSelector:#selector(detecetedLongTap) withObject:nil afterDelay:1.0];
and in the touchesEnded you call:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(detecetedLongTap) object:nil];
to cancel the event if the finger was lifted too soon.
You can get full code for this in this blog post:
http://www.isignmeout.com/adding-long-tap-functionality-uibutton/
The best solution I can think of, is to create another class, and subclass UIButton. Then on Interface Builder (if that's what you're using), you can set the button's class to the custom class you just created.
So in this new class, you have to override a method called
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
This is basically telling you that someone pressed down on your button. The touches is an NSSet and it holds all the information for all the fingers that are pressing down on the screen. If you only are interested in the one that's pressing on the button itself, you'll probably have something like:
NSSet *myTouches = [event touchesForView:self.view];
So now that you have the touches that correspond to your button, you have to find out how many times the user tapped on that button. You do that with something like:
NSUInteger numTaps = [[myTouches anyObject] tapCount];
If this number is 2, that means the user just double tapped your button. Now comes the next part. How do you know if the user is holding that button? Well when the user lets go of the screen, another method gets called. You'll need to override that one as well:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
This is where you know if the person has stopped touching the screen or if his finger is still on it. If his finger is still on it, then this event hasn't been called yet.
Now enough with the background
Here's my suggestion to you. I suggest you override the touchesBegan: method and check if the number of taps in the button is 2. If so, then start a timer that does what you need it to do, for as long as you need it to be done, and then on the touchesEnded: method, you'll go ahead and stop that timer, and this way you will have done whatever it is that you needed to do, for as long as you needed to do it OR as long as the user has held on to the button.
I hope this helps, obviously I didn't write the whole code for you, you'll have to experiment and research that stuff, but if you have any questions, I'll be happy to lend a helping hand :)

How to detect touches on MPMoviePlayerController window and still having the standard playback controlling

I have the MPMoviePlayerController set up to play a movie.I want to detect a touch on the movie for bringing up few buttons.I used the code :
// The movie's window is the one that is active
UIWindow* moviePlayerWindow = [[UIApplication sharedApplication] keyWindow];
// Now we create an invisible control with the same size as the window
UIControl* overlay = [[[UIControl alloc] initWithFrame:moviePlayerWindow.frame]autorelease];
// We want to get notified whenever the overlay control is touched
[overlay addTarget:self action:#selector(movieWindowTouched:) forControlEvents:UIControlEventTouchDown];
// Add the overlay to the window's subviews
[moviePlayerWindow addSubview:overlay];
but then the play back controllers doesn't appear , I guess because the player window doesn't get the touch.how can I keep the player controllers and still detects touches?
thanks
You have to create your own UIView's subclass and add it as overlay.
In the method -(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event you can do everything you want to do when the screen is touched.
I have done this in my project. I have implemented design of the VideoOverlay( the UIView's subclass) in Interface Builder. It is much easier when you have to add other elements that user have to interact with.