Second time entering scene, input not accepted - ios

I've found a peculiarity with Cocos2D and I cannot seem to fix it. From the AppDelegate I load into a Menu, which is a CCScene. The scene holds a CCLayer, which itself holds the CCMenu. Everything works find the first time through. After my game has ended, I bring the user to a GameOverScene and prompt them to return to the menu. I am reloading the menu scene and calling [[CCDirector sharedDirector] replaceScene:menu]. After entering this menu, though, not all input functions. I can no longer tap on menu items, but I can pan / multi-touch on them to trigger the item. What gives?
Is there some way on initialization to reset the CCScene to receive input, and if so, will this mess up CCMenu's input receiving?

Did you override any of the on* methods like onEnter, onExit, etc. in any of your classes?
If so, you must call the super implementation (ie [super onEnter]) in each, otherwise some cocos2d functionality like scheduling or input may stop working.

I finally figured it out, and the answer was right under my nose. Earlier in my game development I needed a way to stop KKInput from swallowing gestures. I'm not entirely sure of the ramifications of this action, but I was able to do so like this:
KKInput* input = [KKInput sharedInput];
UITapGestureRecognizer* tapGestureRecognizer;
tapGestureRecognizer = input.tapGestureRecognizer;
tapGestureRecognizer.cancelsTouchesInView = NO
It seems that the default Kobold2D behavior is to swallow all touches, which was preventing the CCMenu from receiving any tap gesture.

Related

iOS - Superview gesture recognizer getting called when not wanted, can't cancel child view touch event

I have a super view that has a UITapGestureRecognizer on it. It allows touches within the view because there are clickable items within the view.
When these items are clicked on, I want to take a specific action, not the generic one that covers the entire superview. Unfortunately in my TouchDown event of my child control I don't know how to stop the event here. I know I could create a kludge flag, but this seems like the wrong way to go.
Any advice?
James
OK I got a solution. Totally my problem. I was playing around with trying to get all touches to work and at one point I had set cancelTouchesInView = true on the UITapGestureRecognizer superview. While this didn't stop the other touches from happening, for whatever reason the touches carried through to the superview as well. I understand that this explanation probably makes no sense, but that's what did it. Still trying to wrap my head around how iOS does touch.

Begin UIPanGesture Event From A Pressed State At Time Of Instantiation

Is there a way to begin a UIPanGestureEvent if the finger is already pressed at the time the object is instantiated?
I have a situation where when a user holds their find on a screen I create a UIView under their finger.
I want them to be able to drag that around and as such I have put a UIPanGestureRecognizer inside the UIView.
Problem is I need to take my finger off and put it back to trigger the UIPanGestureRecognizer to start up. I need it to start from an already pressed state.
Do you know how I can activate a UIPanGesture from an already pressed state i.e. can I get the touch event thats already active at the time of instantiation and pass it along?
You can do it, but the UIPanGestureRecognizer will need to exist already on the view behind the view you create (and you will then have to adjust your calculations based on this; not difficult).
The reason is that, under the circumstances you describe, the touch does not belong to the UIView you create - it belongs to the UIView behind it, the one that the user was originally touching. And given the nature of iOS touch delivery, you can't readily change that. So it will be simpler to let that view, the actual original touch view, do the processing of this touch.
I think Matt's solution is best so I am going to mark it as correct.
However my code structure wasn't going to allow me to cleanly implement it. Compounding the issue was the object listening was listening for a UILongGestureRecognizer.
So my solution was as follows:
Create a callback in my ViewController that would handle the longGestureOverride call
Add a callback to the object listening for the longGesture that would call the longGestureOverride callback and pass along the point
Manually move the object based on the point passed back
If the user lifts their finger, I disable the longGestureOverride callback, and begin using the UIPanGesture inside the new object

Stopping touch propagation in cocos2d

We are working on a game in cocos2d in which there is a possibility of getting a trivia question. The trivia question is implemented as a new, transparent CCLayer on top of the gameboard, which contains a CCMenu with all of the questions.
Our problem is that we can't seem to get the touches to stop propagating properly. When the trivia menu is up, players should not be able to click on the "roll dice" button on the board layer.
We have tried implementing this by calling dice.isTouchEnabled=NO; right before adding the trivia layer, but we can't figure out how to re-enable the dice button.
We also tried changing ccTouchBegan from NO to YES to always consume all of the touches, but then it stops responding to our menu. It seems that this should be the right way to do it, but why did the menu stop responding then?
Our professor suggested implementing a callback function, which we of course can do, but it seems like it should be easier than that.
Does anyone have any suggestions?
I understand that there are two ways to do this.
Method 1 (method that I am using)
Before trivia question pops up, use the function below to disable menus on the Underlying Scene node. The method is a recursive method so it disables all menus on the node's children too.
When the trivia question is dismissed, send an NSNotification which will be received by the Underlying Scene node and will re-enable menus on the node and its children. You can use the block method of NSNotification to shorten your code.
Docs on addObserverForName:object:queue:usingBlock:
(void) MenuStatus:(BOOL)_enable Node:(id)_node {
for (id result in ((CCNode *)_node).children) {
if ([result isKindOfClass:[CCMenu class]]) {
for (id result1 in ((CCMenu *)result).children) {
if ([result1 isKindOfClass:[CCMenuItem class]]) {
((CCMenuItem *)result1).isEnabled = _enable;
}
}
}
else
[self MenuStatus:_enable Node:result];
}
}
Method 2
Create an invisible layer that will swallow all touches below the Trivia Question layer. Here is a class you can try: https://gist.github.com/christophercotton/1563708

Presentation overlay for iOS

When I demo my touch apps to remote teams the people on the other end dont know where I am touching. To remedy this, I have been working on an event intercepting view/window that can display touches over applications. No matter how may variations on nextResponder I call, I am unable to react to the touch and pass it along to the controllers underneath. Specifically scroll views dont react nor do buttons.
Is there a way to take an event, get its position, then pass it along to what ever component would have been responding to it initially (the controller underneath)?
Update:
I am making some progress with a UIView. The new view is always returning NO to pointInside.This works great for when the touch starts, but it doesnt track moves or releases. Is there a strategy to adding gesture recognizers to the touch in order to track its event lifecycle?
Joe
You could try creating your own subclass of UIApplication that overrides sendEvent:. Your implementation should call [super sendEvent:event] as well as process the event as needed.
Update your main.m and pass the name of you custom UIApplication class as the 3rd parameter to the call of UIApplicationMain.
After some more due diligence, I found my oversight. In the layer that was on top and displaying the touches, user interaction needed to be set to false. Once I set that to false, I was able to use that layer for display while catching events on the layers below. The project still isn't done but I am one step closer.
Take care,
Joe

a last touch event does not come if the view goes out of the screen programmatically

I have a situation where I apply an effect to a UIView when a touch begins and reverse that effect when that touch ends. So basically I am tracking touchesbegan, touchesEnded and touchesCancelled methods of UIView.
But the problem is that when the view goes out of the screen, i.e. when it or one of its parents gets removed from superview, it does not get any more touch events. Is there any way to give this "last" touchesended event to the view? Maybe if the UIView gets notified about being invisible, I can also use this event for that purpose.
Ok I am going to move the answers in comments to original question to make a good summary of important points.
The reason I am tracking touch events is that I want to apply some
nice effects such as glowing on touch start and remove those effects
on touch ending.
The reason why I can not simulate touchesEnded on removing those
views is that I do not directly remove them. Instead I remove one of
the ancestor views of them. I can not keep track of ancestor views
all the way to UIWindow, it is technically impossible I think.
Instead, framework should provide this to as an event I think.
I solved my problem by overriding -(void)willMoveToWindow:(UIWindow *)newWindow method and checking if newWindow is nil.

Resources