SpriteKit: How to stop/reset rotation on physicsBody - objective-c

I have a sprite that bouncing around the screen using physics. I am trying to turn on and off the ability of said sprite to rotate.
In init of the scene I set the following:
ball.physicsBody.allowsRotation = NO;
Works just fine. Later, in another method I allow it with:
ball.physicsBody.allowsRotation = YES;
Things are working great. However, when I set it back to NO it just keeps on spinning. I checked the debugger and allowsRotation is indeed set to NO.
Any idea what I am missing?

From apples documentation:
The allowsRotation property determines whether forces can impart angular velocity on the body.
This means that when it is set to no, the ball will keep its spin, but not be affected further by forces.
Set .angularVelocity to 0, when you set the allowsRotation to 0.

Related

Re-Position MKMapView's userLocationView?

I'm making an app that has a series of trails in the forest. GPS is going to be off now and then by few dozen meters, so I'm trying to write some code that will "snap" the userLocationView to the trail (which is where the user likely is).
I've got some code working that scans the nearby trails, determines the probable location of the user, and gets the CLLocationCoordinate2D for it.
Now I need to apply that location to the userLocationView. I thought that this would work, but it doesn't seem to:
userLocationAnnotation.coordinate = myDerivedCoordinate;
I realize I could probably create my own annotation, but I would like to avoid that if possible.
MapView gets the user location directly from CLLocationManager, and there isn't a documented way to intercept this interaction. Doing so would be more work than adding your own annotation, and a cause for app rejection. Also, it doesn't make sense to redefine correct information provided by the system. That's why mapView.userLocation is a MKUserLocation with a readonly location property.
So you have to create your own, roughly:
Set mapView.showsUserLocation = false;
Create and start a location manager
self.locationManager = [CLLocationManager new]; self.locationManager.delegate = self; [self.locationManager startUpdatingLocation];
Add/replace your own blue dot annotation from locationManager:didUpdateToLocation:fromLocation: of the CLLocationManagerDelegate.
You can get the blue dot graphic (and probably the pulsating circles) with the UIKit-Artwork-Extractor. For the pulsating effect add to the MKAnnotationView a UIImageView with the different frames, or take one circle and use UIView animateWithDuration with a CGAffineTransformMakeScale for the view.transform property.

cocos2d getting a sprite to blink, it becomes invisible

im trying to make my sprite blink, but it just disappears, i have searched google, but i cant find a solution, heres what im doing:
CCBlink * blinker = [CCBlink actionWithDuration: 0.5 blinks: 1];
[player runAction: blinker];
this method is called when two of my sprites collide, when the collision takes place, i want the 'player' sprite to blink for a few seconds. at the moment, when the sprites collide, the 'player' sprite becomes invisible....thanks
CCBlink seems to work by toggling the visibility of your sprite on and off a given number of times within the stated duration you gave it. Depending on the duration you set, you might sometimes end up with an "off" visibility state at the end of the action (very buggy yeah, I had that too before), which isn't quite desired.
Two suggestions:
(1) Play around with the number of blinks.
(2) Always force the sprite to be visible at the end of the blink:
Add: [CCShow action] to the end of your blink action. You can string both actions into a CCSequence.
Verify that when (and where) you process 'onCollision' types of events you do not remove the sprite from its parent.
Blink action is buggy. I always use the following to guarantee that the object remains visible at the end of the animation:
Sequence* action = Sequence::create(Blink::create(BLINK_DURATION, BLINK_TIMES), Show::create(), NULL);

How to replace the custom animation for a CALayer appearing

I have a CALayer that I want to change the custom animation for it appearing on screen. I have created a delegate so that I can catch the method:
- (id < CAAction >)actionForLayer:(CALayer *)layer forKey:(NSString *)key
And I check for the key to be equal to kCAOnOrderIn, however, the layer hasn't been told what it's bounds will be yet (it currently reports (0, 0, 0, 0) for the bounds). So then I tried checking for the key to be equal to "bounds" but I still get reported the same rect (0, 0, 0, 0).
The animation I want to do is instead of the layer gradually "unfading" onto the screen via opacity, I want it to grow onto the screen from small and in the middle to it's full bounds. But to do that I need to know what it's full bounds will be. Is there anyway to know that so that I can replace the custom animation, or am I just simply approaching this the wrong way?
Thanks
I know this is 3 months old but I was looking for an answer for a similar problem and notice that this doesn't have an answer (an I'd like some rep ;)
I'm overwriting the actionForLayer:forKey: on a UIView (iPhone SDK) that is automatically set as the delegate to it's backing CALayer in order to inject a different action for anchorPoint. I notice I can get access to the old value via the presentationLayer of the CALayer and the new value is in the modelLayer. But what I think is happening in your case is that the system is calling for the onOrderIn action (perhaps at the time that the layer is added to the view hierarchy) before you have told the layer it's bounds. In fact this is normal.
You should be waiting for the "bounds" in addition to the "onOrderIn" key.
Are you waiting for other keys? "onOrderOut" or "hidden"?
Cheers,
Corin

MKMapView and setRegion:animated: not updating the map visuals

Greetings! I'm attempting to use MKMapView without any Apple code samples, though there are a few others out there of varying clarity. (I know, "Read the friendly manual." I've done that but it's not 100% clear, so please bear with me on this one.)
Here's the situation. I have a MKMapView object, wherein I have added a set of about ten MKPinAnnotation objects. So far, so good. Everything is alloced/released sanely and there doesn't appear to be any complaints from Instruments.
Upon initial display, I set up a MKCoordinateRegion object with the centerpoint at our first pin location, and a (arbitrary) span of 0.2 x 0.2. I then call:
[mapView setRegion:region animated:YES];
[mapView regionThatFits:region];
Wow! That worked well.
Meanwhile ... I also have a segmented control to allow for movement to each pin location. So as I tap through the list, the map animates to each new pin location with a new pair of calls to setRegion:animated: and regionThatFits: ... or at least that's the idea.
While the map does "travel" to the new pin location, the map itself doesn't update underneath. Instead, I see my pin on a gray/blank-map background ... until I nudge the map in any direction, however slightly. Then the map shows through! (If I'm only moving within a short distance of the previous pin location, I'll usually see whatever part of the map was already loaded.)
I suspect I'm doing something dumb here, but I haven't been able to figure out what, at least not from the MapKit docs. Perhaps I'm using the wrong calls? (Well, I do need to set the region at least once, yes? Moving that around doesn't seem to help though.) I have also tried using setCenterCoordinate:animated: - same problem.
I'm assuming nothing at this point (no pun intended). Just trying to find my way.
Clues welcome/appreciated!
UPDATE: Calling setRegion:animated: and regionThatFits: the first time, followed by setCenterCoordinate:animated: while traversing the list, has no effect. Interesting finding though: If I change animated to NO in both cases, the map updates!!! Only when it's set to YES. (Wha happen?! Is animated: broken? That can't be ... ???)
It turns out that the map update doesn't work when using the SIMULATOR. When I try setCenterCoordinate:animated: on the device, I do get the map update underneath.
Bottom line: I was trusting the simulator to match the device in terms of map updating behavior. Alas, I was mistaken! Lesson learned. "Don't let this happen to you." :)
You need to invoke the setRegion:animated: call in the Main thread context.
Just do something like:
....
[self performSelectorOnMainThread:#selector(updateMyMap) withObject:nil waitUntilDone:NO];
}
-(void) updateMyMap {
[myMap setRegion:myRegion animated:YES];
}
and it should work in any case (animated or not), with the map updated underneath.
Hum strange. The map updates on my Mac even in the simulator. Maybe a network setting (proxy or whatever) that would prevent the map widget to download the tiles on the simulator ?
Even though this is an old topic I thought I'd ring in with my experience. It seems the map animation only fails on devices running iOS 3.1.x and the simulator running 3.1.x. My dev iPod touch with 3.1.3 fails to zoom if animation is on.

Swapping UIImageView Images in a loop

I have a NSArray of UIImageViews that I want to loop over and quickly swap out an "on" and "off" state. I wrote the code to do so in a for loop instead a method that was called when the user tapped a UIButton ( the button's action ).
Here's that loop:
for(int i = 0; i < [Images count]; i++) {
if( i > 0 ){
[self toggleImageViewOff:[Images objectAtIndex:i - 1]];
}
[self toggleImageViewOn:[Images objectAtIndex:i]];
[NSThread sleepForTimeInterval:0.5f];
}
The UI did not update as I expected as I only ever saw the last UIImageView in the "on" state. I figured that the drawing update of the views must occur in the main thread this code was also executing in. So I learned about performSelectorInBackground:withObject: . Performing the toggleImageViewOn/Off methods using this made the loop work. The problem is if I make the sleep interval too short I can have an "on" update after an "off" with Threads operating out of order.
So I had the bright idea of moving the whole loop with the sleep into its own method and calling that from the action method using performSelectorInBackground:withObject: . I tried that and I'm back to not getting an updated view until the loop is over.
That's a long winded way to get to my question:
What's the best way to animate this to guarantee the on/off code fires in the right order and still get view updates, even at high speeds? ( i.e. looping very quickly )
I tried to think about how I'd do it with CoreAnimation, but I can't seem to get my head around how to do it there.
For bonus, here are the toggle methods:
- (void)toggleImageViewOn:(UIImageView *)theImageView {
[theImageView setImage:[UIImage imageNamed:#"on.png"]];
}
- (void)toggleImageViewOff:(UIImageView *)theImageView {
[theImageView setImage:[UIImage imageNamed:#"off.png"]];
}
Did you set up an animation context (UIView class method does that) around this for loop? Without it changes are immediate instead of animated.
The problem is that you are not giving any of the UIImages time to draw. The drawing code is optimised to only draw what's needed - rendering all those intermediate stages is optimised out.
Sleeping the main thread doesn't actually give it chance to run.
Bill is right in that you need to set up an animation context around your loop. This will capture all of the UIView changes you make and then play them out. The easiest way to do this is using Core Animation. Core animation 'records' changes in UIElemenets and plays them back. Your code (without the sleep) will work just fine in a Core Animation block.
Apple have a reasonable cookbook for Core Animation on their site
You're on the right track with moving the loop to a background thread, but you also need to make sure that you give the main run loop a chance to update the UI. You should be able to replace the direct calls to toggleImageViewOn: and toggleImageViewOff: with something like
[self performSelectorOnMainThread:#selector(toggleImageViewOn:) withObject:[Images objectAtIndex:i] waitUntilDone:NO];
This will do the UI update on the main thread, and by not waiting until the update is done you give the main run loop a chance to reach its end. You run into the same issue with things like progress bars, where they won't change until the loop ends unless you do your updates from a background thread with a UI update call like the one above.
Hey Patrick. Have a look at UIImageView's animationImages property, as well as the animationRepeatCount and animationDuration properties. If you put your on/off images into an array and assign that as the animationImages property, you should be able to control the repeat and duration to get the desired effect.
Hope that helps!
Thanks for that. I've already looked into UIIMageView's animationImages property. That's not exactly what I'm attempting to do. I'm cycling between several UIImageView's that are placed near each other to give the impression that a light is moving between them and cycling over them. So an individual UIImageView's animation is separate from each other as I need to swap the image as necessary in code.
Calling peformSelectorOnMainThread:withObject:waitUntilDone: from the loop on the background thread does indeed update the view as quickly as I can think I will ever need. I'm curious why I need to do the UIImageView swap on the Main thread? Why wouldn't changing it on the background thread and then using NSThread's sleepForTimeInterval allow the main thread to update the drawing anyway?
I guess I need to go read up on the run loop and where drawing updates occur.
Thanks so much for the help. ( I'm also going to try some additional suggestions from Bill Dudney, that I think will work based on CoreAnimation )