Iphone - creating animations like user location Blue marble drop

Do you know how to create animations like Blue-Marble drop User-Location in MKMapView?

+4
source share
2 answers

Although I'm not sure about the specifics of how Apple dealt with this effect, it seems to me that this is a great opportunity to use CoreAnimation and custom animation properties. This post contains some interesting information on this subject. I assume that in the "Blue Marble drop" animation, you are referring to the following sequence:

  • Big light blue circle scales in frame
  • The large light blue circle oscillates between two relatively large radii, as the location is calculated
  • A large light blue mug scales to a small dark blue circle in a custom location.

Although this may simplify the process a bit, I think this is a good place to start, and more sophisticated / detailed functionality can be added with relative ease (i.e. a small dark circle pulsates as a larger circle converges on it )

First of all, we need a custom subclass of CALayer with a custom property for our outer large radius of blue circles:

#import <QuartzCore/QuartzCore.h> @interface CustomLayer : CALayer @property (nonatomic, assign) CGFloat circleRadius; @end 

and implementation:

 #import "CustomLayer.h" @implementation CustomLayer @dynamic circleRadius; // Linked post tells us to let CA implement our accessors for us. // Whether this is necessary or not is unclear to me and one // commenter on the linked post claims success only when using // @synthesize for the animatable property. + (BOOL)needsDisplayForKey:(NSString*)key { // Let our layer know it has to redraw when circleRadius is changed if ([key isEqualToString:@"circleRadius"]) { return YES; } else { return [super needsDisplayForKey:key]; } } - (void)drawInContext:(CGContextRef)ctx { // This call is probably unnecessary as super implementation does nothing [super drawInContext:ctx]; CGRect rect = CGContextGetClipBoundingBox(ctx); // Fill the circle with a light blue CGContextSetRGBFillColor(ctx, 0, 0, 255, 0.1); // Stoke a dark blue border CGContextSetRGBStrokeColor(ctx, 0, 0, 255, 0.5); // Construct a CGMutablePath to draw the light blue circle CGMutablePathRef path = CGPathCreateMutable(); CGPathAddArc(path, NULL, rect.size.width / 2, rect.size.height / 2, self.circleRadius, 0, 2 * M_PI, NO); // Fill the circle CGContextAddPath(ctx, path); CGContextFillPath(ctx); // Stroke the circle border CGContextAddPath(ctx, path); CGContextStrokePath(ctx); // Release the path CGPathRelease(path); // Set a dark blue color for the small inner circle CGContextSetRGBFillColor(ctx, 0, 0, 255, 1.0f); // Draw the center dot CGContextBeginPath (ctx); CGContextAddArc(ctx, rect.size.width / 2, rect.size.height / 2, 5, 0, 2 * M_PI, NO); CGContextFillPath(ctx); CGContextStrokePath(ctx); } @end 

Thanks to this infrastructure, we can now easily animate the radius of the outer circle. b / c CoreAnimation will take care of value interpolations as well as redraw calls. All we need to do is add animation to the layer. As a simple proof of concept, I chose a simple CAKeyframeAnimation for going through a three-stage animation:

 // In some controller class... - (void)addLayerAndAnimate { CustomLayer *customLayer = [[CustomLayer alloc] init]; // Make layer big enough for the initial radius // EDIT: You may want to shrink the layer when it reacehes it final size [customLayer setFrame:CGRectMake(0, 0, 205, 205)]; [self.view.layer addSublayer:customLayer]; CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"circleRadius"]; // Zoom in, oscillate a couple times, zoom in further animation.values = [NSArray arrayWithObjects:[NSNumber numberWithFloat:100], [NSNumber numberWithFloat:45], [NSNumber numberWithFloat:50], [NSNumber numberWithFloat:45], [NSNumber numberWithFloat:50], [NSNumber numberWithFloat:45], [NSNumber numberWithFloat:20], nil]; // We want the radii to be 20 in the end customLayer.circleRadius = 20; // Rather arbitrary values. I thought the cubic pacing w/ a 2.5 second pacing // looked decent enough but you'd probably want to play with them to get a more // accurate imitation of the Maps app. You could also define a keyTimes array for // a more discrete control of the times per step. animation.duration = 2.5; animation.calculationMode = kCAAnimationCubicPaced; [customLayer addAnimation:animation forKey:nil]; } 

The above is a pretty “hacky" proof of concept, as I'm not sure how you intend to use this effect. For example, if you want to oscillate a circle until the data is ready, the above will not make much sense, because it will always oscillate twice.

Some closing notes:

  • Again, I am not sure of your intentions regarding this effect. If for example, you add it to MKMapView , this may require some settings for integration with MapKit.
  • A related article assumes that the above method requires a version of CoreAnimation in iOS 3.0+ and OS X 10.6 +
  • Speaking of a related post (as I often did), many thanks and thanks to Ole Begemann, who wrote it and did a great job explaining user properties in CoreAnimation.

EDIT: Also, for performance reasons, you probably want to make sure that the level does not exceed as much as necessary. That is, after you have done the animation of a larger size, you may need to scale the size down so that you use / draw as much space as necessary. A good way to do this would be to simply find a way to animate bounds (as opposed to circleRadius ) and perform this animation based on size interpolation, but I had some problems implementing it (maybe someone could add some insight on this) .

Hope this helps, Sam

+11
source

Add this to your map object:

 myMap.showsUserLocation = TRUE; 
-2
source

All Articles