Replicate radiating circles like user location on MKMapView - objective-c

Is it possible to have the radiating circles like the user location annotation. So that custom annotations have radiating circles of other colors. If not is there a hack way to make it work?

Check this out. You may able to do what you need with that. With a combination of using Core Animation and subclassing MKCircleView or MKOverlayView.
http://yickhong-ios.blogspot.com/2012/04/animated-circle-on-mkmapview.html

It is possible to create a custom subclass of UIView that does this. A UIView with two sublayers, one for the center ball and one for the expanding rings. The ring layer and the ball layer can be created by subclassing CALayer and overriding drawInContext: so you can get any colors you want. Code to animate the rings so that they expand and fade out at the same time could use a CAAnimationGroup like this:
// expand the ring from the ball size to the ring's max size
CABasicAnimation *sizeAnim = [CABasicAnimation animationWithKeyPath:#"bounds"];
sizeAnim.fromValue = [NSValue valueWithCGRect:ballBounds];
sizeAnim.toValue = [NSValue valueWithCGRect:ringBoundsMax];
sizeAnim.duration = kRingExpansionTime;
// fade out the ring part way thru the animation
CABasicAnimation* alphaAnim = [CABasicAnimation animationWithKeyPath:#"opacity"];
alphaAnim.fromValue = [NSNumber numberWithFloat:1];
alphaAnim.toValue = [NSNumber numberWithFloat:0];
alphaAnim.beginTime = kRingExpansionTime * 0.7f; // start part way thru
alphaAnim.duration = kRingExpansionTime - alphaAnim.beginTime;
CAAnimationGroup* group = [CAAnimationGroup animation];
group.duration = kRingExpansionTime;
group.repeatCount = HUGE_VALF; // repeat forever
group.animations = [NSArray arrayWithObjects:sizeAnim, alphaAnim, nil];
[ringLayer addAnimation:group forKey:nil];

Related

How do I further animate an image that's on a bezier path

Maybe someone can point me in the right direction. I tried Google but dont really know how to define my question in searchable terms.
I am animating an image along a UIBezierPath like this:
UIBezierPath *trackPath = [UIBezierPath bezierPath];
[trackPath moveToPoint:P(8, 250)];
[trackPath addCurveToPoint:P(318, 250)
controlPoint1:P(156, 86)
controlPoint2:P(166, 412)];
[trackPath addCurveToPoint:P(652, 254)
controlPoint1:P(488, 86)
controlPoint2:P(512, 412)];
[trackPath addCurveToPoint:P(992, 254)
controlPoint1:P(818, 96)
controlPoint2:P(872, 412)];
CALayer *bee = [CALayer layer];
bee.bounds = CGRectMake(0, 0, 69, 71);
bee.position = P(160, 25);
bee.contents = (id)([UIImage imageNamed:#"bee3.png"].CGImage);
[self.view.layer addSublayer:bee];
What I want to do is animate the image itself as it moving along the path. I can animate the image when its b itself with like this:
NSArray *blueArray = [NSArray array];
blueArray = [[NSArray alloc] initWithObjects:
[UIImage imageNamed:#"blue1.png"],
[UIImage imageNamed:#"blue2.png"], nil];
blueFly.animationImages = blueArray;
blueFly.animationDuration = 0.20;
blueFly.animationRepeatCount = -1;
[blueFly startAnimating];
How can I combine the two, so to speak?
Additionally I know how to draw the path itself onto the screen like this:
CAShapeLayer *beeline = [CAShapeLayer layer];
beeline.path = trackPath.CGPath;
beeline.strokeColor = [UIColor whiteColor].CGColor;
beeline.fillColor = [UIColor clearColor].CGColor;
beeline.lineWidth = 2.0;
beeline.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:6], [NSNumber numberWithInt:2], nil];
[self.view.layer addSublayer:beeline];
But how can I draw the line only behind the image as its being animated along the path?
You can use a CAShapeLayer to display the path. A shape layer provides two properties strokeStart and strokeEnd that have values between 0 and 1 and determine the fraction of the path that is visible. By animating strokeEnd you can create the impression that the path is "drawn" on the screen. See http://oleb.net/blog/2010/12/animating-drawing-of-cgpath-with-cashapelayer for an example that animates a pen that draws a line.
In order to change the image of the bee you can simply add an animation that changes the contents of your bee layer. For example, to change the image of the pen in the CAShapeLayer project above, we can add the following code to the startAnimation method.
CAKeyframeAnimation *imageAnimation = [CAKeyframeAnimation animationWithKeyPath:#"contents"];
imageAnimation.duration = 1.0;
imageAnimation.repeatCount = HUGE_VALF;
imageAnimation.calculationMode = kCAAnimationDiscrete;
imageAnimation.values = [NSArray arrayWithObjects:
(id)[UIImage imageNamed:#"noun_project_347_1.png"].CGImage,
(id)[UIImage imageNamed:#"noun_project_347_2.png"].CGImage,
nil];
[self.penLayer addAnimation:imageAnimation forKey:#"contents"];
This simply adds a key animation to the contents property of the layer that displays the pen. Note that the image "noun_project_347_1.png" is not part of the project so you would have to add an image to see the effect. I have simply horizontally flipped the image that comes with the project. By setting calculationMode to kCAAnimationDiscrete the animation does not interpolate between two images by replaces one by the other.
As long as you can get the exact path that you want to animate the image align you can do it using Core Animation and CAKeyframeAnimation.
Refer how-to-animate-one-image-over-the-unusual-curve-path-in-my-ipad-application) link.

changing image during translation

How can I change myLayer.contents with a set of images (i.e. switching between img1 and img2) during the translation? thanks!
UIImage *myImage = [UIImage imageNamed:#"img1.png"];
CALayer *myLayer = [CALayer layer];
myLayer.contents = (id)myImage.CGImage;
myLayer.Position = CGPointMake(0,0);
myLayer.Bounds=CGRectMake(0.0, 0.0, 50, 50);
[self.view.layer addSublayer:myLayer];
//translation1
CGPoint startPt = CGPointMake(10,10);
CGPoint endPt = CGPointMake(100,100);
CABasicAnimation *transl1 = [CABasicAnimation animationWithKeyPath:#"position"];
transl1.removedOnCompletion = FALSE;
transl1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
transl1.fromValue = [NSValue valueWithCGPoint:startPt];
transl1.toValue = [NSValue valueWithCGPoint:endPt];
transl1.duration = 3;
transl1.fillMode = kCAFillModeForwards;
transl1.beginTime = 0;
[myLayer addAnimation: transl1 forKey: nil];
The walking man example:
I have dealt with exactly the same task, but I had to do a running spider which is 6 leg walk and has 12 frames.
It was actually very difficult to do and took me few months to perfect.
The thing is that the the waking man example is usually done by setting an array of image frames( First leg, Last Leg) to an animationImages property of UIImageView.
Then you turn the animation on and off together with moving character right and left and by this creating an illusion of walking.
Now the big problem arises when you want create an illusion of acceleration. There is no way to change the animation duration DURING the animation playback and this is a major setback that is very hard to overcome.
Here is the code that I produced to overcome this issue:
Here you define an array with the walking leg frames, frame per step.
animationImagesSpider = [NSArray arrayWithObjects:
[UIImage imageNamed:#"1#2x.png"], [UIImage imageNamed:#"2#2x.png"], [UIImage imageNamed:#"3#2x.png"], [UIImage imageNamed:#"4#2x.png"], [UIImage imageNamed:#"5#2x.png"], [UIImage imageNamed:#"6#2x.png"], [UIImage imageNamed:#"6#2x.png"], [UIImage imageNamed:#"8#2x.png"], [UIImage imageNamed:#"9#2x.png"], [UIImage imageNamed:#"10#2x.png"], [UIImage imageNamed:#"11#2x.png"], [UIImage imageNamed:#"1#2x.png"], nil];
Here you attach the array to the UIImageView:
imgViewSpider = [[UIImageView alloc] initWithFrame:CGRectMake(200,410,100,145)];
imgViewSpider.animationImages = animationImagesSpider;
Now if you simply call [imgViewSpider startAnimating]; this will start the animation at a constant speed until you stop it. To overcome this I have used a recursion that plays a short animation for each step and this allows to adjusts the duration between each steps:
- (void) spiderRun {
imgViewSpider.animationDuration= 0.51-(accSp/3.5);
[imgViewSpider setAnimationRepeatCount:222]; /// this is a dummy value that has no effect because animtion ends after the first frame
[imgViewSpider startAnimating];
[self performSelector:#selector(spiderRun) withObject:nil afterDelay: 0.5-(accSp/3.5)];
}
By constantly changing the accSp value, I can control the walk speed during the walk.
As the other poster said, create a second animation that animates the contents property of the layer to a new image.
I would suggest putting both animations into an animation group, and adding the animation group to the layer. Then you can easily control both the duration and start time of both animations. You could make the image swap take place over part of the total duration, timed to take place in the middle of the animation.
You'd set the start of the animation using the beginTime property, and the duration with the duration property.
You will need to create a second animation transl2, this animation will make the image swap. You will need to set the duration to a half of transl1 duration so that it will fall in the middle of the transl1 animation.
transl1.duration = 3
transl2.duration = 1.5

Animating a gaussian blur using core animation?

I'm trying to animate something where it's initially blurry then it comes into focus. I guess it works OK, but when the animation is done it's still a little blurry. Am I doing this wrong?
CABasicAnimation* blurAnimation = [CABasicAnimation animation];
CIFilter *blurFilter = [CIFilter filterWithName:#"CIGaussianBlur"];
[blurFilter setDefaults];
[blurFilter setValue:[NSNumber numberWithFloat:0.0] forKey:#"inputRadius"];
[blurFilter setName:#"blur"];
[[self layer] setFilters:[NSArray arrayWithObject:blurFilter]];
blurAnimation.keyPath = #"filters.blur.inputRadius";
blurAnimation.fromValue = [NSNumber numberWithFloat:10.0f];
blurAnimation.toValue = [NSNumber numberWithFloat:1.0];
blurAnimation.duration = 1.2;
[self.layer addAnimation:blurAnimation forKey:#"blurAnimation"];
Your problem is that the animation stops and is automatically removed, but the filter lingers with the tiniest of blur applied.
What you want to do is to remove the blur filter when the animation completes. You need to add a delegate to the CABasicAnimation instance and implement the -[id<CAAnimationDelegate> animationDidStop:finished:] method.
If you let self be the delegate in this case it should be fairly simple, add this line before adding the animation to your layer:
blurAnimation.delegate = self;
And the callback is equally simple:
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
[[self layer] setFilters:nil];
}
If you're looking for an optimized way to animate a blur then I recommend creating a single blurred image of your view and then fading the blurred image from alpha 0 to 1 over the top of your original view. Seems nice and fast in tests.

ipad touch animation

Im having trouble animating a wheel using touch. Ive spent some time punching in different numbers values for duration, spin times and animation durations to get a smooth move using on thouchsMove, but every time a touch happens the wheel rotates and what seems to be happening is it jumps back to its original position. If any one can shed some light on this i would very much appreciate it.
CABasicAnimation* rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.fillMode = kCAFillModeForwards;
rotationAnimation.removedOnCompletion = NO;
rotationAnimation.delegate = self;
rotationAnimation.toValue = [NSNumber numberWithFloat: 2 * 1 * 45 ];
rotationAnimation.duration = 2;
rotationAnimation.cumulative = YES;
rotationAnimation.repeatCount = 1;
rotationAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
[animatedImage.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
for those interested code below is whats needed to get the desired effect
CGAffineTransform transforms = CGAffineTransformConcat(animatedImage.transform,CGAffineTransformMakeRotation(M_PI/2));
animatedImage.transform = transforms;
If you are having the same problem follow this example, its spot on.
http://ericmcconkie.com/2010/03/trig-and-objective-c/

Animate CALayer hide

I'm trying to hide a CALayer after a few microseconds and I'm using CABasicAnimation to animate the hide.
At the moment I'm trying to use
[aLayer setHidden:YES];
CABasicAnimation * hideAnimation = [CABasicAnimation animationWithKeyPath:#"hidden"];
[hideAnimation setDuration:aDuration];
[hideAnimation setFromValue:[NSNumber numberWithBool:NO]];
[hideAnimation setToValue:[NSNumber numberWithBool:YES]];
[hideAnimation setBeginTime:0.09];
[hideAnimation setRemovedOnCompletion:NO];
[hideAnimation setDelegate:self];
[alayer addAnimation:hideAnimation forKey:#"hide"];
But when I run this, the layer is hidden immediately, rather than waiting for the desired beginTime.
I'm uncertain about my keyPath as "hidden" but couldn't find any other option and the documentation does state that the hidden property of a CALayer is animatable.
What's the correct way to achieve what I'm looking for?
Try animating the opacity property instead. Go from 1.0 to 0.0 and you should get the effect you want.
From CAMediaTiming.h, it says about beginTime property:
The begin time of the object, in
relation to its parent object, if
applicable. Defaults to 0.
You should use CACurrentMediaTime() + desired time offset.
[hideAnimation setBeginTime:CACurrentMediaTime() + 0.09];
I'm sure this is too late to do the original poster any good, but it may help others. I've been trying to do something similar, except to make the animation implicit when the hidden property is changed. As Tom says, animating opacity doesn't work in that case, as the change to the layer's hidden property seems to take effect right away (even if I delay the animation with beginTime).
The standard implicit action uses a fade transition (CATransition, type = kCATransitionFade), but this operates on the whole layer and I want to perform another animation at the same time, which is not a compatible operation.
After much experimentation, I finally noticed #Kevin's comment above and --- hello! --- that actually works! So I just wanted to call it out so the solution is more visible to future searchers:
CAKeyframeAnimation* hiddenAnim = [CAKeyframeAnimation animationWithKeyPath:#"hidden"];
hiddenAnim.values = #[#(NO),#(YES)];
hiddenAnim.keyTimes = #[#0.0, #1.0];
hiddenAnim.calculationMode = kCAAnimationDiscrete;
hiddenAnim.duration = duration;
This delays the hiding until the end of the duration. Combine it with other property animations in a group to have their effects seen before the layer disappears. (You can combine this with an opacity animation to have the layer fade out, while performing another animation.)
Thank you, Kevin!
swift 4
let keyframeAnimation = CAKeyframeAnimation(keyPath: "hidden")
keyframeAnimation.calculationMode = kCAAnimationDiscrete
keyframeAnimation.repeatCount = 1.0
keyframeAnimation.values = [true, false,true,false,true]
keyframeAnimation.keyTimes = [0.0, 0.25,0.5,0.75, 1.0]
keyframeAnimation.duration = 30.0 //duration of the video in my case
keyframeAnimation.beginTime = 0.1
keyframeAnimation.isRemovedOnCompletion = false
keyframeAnimation.fillMode = kCAFillModeBoth
textLayer.add(keyframeAnimation, forKey: "hidden")
CABasicAnimation *endAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
endAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[endAnimation setFromValue:[NSNumber numberWithFloat:1]];
[endAnimation setToValue:[NSNumber numberWithFloat:0.0]];
[endAnimation setBeginTime:AVCoreAnimationBeginTimeAtZero];
endAnimation.duration = 5;
endAnimation.removedOnCompletion = NO;
[alayer addAnimation:endAnimation forKey:nil];