ipad touch animation - objective-c

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/

Related

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

Replicate radiating circles like user location on MKMapView

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];

CABasicAnimation stopping before it started?

I need to fade in my CALayers. Here's my code:
for(int i = 0; i < viewArray.count; i++)
{
CABasicAnimation *fadeIn = [CABasicAnimation animationWithKeyPath:#"opacity"];
fadeIn.duration = 0.3;
fadeIn.beginTime = i;
fadeIn.fromValue = [NSNumber numberWithFloat:0.0];
fadeIn.toValue = [NSNumber numberWithFloat:0.8];
fadeIn.removedOnCompletion = NO;
fadeIn.delegate = self;
[((CALayer*)[viewArray objectAtIndex:i]) addAnimation:fadeIn forKey:nil];
}
However, only the first two objects properly fades, all other objects do not fade at all. I've noticed that when I put a breakpoint at animationDidStop, most of the animations that have not started already stopped (even the animations that are supposed to start 60 seconds in are stopped within the first couple of seconds). I'm not exactly sure what's going on. I can see it properly when I manually set each CALayer's opacity to 1 but not when animating.
You are starting all of the animations at the same time, because the call to addAnimation:forKey: does not synchronously wait until the animation is complete.
I have checked it and it seems you have to set the beginTime in a following way
fadeIn.beginTime = CACurrentMediaTime() + i;

CALayer Animation

I'm a beginner and I trying continue to familiarize myself with CALayer ...
Thanks again #Alexsander Akers, #DHamrick and #Tommy because now I can skew my CALayer !
It look like :
I would like to move my finger on the Gray UIView (with touchesBegan/touchesMoved/touchesEnded) and my "cards" move like this :
(For exemple if I move my finger left)
the yellow card disappear & the green one take is place
white take the green's place
red the white
blue the red one
and instead of a blue card a Black one appear ...
Maybe I'm dreaming and it's to hard for me, but if if you can give me advice I'll take it !!
thank you !
You may use the customized function like this
- (void)moveAndRotateLayer: (CALayer *)layer withDuration:(double)duration degreeX:(double)degreeX y:(int)degreeY angle:(float)angle autoreverseEnable:(BOOL)ar repeatTime:(int)repeat andTimingFunctionType:(NSString *)type
{
// You should type the timing function type as "Liner", "EaseIn", "EaseOut" or "EaseInOut".
// The default option is the liner timing function.
// About these functions, look in the Apple's reference documents.
CAAnimationGroup *theGroup = [CAAnimationGroup animation];
CGPoint movement = CGPointMake(layer.position.x + degreeX, layer.position.y + degreeY);
NSArray *animations = [NSArray arrayWithObjects:[self moveLayers:layer to:movement duration:duration], [self rotateLayers:layer to:angle duration:duration], nil];
theGroup.duration = duration;
theGroup.repeatCount = repeat;
theGroup.autoreverses = ar;
if ([type isEqualToString:#"EaseIn"])
{
theGroup.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseIn];
}
else if ([type isEqualToString:#"EaseOut"])
{
theGroup.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut];
}
else if ([type isEqualToString:#"EaseInOut"])
{
theGroup.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut];
}
else
{
theGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
}
theGroup.animations = [NSArray arrayWithArray:animations];
[layer addAnimation:theGroup forKey:#"movingAndRotating"];
}
This is the solution of the animation just for moving and rotating the layer,
but, I know that you can customizing yourself.
It's very good for you. You can make it.
Good luck!

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];