How to cancel UIView block-based animation? - iphone

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)

Related

Start animate UIView before complete?

I'm using [UIView animateWithDuration:...] for animate sequence of UIImageView views. Like this:
[UIView animateWithDuration:1.0 animations:^{
imageView.frame = newImageRectPosition;
}completion:^(BOOL finished){
//animate next UIImageView
}];
I need animate 'next UIImageView' not on completion. I need animate 'next UIImageView' on the middle of previous animation, not on completion. Is it possible to do so?
You can setup two UIView animation blocks, one which has a delay of half the duration of the first animation:
[UIView animateWithDuration:1.0
animations:^{ ... }
completion:^(BOOL finished){ ... }
];
[UIView animateWithDuration:1.0
delay:0.5
options:UIViewAnimationCurveLinear
animations:^{ ... }
completion:^(BOOL finished) { ... }
];
There are many options you can use to achieve the effect you're after. One which comes to mind is the use of timers.
Use an NSTimer with a firing interval half of that of your animation, and get the timer to fire off another animation. As long as the two animations don't interfere with each other, you should be ok.
An example would be as so:
NSTimer* timer;
// Modify to your uses if so required (i.e. repeating, more than 2 animations etc...)
timer = [NSTimer scheduledTimerWithTimeInterval:animationTime/2 target:self selector:#selector(runAnimation) userInfo:nil repeats:NO];
[UIView animateWithDuration:animationTime animations:^{
imageView.frame = newImageRectPosition;
} completion:nil];
- (void)runAnimation
{
// 2nd animation required
[UIView animateWithDuration:animationTime animations:^{
imageView.frame = newImageRectPosition;
} completion:nil];
}
With a timer, this could scale up if you needed to do more than two animations, and it all holds together if you need to change the animation time later on.

iPhone : UIVIew Boing kind of animation

Sorry for the poor title, I am trying to think of a easy way to explain this.
I have a UIView in the center of the screen which contains a progress indicator and background image.
What I want it to do is get bigger to a certain point and then shrink a tiny bit. So it "boings" in.
I had a play with normal UIView animations etc and have it coming in. However I thinking to get this to work well I need to use the views layer. The main issue at the moment is the indicator does not size.
Has anyone done a boing effect on a view?
Something like this?
- (void)boingView:(UIView *)theView {
[UIView animateWithDuration:0.1 animations:^(void) {
theView.transform = CGAffineTransformMakeScale(1.3, 1.3);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.1 animations:^(void) {
theView.transform = CGAffineTransformMakeScale(0.8, 0.8);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.1 animations:^(void) {
theView.transform = CGAffineTransformMakeScale(1.0, 1.0);
} completion:nil];
}];
}];
}

UIView block animation with completion handler and setting UIViews to hidden

I'm keen on the new block animations in iOS 4. That is, the syntax
[UIView animateWithDuration:.25 delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^
{
someview.alpha = 0
} completion::^(BOOL finished)
{
focusAndExposureBox.hidden = true;
}];
I have a case where I am using gesture recognizers and animating a view at the end of a gesture. I have a completion handler which sets someview to hidden (for performance reasons I need to do this). Often interaction is blocked due to the hidden property being set. My previous solution is to use the previous style of animation with an animationDidStop handler
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:.25];
someview.alpha = 0;
[UIView commitAnimations];
However, I'd like to use the newer style if possible, because it has a cleaner method of performing actions after the animation finishes, and is recommended by Apple. So, currently I am using this hackery:
[UIView animateWithDuration:1 delay:0
options:UIViewAnimationOptionAllowUserInteraction animations:^{
someview.alpha = 0;
} completion:^(BOOL finished) {
[someview performSelector:#selector(setHidden:) withObject:[NSNumber numberWithBool:true] afterDelay:1];
}];
Does anyone know of a way to prevent blocking in this case?
You should be able to avoid the Blocking issue if you're using Gesture Recognizers with delegates, as the delegate function will be invoked independent of the main thread.

User interaction with uiview and animation completion blocks

I have the following code:
[UIView animateWithDuration:2.0 delay:0.0 options:UIViewAnimationOptionAllowUserInteraction
animations:^{
imageView.bounds = endBounds;
}
completion:^(BOOL finished) {
[UIView animateWithDuration:2.0 delay:0.5 options:UIViewAnimationOptionAllowUserInteraction
animations:^{
imageView.bounds = startBounds;
}
completion:^(BOOL finished) {
[imageView removeFromSuperview];
}];
}];
Additionally I have:
[imageView setUserInteractionEnabled:YES];
and a tap gesture recognizer set that will handle the user tapping on imageView. While the first animation is happening, the gesture recognizer fires as I would expect. But if I try and tap imageView during the chained animation from the completion block, nothing happens even though I have set the appropriate option.
Anyone have any thoughts? I've googled and can't find an answer.
When using the new animation blocks, if you want user interaction to be enabled during the animation, you have to set it in the options mask. For example:
[UIView animateWithDuration:1.0
delay:0
options:UIViewAnimationOptionAllowUserInteraction
animations:^{ myView.alpha = 0.5; }
completion:NULL];
I came up with a solution:
I wrap the UIImageView in a UIView (I subclass UIView) with the same bounds/center point as the image. Then I attach the gesture recognizer to the wrapper, instead of the image. Because the wrapper's bounds rectangle/center point never change for the duration of the animation, it's always available as the target of a gesture.
This works quite well.
-j
Do you see the same behaviour if you use:
+ [UIView setAnimationDidStopSelector:]
instead of using blocks?

What are block-based animation methods in iPhone OS 4.0?

I am trying to implement a game using the iPhone OS 4.0 (iOS4?) SDK. In the previous versions of the SDK, I've been using the [UIView beginAnimations:context:] and [UIView commitAnimations] to create some animations. However, when I look at the documentation of the functions in 4.0, I see this comment.
Use of this method is discouraged in
iPhone OS 4.0 and later. You should
use the block-based animation methods
instead.
You can find it here:
http://developer.apple.com/iphone/library/documentation/uikit/reference/UIView_Class/UIView/UIView.html#//apple_ref/occ/clm/UIView/commitAnimations
My question is, what are block-based animations in iPhone OS 4.0? I though that the beginAnimations:context: and commitAnimations functions were used to create animation blocks..
I have posted an example in my blog:
CGPoint originalCenter = icon.center;
[UIView animateWithDuration:2.0
animations:^{
CGPoint center = icon.center;
center.y += 60;
icon.center = center;
}
completion:^(BOOL finished){
[UIView animateWithDuration:2.0
animations:^{
icon.center = originalCenter;
}
completion:^(BOOL finished){
;
}];
}];
The above code will animate a UIImageView* (icon) in a 2-second animation. Once completed, another animation will move the icon back to it’s original position.
If you follow that link and scroll up a bit, you will see animate methods new to ios4.
animateWithDuration:animations:
animateWithDuration:animations:completion:
animateWithDuration:delay:options:animations:completion:
There are also some related transition methods. For each of these, the animations argument is a block object:
animations
A block object containing
the changes to commit to the views.
This is where you programmatically
change any animatable properties of
the views in your view hierarchy. This
block takes no parameters and has no
return value. This parameter must not
be NULL.
Block objects are part of Concurrent Programming
Here's a very simple example. The code just fades out an UIView and hides it after the animation is done:
[UIView animateWithDuration:1.0
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^ {
bgDisplay.alpha = 0.0;
}
completion:^(BOOL finished) {
bgDisplay.hidden = YES;
}];
or in different formatting:
[UIView animateWithDuration:1.0 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^ {
bgDisplay.alpha = 0.0;
} completion:^(BOOL finished) {
bgDisplay.hidden = YES;
}];