multi touch..need help with cctouchesended - cocoa-touch

I've almost finished with my multi touch code for my game but I'm stuck with one more problem. This is done in Cocos2d btw :)
The player has a dpad on the left side and a button on the right, it works fine if i hold down on of the dpads and hold the jump button, so that he runs to the right and jumps.
The problem is once I lift my finger off the jump button all actions stop, so I have to lift and press my finger down again on the dpad...
this is my code for cctouchesended
-(void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
////when the user has stopped touching the screen set the variables
////back to false to stop its movement////
//self.myTouch = nil;
for (UITouch *touch in touches)
{
if (moveRight == TRUE) {
[player stopAction:RunForward];
}
if (moveLeft == TRUE) {
[player stopAction:RunBackwards];
}
moveLeft = FALSE;
moveRight = FALSE;
// jump = FALSE;
}
}

You would be better off mapping the logic to 2 buttons instead. Or at least for the jumping.
The problem with the current code is that you are not checking to see which finger you lift. You could ad a check so if the touch that ended is on the left side of the screen then stop moving.
Also you don't need to compare a BOOL value to TRUE. Just do, "if (moveRight)". It is much better

You will want to keep reference to the HASH (touch.hash) and save that CCTouchesBegan
then in the function you have now (the release) you check which hash has been released.
so:
in your ccTouchesBegan function you check which button is pressed:
if (CCRectContainsPoint(sprite1.boundingbox,touchlocation))
{
rightSideButtonHash = touch.hash;
)
in the release you check the touch.hash against the rightSideButtonHash, and you know if it was that touch!

Related

NSScrubber pan animation end notification

The touchbar-specific NSScrubber control scrolls with inertia on a pan gesture. I want to be notified of this animation's end to perform some function.
Try 1
The NSScrubberDelegate has a - didFinishInteractingWithScrubber: method which I implemented. However, soon after I stop manipulating the scrubber directly -- lifts the finger off the touchbar -- I get a callback, but the scroll continues to happen due to inertia. The final item that gets selected is NOT the one when this delegate method was called back.
Try 2
Digging further, I came across NSAnimation. Though it isn't documented clearly, I gather that scrubber is also a NSAnimatablePropertyContainer, as its selectedIndex property documentation says one can animate the selection through the animator proxy thus: scrubber.animator.selectedIndex = i. By that virtue, assuming that the animated property for the smooth panning is the boundsOrigin, I tried querying it.
I was able to get a CAAnimation by doing this
CAAnimation* a = [NSScrubber defaultAnimationForKey:#"boundsOrigin"];
// returns the same pointer value as above
// a = [myScrubber animationForKey:#"boundsOrigin"];
a.delegate = self;
...
- (void)animationDidStop:(CAAnimation *)anim
finished:(BOOL)flag {
if (flag == YES)
NSLog(#"Animation ended!\n");
}
I get a valid pointer value for a. However, I get numerous calls to animationDidStop with all of them having flag = YES; as the scrubber scrolls I keep getting these calls and when the scroll stops the calls stop. This feels closest to what I want but I dunno why so many calls come instead of just one when the animation ends.
Since NSScrubber's NSView or NSScrollView aren't exposed, I'm not sure if I'm querying the right object to get to the right NSAnimation.
Try 3
I also tried the hacky route of doing this on manipulation end code in vain
-(void)didFinishInteractingWithScrubber:(NSScrubber *)scrubber {
NSLog(#"Manipulation ended\n");
NSAnimationContext*c = NSAnimationContext.currentContext;
[c setCompletionHandler:^{
NSLog(#"Inertial scrolling stopped!\n");
}];
}
The completion handler is called almost immediately, before the inertial scroll stops :(
Ask
Is there anyway to know when the scrubber's pan gesture inertial animation ends?
I finally found a way to register a callback for the pan gesture's inertial scroll animation end.
Like any other scroll view, this also has the NSScrollViewDidEndLiveScrollNotification. Use the notification centre to register for the callback!
NSScrollView *sv = myScrubber.enclosingScrollView;
// register for NSScrollViewWillStartLiveScrollNotification if start is also needed
[[NSNotificationCenter defaultCenter] addObserverForName:NSScrollViewDidEndLiveScrollNotification
object:sv
queue:nil
usingBlock:^(NSNotification * _Nonnull note) {
NSLog(#"Scroll complete");
}];
Thanks to this answer for showing this approach.

Only first link is detected in uitextview

I have strange problem with url detection in textView. My code is like this:
_contactDescription.dataDetectorTypes = UIDataDetectorTypeLink | UIDataDetectorTypePhoneNumber;
[_contactDescription setUserInteractionEnabled:YES];
[_contactDescription setSelectable:YES];
[_contactDescription setEditable:NO];
_contactDescription.delegate = self;
_contactDescription.text = desc;
My string has a lot of phone numbers and emails to detect but using data detection only first phone and first email run action on tap. All phones and emails are highlighted but no action on tap.
Does anyone had similar problem ?
Thanks in advance.
If they are highlighted but you can't click them, but you can click the first one, that usually means that there is another view you didn't expect over lapping the text. You could try adding this in your view controller to see what is getting tapped.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch * touch in [touches allObjects])
{
NSLog(#"View: %#", touch.view);
}
}
Or run it in the simulator and in Debug turn on Color Blended Layers
Or try hiding all other views until it works to find the offender.
Another issue could bet the text is actually outside the view and you can't tell because it isn't clipping the extra. To verify that isn't the case.
[_contactDescription setClipsToBounds:YES];
Hopefully that is the case and one of those help find the offender. Good luck.

Cocos2d 'snap to grid' logic

I'm building a game in Cocos2d and I'm having a hard time implementing what I want. Imagine an 8 x 10 grid of squares. You can touch a square and drag that square's row / column horizontally / vertically, but not both. After you release the row / column, the squares will 'snap' back into place in the grid according to their closest row / col positions.
Everything works programmatically, with no overlaps or misplaced squares or invalid positions. However, I just cannot seem to get this 'snap to grid' to animate the way I want.
I'm using the MVC design pattern to separate any views from any game logic. The view is simply handed an array of 'squaresToUpdate', looks at every square, finds the corresponding sprite, and updates the position of the sprite based on the position of the square.
The problem arises when trying to do something along the lines of this:
-update():
for each square in squaresToUpdate:
if square is not being dragged at the moment:
setup a 'CCMoveTo' to bring the sprite in line with square
I can't get the squares to move freely while being dragged, but CCMoveTo when not being dragged. Either I create a new action every update, or the squares just freak out.
I don't know if it's my logic that is broken, if CCMoveTo is not doing what I want, or if this problem is actually much harder than I initially thought. Could someone help me out with this logic?
Good old pencil and paper gives me this, but I'm not sure it's 100%:
- (void)update:(ccTime)dt {
NSMutableArray *toDraw = [self.rootView whatAmIDrawing];
for (GameObject *o in toDraw) {
CCSprite *sprite = (CCSprite*)[self.batch getChildByTag:o.tag];
if (o.moving == NO) {
if (o.snapping == NO) {
o.snapping = YES;
CCMoveTo *move = [CCMoveTo actionWithDuration:self.rootView.model.activeGame.snapSpeed position:o.position];
[sprite runAction:move];
}
} else {
o.snapping = NO;
sprite.position = o.position;
sprite.rotation = o.rotation;
}
}
[super update:dt];
}
I would override the cocos2d touch methods:
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
ccTouchBegan would compare the touch point with every game object and set a "touched" flag:
ccTouchMoved would set the position of any "touched" object to the new touch position, and set a "moving" flag.
ccTouchEnded would clear all "touched" flags, run the CCMoveTo to the new position for any object with the "moving" flag, and then clear all "moving" flags.
The update() method would then just have to change position of every sprite to the position of the object.

Disable double tap zoom in MKMapView (iOS 6)

in ios 5 i was able to disable the double tap zoom by just overriding it with a new double tap gesture. But it seems that the double tap gesture is no longer in the gesturerecognizer array that comes with the mkmapview.
NSArray *gestureRecognizers = [_mapView gestureRecognizers];
for (UIGestureRecognizer *recognizer in gestureRecognizers) {
NSLog(#"%#", recognizer);
}
returns nothing in ios 6, where in ios 5 it would return 2 recognizers, one for single tap and one for double tap.
I'd look through the gesture recognizers of MKMapView's subviews. It's probably still there somewhere.
Of course, messing around with another view's GRs is slightly dubious and will likely break the next time Apple changes something about MKMapView...
EDIT: For the benefit of anyone else reading this, please check that it's a UITapGestureRecognizer and that numberOfTapsRequired == 2 and numberOfTouchesRequired == 1.
Also, instead of disabling double-taps on the map entirely, consider adding a double-tap GR on the annotation and then do [mapDoubleTapGR requireGestureRecognizerToFail:annotationDoubleTapGR]. Again, hacky — don't blame me if it breaks on the next OS update!
This worked for me:
[_mapView.subviews[0] addGestureRecognizer:MyDoubleTapOverrider];
Do you want to let the user do anything with the view? If not, it sufficient to set userInteractionEnabled to NO. If so, what specific interactions do you need to allow? Everything but double-tapping? Why disable that one interaction?
The more we know about your use case, the better the answers we can provide.
This works for me:
//INIT the MKMapView
-(id) init{
...
[self getGesturesRecursive:mapView];
...
}
And then let the recursive function loop through the subviews and find the GR:s.
-(void)getGesturesRecursive:(UIView*)v{
NSArray *gestureRecognizers = [v gestureRecognizers];
for (UIGestureRecognizer *recognizer in gestureRecognizers) {
if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]) {
[v removeGestureRecognizer:recognizer];
}
}
for (UIView *v1 in v.subviews){
[self getGesturesRecursive:v1];
}
}
This example removes all tap-GR:s. But I guess you can specify to remove whatever you'd like.
You can use a long tap gesture instead, that works.

How to control CAKeyframeAnimation with touch gesture?

I have a CAEmitterLayer animated along a bezier path (closed form, like an '8', out of four control points) with a CAKeyframeAnimation. Now I want to control the animation by a slide of a touch-finger along (but not necessarily on) the path. How is this possible and is this even possible?
Make a CGpoint click; variable to remember your initial "drag" point, then create a local NSEvent handler...
[NSEvent addLocalMonitorForEventsMatchingMask: NSMouseMovedMask
| NSLeftMouseDownMask
handler:^(NSEvent *e) {
if ( e.type == NSLeftMouseDown ) click = e.locationInWindow;
else "yourDelta" = click - e.locationInWindow; // pseudoCode
return e;
}];
"yourDelta" is the offset of that initial point from the current location... you could also achieve similar results with scroll events, by monitoring NSEventScrollWheelMask... and looking at the e.deltaX and e.deltaY values.
Edit: I'm not as familiar with event handling on iOS.. but the same technique could be applied with normal event handlers... ie.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)e {
click = [e locationInView:_yourView];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent*)e {
"yourDelta" = click - [e locationInView:_yourView]; // pseudoCode
}
As to "seeking" your animation.. One possible way is to simply [layer addAnimation:theNewAnimation], using your previous toValue's, but instead of basing the fromValue's of 0, or your model layer... use your layer.presentationLayer values instead? It is hard to say without seeing the full content of your CAKeyframeAnimation.