How can I have one animation continue forever after calling a second animation? For example:
1) start an object pulsating
2) Move it while its pulsating
3) it continues pulsating
Everything works except the second animation is stopping the first one indefinitely.
Below is some sample code:
//Pulsate **
[UIView animateWithDuration:0.25
delay:0
options: (UIViewAnimationCurveEaseOut | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionRepeat)
animations:^{
CGAffineTransform currentTransform = self.transform;
CGAffineTransform newTransform1 = CGAffineTransformScale(currentTransform, .95, .95);
[self setTransform:newTransform1];
CGAffineTransform newTransform2 = CGAffineTransformScale(currentTransform, 1, 1);
[self setTransform:newTransform2];
}
completion:nil];
//Move **
[UIView animateWithDuration:0.30
delay:0
options: (UIViewAnimationCurveEaseOut | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState )
animations:^{
[[(UIPinchGestureRecognizer*)sender view] setCenter:CGPointMake(myAppDelegate.MCViewReference.center.x-300, myAppDelegate.MCViewReference.center.y)];
}
completion:^(BOOL finished){
}];
You will not be able to do this with the block-based animations as you have them here. You will need to split your animations up using explicit animations with CABasicAnimation. Create one animation for the pulsating effect and set it to repeat indefinitely. Then you can move it around by setting the center (either animated or non-animated).
CABasicAnimation *pulsation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
pulsation.fromValue = [NSNumber numberWithFloat:0.95f];
pulsation.toValue = [NSNumber numberWithFloat:1.f];
pulsation.duration = 0.25f;
pulsation.autoreverses = YES;
pulsation.repeatCount = INFINITY;
[self.layer addAnimation:pulsation forKey:#"pulse"];
As soon as you add the animation to the layer, it will begin animating. To remove the animation, simply call [self.layer removeAnimationForKey:#"pulse" or removeAllAnimations:.
You could separate your animations into phases and use the completion block to start next phase.
Related
I need a view to enter the screen with oscillating animation and finally, the animation should stop in a natural way (decreasing oscillations - pendulum effect). I have added the subview above the screen so that the view rotates into the screen when required. The code for adding the subview is:
myView.layer.anchorPoint = CGPointMake(1.0, 0.0);
[[self view] addSubview:myView];
[myView setHidden:YES];
// Rotate 75 degrees to hide it off screen
CGAffineTransform rotationTransform = CGAffineTransformIdentity;
rotationTransform = CGAffineTransformRotate(rotationTransform, DEGREES_RADIANS(75));
bannerView.transform = rotationTransform;
bannerView.center = CGPointMake(((self.view.bounds.size.width)/2.0), -5.0);
[self performSelector:#selector(animateSwing) withObject:nil afterDelay:3.0];
The way I'm trying to achieve this that the view should rotate one full semi circle & back rotation, then rotate one semi circle rotation and finally come to halt at desired point using EaseOut animation curve. The code for my animateSwing() method is given below:
- (void)animateSwing {
NSLog(#"ANIMATING");
[myView setHidden:NO];
CGAffineTransform swingTransform = CGAffineTransformIdentity;
swingTransform = CGAffineTransformRotate(swingTransform, DEGREES_RADIANS(-20));
[UIView animateWithDuration:0.30
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
[UIView setAnimationRepeatCount:1.5];
[UIView setAnimationRepeatAutoreverses:YES];
myView.transform = swingTransform;
}completion:^(BOOL finished){
[UIView animateWithDuration:0.10
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
myView.transform = CGAffineTransformMakeRotation(DEGREES_RADIANS(0));
}completion:^(BOOL Finished){
}];
}];
}
For some reason the above code isn't working. If I do not chain animations, the code performs the semi-circle routine. But if I chain animations like above, it just oscillates a little bit around the desired point and ends abruptly.
Please suggest a fix to this code OR suggest a way to implement the required animation
Thanks
You want to use a keyframe animation. I actually have an example of a "decreasing waggle" animation in my book (http://www.apeth.com/iOSBook/ch17.html#_keyframe_animation):
CompassLayer* c = (CompassLayer*)self.compass.layer;
NSMutableArray* values = [NSMutableArray array];
[values addObject: #0.0f];
int direction = 1;
for (int i = 20; i < 60; i += 5, direction *= -1) { // alternate directions
[values addObject: #(direction*M_PI/(float)i)];
}
[values addObject: #0.0f];
CAKeyframeAnimation* anim =
[CAKeyframeAnimation animationWithKeyPath:#"transform"];
anim.values = values;
anim.additive = YES;
anim.valueFunction =
[CAValueFunction functionWithName: kCAValueFunctionRotateZ];
[c.arrow addAnimation:anim forKey:nil];
Of course that isn't identical to what you're trying to do, but it should get you started.
I try to implement the animation:
when you enter iPhone Gallery, press the image, you see full-screen image. Below you can see toolbar with trash button. When you press this button, the image is being deleted with animation.
I try to implement this, but I don't know, how to implement the transform of image, apple use.
This is the best, I could do:
[UIView transitionWithView:self.view duration:0.1 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
[self.view addSubview:scrollImageView];
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
CGRect frame = scrollImageView.frame;
frame.size = CGSizeMake(frame.size.width * 0.75, frame.size.height * 0.75);
frame.origin = CGPointMake((size.width - frame.size.width) / 2, (size.height - frame.size.height) / 2);
scrollImageView.frame = frame;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
CGRect frame = scrollImageView.frame;
frame.size = CGSizeMake(frame.size.width * 0.05, frame.size.height * 0.05);
frame.origin = CGPointMake(size.width, size.height);
scrollImageView.frame = frame;
CGAffineTransform transform = scrollImageView.transform;
CGAffineTransform rotatedTransform = CGAffineTransformRotate(transform, 45 * 3.14 / 180);
scrollImageView.transform = rotatedTransform;
} completion:^(BOOL finished) {
[scrollImageView removeFromSuperview];
}];
}];
}];
Thank you in advance.
Update
As I understand, I can't do this animation with Core-Animation, but may anyone can advice me the animation the most simular to iPhone Gallery animation, but without using OpenGL?
You can use following example for this animation:
UIView *senderView = (UIView*)sender;
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"transform"];
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
anim.duration = 0.125;
anim.repeatCount = 1;
anim.autoreverses = YES;
anim.removedOnCompletion = YES;
anim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.2, 1.2, 1.0)];
//[senderView.layer addAnimation:anim forKey:nil];
UIBezierPath *movePath = [UIBezierPath bezierPath];
[movePath moveToPoint:icon.center];
[movePath addQuadCurveToPoint:senderView.center
controlPoint:CGPointMake(senderView.center.x, icon.center.y)];
CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
moveAnim.path = movePath.CGPath;
moveAnim.removedOnCompletion = YES;
CABasicAnimation *scaleAnim = [CABasicAnimation animationWithKeyPath:#"transform"];
scaleAnim.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
scaleAnim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.1, 0.1, 1.0)];
scaleAnim.removedOnCompletion = YES;
CABasicAnimation *opacityAnim = [CABasicAnimation animationWithKeyPath:#"alpha"];
opacityAnim.fromValue = [NSNumber numberWithFloat:1.0];
opacityAnim.toValue = [NSNumber numberWithFloat:0.1];
opacityAnim.removedOnCompletion = YES;
CAAnimationGroup *animGroup = [CAAnimationGroup animation];
animGroup.animations = [NSArray arrayWithObjects:moveAnim, scaleAnim, opacityAnim, nil];
animGroup.duration = 0.5;
[icon.layer addAnimation:animGroup forKey:nil];
I have modified the code, you have to perform following changes in it, set the sender view as self.view, and change the ending point of animation (which is currently senderView.center) according to your requirement
I know its a bit late. But, you should check Ciechan's solution named BCGenieEffect. Can be found here. Its pure Core Animation and very easy to understand. I think that's what you are looking for.
Good luck
At this point the exact animation you are talking about cannot be done using Core Animation or UIKit. You would need to use OpenGL and apply the image as a texture and do your animation in there.
I think you are talking about the "suck" transition animation.
It is possible to trigger it, but it is a private transition, and Apple may reject your app if you use it.
This code should do it, using a transition code of 103:
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition: transtionIndex forView:containerView cache:NO];
[containerView addSubview: newView];
[oldView removeFromSuperview];
[UIView commitAnimations];
Or search the net for a Core Image transition called "suckEffect"
I've searched loads of SO stuff and in Apple's references, but still unable to manage my problem.
What I have:
A screen with 2 UIImageViews and 2 UIButtons connected to them
2 kinds of animation:
Scaling up and then down of each image, one after another, only once in viewDidLoad
When a button pressed (a custom button hidden 'inside' of each UIImageView) it triggers animation of appropriate UIImageView–only one, not both–(also scale up, then down).
As I am writing for iOS4+ I'm told to use block based animations!
What I need:
How do I cancel a running animation? I've managed to cancel after all but the last one... :/
Here is my code snippet:
[UIImageView animateWithDuration:2.0
delay:0.1
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
isAnimating = YES;
self.bigLetter.transform = CGAffineTransformScale(self.bigLetter.transform, 2.0, 2.0);
} completion:^(BOOL finished){
if(! finished) return;
[UIImageView animateWithDuration:2.0
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
self.bigLetter.transform = CGAffineTransformScale(self.bigLetter.transform, 0.5, 0.5);
} completion:^(BOOL finished){
if(! finished) return;
[UIImageView animateWithDuration:2.0
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
self.smallLetter.transform = CGAffineTransformScale(self.smallLetter.transform, 2.0, 2.0);
} completion:^(BOOL finished){
if(! finished) return;
[UIImageView animateWithDuration:2.0
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
self.smallLetter.transform = CGAffineTransformScale(self.smallLetter.transform, 0.5, 0.5);
}
completion:^(BOOL finished){
if (!finished) return;
//block letter buttons
[self.bigLetterButton setUserInteractionEnabled:YES];
[self.smallLetterButton setUserInteractionEnabled:YES];
//NSLog(#"vieDidLoad animations finished");
}];
}];
}];
}];
Somehow the smallLetter UIImageView is not working properly, because when pressed (through button) bigLetter is canceling animations properly...
EDIT:
I've used this solution, but still having problem with scaling down smallLetter UIImageView - not cancelling at all...
solution
EDIT2: I've added this at the beginning of next/prev methods:
- (void)stopAnimation:(UIImageView*)source {
[UIView animateWithDuration:0.01
delay:0.0
options:(UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction)
animations:^ {
source.transform = CGAffineTransformIdentity;
}
completion:NULL
];
}
problem stays... :/ no idea how to interrupt last animation for letters in animation chain
You can stop all animations on a view by calling:
[view.layer removeAllAnimations];
(You'll need to import the QuartzCore framework to call methods on view.layer).
If you want to stop a specific animation, not all animations, your best best bet is to use CAAnimations explicitly rather than the UIView animation helper methods, then you will have more granular control and can stop animations explicitly by name.
The Apple Core Animation documentation can be found here:
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreAnimation_guide/CreatingBasicAnimations/CreatingBasicAnimations.html
For iOS 10 use UIViewPropertyAnimator to animate.
It provides methods to start, stop and pause UIView animations.
let animator = UIViewPropertyAnimator(duration: 2.0, curve: .easeOut){
self.view.alpha = 0.0
}
// Call this to start animation.
animator.startAnimation()
// Call this to stop animation.
animator.stopAnimation(true)
I'd add to Nick's answer that to make removeAllAnimations smooth next idea be very handy.
[view.layer removeAllAnimations];
[UIView transitionWithView:self.redView
duration:1.0f options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
[view.layer displayIfNeeded];
} completion:nil];
You can try this (in Swift):
UIView.setAnimationsEnabled(false)
UIView.setAnimationsEnabled(true)
Note: you can put code between those two calls if necessary, for example:
UIView.setAnimationsEnabled(false)
aview.layer.removeAllAnimations() // remove layer based animations e.g. aview.layer.opacity
UIView.setAnimationsEnabled(true)
I wanted to rotate a UIView on its horizontal axis for 360 degrees and then refresh the content in the view. I was looking out for solutions on this. Found a couple here n there. This is what I came out with.
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionTransitionNone animations:^{
self.tableView.layer.transform = CATransform3DMakeRotation(M_PI,1.0,0.0,0.0);
} completion:^(BOOL finished){
NSLog(#"Finished first pi");
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionTransitionNone animations:^{
self.tableView.layer.transform = CATransform3DMakeRotation(M_PI,1.0,0.0,0.0);
} completion:^(BOOL finished) {
NSLog(#"Finished second pi");
}];
}];
This flips the view but only by 180 degrees. I want it to flip one more time so that I can see the view normally..
Both my NSLogs are displayed one after the other. I am sure I am missing something here. ANy help would be helpful..
Thanks
In your completion block try concatenating the new transform with the current transform.
self.tableView.layer.transform = CATransform3DConcat(self.tableView.layer.transform, CATransform3DMakeRotation(M_PI,1.0,0.0,0.0));
You should check this answer How to make a CATransform3dMakeRotation rotate the other way? And chain together
I think that your problem is related with: "When you are working with a transform directly, Core Animation will interpolate the transform from the current value to the specified transform. It will find the shortest path to get to that transform, which will restrict the animation direction. If you try to animate the same transform property twice, the second value will simply override the first, not combine the two transforms together."
use this:-
rotatingView.layer.anchorPoint = CGPointMake(0.0f, 0.0f);
rotatingView.center = CGPointMake(0,
(rotatingView.center.y -
(rotatingView.bounds.size.height/2.0f)));
rotatingView.center = CGPointMake(0,
(rotatingView.center.y-
(rotatingView.bounds.size.height/2)));
// start the Page Open
[UIView beginAnimations:#"Animation" context:nil];
[UIView setAnimationDuration:13.0];
// set angle as per requirement
[rotatingView.layer setValue:[NSNumber numberWithInt:280]
forKeyPath:#"transform.rotation.x"];
[UIView commitAnimations];
You just have to change your code from 1.0 to 0.0 inside completion block and woohoo all done.
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionTransitionNone animations:^{
self.tableView.layer.transform = CATransform3DMakeRotation(M_PI,1.0,0.0,0.0);
} completion:^(BOOL finished){
NSLog(#"Finished first pi");
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionTransitionNone animations:^{
self.tableView.layer.transform = CATransform3DMakeRotation(M_PI,0.0,0.0,0.0);
} completion:^(BOOL finished) {
NSLog(#"Finished second pi");
}];
}];
You can also approach this using the .Repeat and .Autoreverse animation options + setting animation's repeat count. Here's an example in Swift:
UIView.animateWithDuration(time, delay: 0, options: [.Repeat, .Autoreverse], animations: {
UIView.setAnimationRepeatCount(3)
self.view.layer.transform = CATransform3DMakeRotation(CGFloat(M_PI), 1, 0, 0)
}) { (completed) in
// completion
}
I want to rotate a UIButton continuously as a means of indicating that an app is recording something. Is this a good idea. I also want to be able to stop this continuous rotation.
How would I use animation to do this?
Use the UIView animation method:
animateWithDuration:delay:options:animations:completion:
and use the option UIViewAnimationOptionRepeat
For example for a UIButton * outlet named button:
- (void)viewDidLoad
{
[super viewDidLoad];
[UIView animateWithDuration:2.0 delay:0.0 options:UIViewAnimationOptionRepeat | UIViewAnimationOptionCurveLinear animations:^{
CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI);
self.button.transform = transform;
} completion:NULL];
}
Since it's going round and round, I just rotate it by pi radians rather than 2pi. you could use any angle you want.
Edit
To stop the animation, just create another animation of a short duration beginning from the current state, e.g.
- (void)stopRotating {
[UIView animateWithDuration:0.1 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveLinear animations:^{
CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI * 0.05);
self.button.transform = transform;
} completion:NULL];
}
This provides a very short animation which overrides the current animation. Note that the angle of the transform is multiplied by 0.05 which is the ratio of 0.1/2.0 which means that the rotational speed for this little segment is the same as the continuous rotational speed.
Try below code that will help you because I run that code successfully.
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
[button setTitle:#"Show View" forState:UIControlStateNormal];
button.frame = CGRectMake(80.0, 210.0, 160.0, 40.0);
[self.view addSubview:button];
CABasicAnimation *halfTurn;
halfTurn = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
halfTurn.fromValue = [NSNumber numberWithFloat:0];
halfTurn.toValue = [NSNumber numberWithFloat:((360*M_PI)/180)];
halfTurn.duration = 0.5;
halfTurn.repeatCount = HUGE_VALF;
[[button layer] addAnimation:halfTurn forKey:#"180"];
I had the same problem myself. In the end I made it like that:
#pragma mark Start/StopSpining
-(void)startSpin{
isSpining = YES;
[self Spin];
}
-(void)stopSpin{
isSpining = NO;
}
-(void)spin{
[UIView animateWithDuration:1.0 delay:0 options: UIViewAnimationOptionCurveLinear animations:^{
self.spinCircle.transform = CGAffineTransformRotate(self.spinCircle.transform, M_PI/2);
} completion:^(BOOL finished) {
if (isSpining)
[self spin];
}];
}
Try below extension for swift 3.
extension UIView {
func rotate360Degrees(duration: CFTimeInterval = 3) {
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotateAnimation.fromValue = 0.0
rotateAnimation.toValue = CGFloat(M_PI * 2)
rotateAnimation.isRemovedOnCompletion = false
rotateAnimation.duration = duration
rotateAnimation.repeatCount=Float.infinity
self.layer.add(rotateAnimation, forKey: nil)
}
}
For start rotation.
MyButton.rotate360Degrees()
And for Stop.
MyButton.layer.removeAllAnimations()
I suggest you don't rotate the UIButton, just the image you put on the button. Who knows what complications or overhead rotating a rectangular object that can detect touches introduces - what happened if your user somehow taps in the corner of the button and then releases the tap as the button has rotated so that the touch is now outside? Does that register a touch up inside or not?
Anyway, if you want continuous rotation use a block method:
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
seems to fit the bill. The complete rotation (2pi) will occur every duration and you cause it to repeat until you stop it using UIViewAnimationOptionRepeat. You can check with completion to see if you should begin another cycle when one animation cycle has ended and you can stop the animation at any time with
[myView.layer removeAllAnimations];
(If you simply want to run it until you stop it then there are other variants of this method without a completion block)
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.3];
uiButtonObject.transform = CGAffineTransformMakeRotation(M_PI);
[UIView commitAnimations];
M_PI is the radian value of the angle you want to rotate. Run this in a loop and in a different thread. You might also want to run the above using performSelectorInMainThread method as UI changes are not permitted in the UIThread.