UIView animateWithDuration Not Honoring Duration - objective-c

I have a simple animation that moves a view from one location to another. The animation functions correctly except for the fact that is doesn't follow the duration set in the method declaration...
[UIView beginAnimations:#"listAnimationIn" context:nil];
[UIView animateWithDuration:1.0
delay:1.0
options:UIViewAnimationCurveLinear
animations:^{
lvc.view.frame = CGRectMake(0, 0, 320, 480);
}
completion:nil];
[UIView commitAnimations];
The delay works, but the animation runs FAST no matter what value I put in for duration (I have tried values from 0.5 to 2000). I have also tried a few UIViewAnimationCurve options and nothing seems to work.
Am I missing something?

You don't need the -beginAnimations:context: and the -commitAnimations messages. That's the old way of doing it, and you shouldn't really mix. Just call the -animateWithDuration:delay:options:animations:completion: method. Also, if you add a completion block, it will be passed a boolean variable that will tell you if your animation terminated early or ran to completion. This happens if the animation is too slow, or you have competing animations starting on same view hierarchy (which you do have, with the old style begin/commit calls).

Related

no animation when using modal segue

I have a view controller and have a few objects that I have some simply animations hooked up to.
[UIView animateWithDuration:0.25
delay: 10.1
options: UIViewAnimationOptionCurveEaseIn
animations:^{
addNameViewConstraint.constant = 10;
addNameView.alpha = 1.0;
[self.view layoutIfNeeded]; // move
}
completion:nil];
[UIView animateWithDuration:0.25
delay: 0.15
options: UIViewAnimationOptionCurveEaseIn
animations:^{
addEmailViewConstraint.constant = 10;
addEmailView.alpha = 1.0;
[self.view layoutIfNeeded]; // move
}
completion:nil];
When I was using the push segue the animation works fine. But when I switched to using a modal segue the animation stopped working. I increased the delay in the first one to 10.2 seconds thinking that maybe it was animating before I got to see it. I am calling this animation in the viewWillAppear method. Again works if I'm doing a push segue.. but not for modal. Any ideas?
I am calling this animation in the viewWillAppear method
This is a common mistake. viewWillAppear: is too soon to start the animation. The view has not yet appeared so there is nothing to animate. Move the animateWithDuration:... code to viewDidAppear: and all will be well.
This is, however, as you say in your comment, insufficiently satisfying. What you are after is that the modal transition should be happening and your extra animations within the new view should already be happening as the modal view is in the process of appearing. Instead, with viewDidAppear:, the modal view finishes appearing, it settles into place, and then your other animations start, which is not as cool.
One solution might be to move the animations again, this time to viewWillLayoutSubviews. This is trickier because this method gets called a lot. You will need to use a BOOL instance variable as a flag to ensure that your animations run only once. Thus, this should work (I tried it and it seemed fine):
-(void)viewWillLayoutSubviews {
if (!self->didLayout) {
self->didLayout = YES;
[UIView animateWithDuration:0.25 animations:^{
// mess with constraint constants here
[self.subv layoutIfNeeded];
}];
}
}

UIView animation stop when UIView willDisappear

I am not much into iOS animation. What I am trying to achieve is a simple message view that slide vertical from bottom of screen to a given y, then after few instants the UIView rollback in vertical to go off screen.
[UIView animateWithDuration:0.5f
animations:^{
self.messageView.frame=CGRectMake(x, y -80, width, height);
}
completion:^(BOOL finished){
if (finished) {
[UIView animateWithDuration:0.5f delay:2.0f options:UIViewAnimationOptionCurveLinear
animations:^{
self.messageView.frame=CGRectMake(x, y + 80, width, height);
}
completion:^(BOOL finished){
// do something...
}
];
}
} ];
This is working fine, but I am having a problem using this mechanism in a iOS UITabBar application: when I change tab, the animation stop, I can infact see that "finished" completion is "false". Therefore the second block is not called, and the message view stays on.
Here are the questions:
my first concern is to understand if the code I have written is correct, regarding the nested animations.
I could solve by ignoring 'finished' and execute code anyway, but I don't feel it is a good idea
within the last completion block, I have put some programming logic, basically I am restoring few UIButtons state, and some other little UI change. At this point I don't know if it is a good idea, it seems not, but how can let the UI knows that the message view has disappeared. NSNotification and KVO seems a bad idea when fast responsive UI change are involved.
You can stop all animations for a layer by sending the layer a removeAllAnimations message.
[sel.view removeAllAnimations];

Detect end of implicit animation when changing the UICollectionViewLayout

I am currently working with UICollectionView and after changing the layout from one to another with setCollectionViewLayout:animated: I want to execute some code upon the completion of the animation. Any idea how to achieve that?
Cheers,
Just for the record, there is now a -setCollectionViewLayout:animated:completion: method on UICollectionView, introduced in iOS 7.
I found that to be able to control the animation, rather than using the implicit one with setCollectionViewLayout:animated: you can use a standard UIView animationWithDuration and change the layout in the animations block, like this:
[UIView animateWithDuration:0.4
delay:0
options:UIViewAnimationCurveEaseOut
animations:^{
self.oldView.collectionViewLayout = self.otherLayout;
self.newView.alpha = 1.0;
}
completion:^(BOOL finished){
self.oldView.alpha = 0.0;
self.otherLayout = nil;
}];
I don't know if it is the recommended way to do it but it lets you control the animation and execute code upon completion.

XCode/Objective-C: Simultaneous animations for instances of UIView?

I have two IBOutlet variables connected to two instances of UIView in Interface Builder, called blankView and fadedBGView in my ViewController.
How can I set up this code so that the blankView instance will fade into fadedBGView, using the UIViewAnimationOptionTransitionCrossDissolve transition, and the two UIViews can simultaneously transform (move) from their current position (they have equal positions) to 0,0?
What's happening is the fade occurs, and then the fadedBGView view abruptly moves to 0,0.
In other words, I'd like to have the first UIView fade into the second, while simultaneously moving up the screen.
Hopefully this is clear enough to answer.
Best...SL
[UIView transitionFromView:blankView
toView:fadedBGView
duration:1.0
options:UIViewAnimationOptionTransitionCrossDissolve
completion:^(BOOL finished) {
[blankView removeFromSuperview];
}];
[UIView commitAnimations];
CGRect frame = fadedBGView.frame;
frame.origin.x = 0;
frame.origin.y = 0;
[UIView animateWithDuration:1.0 animations:^{
NSLog(#"UIView Transition/Move Called");
fadedBGView.frame = frame;
}];
iOS 4 and newer provides block-based animations, which your code is already using:
+[UIView animateWithDuration:animations:];
+[UIView animateWithDuration:animations:completion:];
Within the animation block, you can set multiple destination values. See Apple's UIView documentation for a reference to the animatable properties. So within one block, you can modify frames, and alpha (transparency) values of views. I'm not sure how the cross dissolve animation works, but if it's simply one view going from 0 to 1, and another view going from 1 to 0, that's easy to implement.
Instead of
UIViewAnimationOptionTransitionCrossDissolve
use
UIViewAnimationOptionTransitionCrossDissolve | UIViewAnimationOptionAllowAnimatedContent
UIViewAnimationOptionAllowAnimatedContent option allows additional animations during transition. Make sure your fadedBGView has correct starting frame.

Consecutive animations using nested animation blocks

I'm looking for a way to implement consecutive animations using nested animation blocks.
Somewhat complicated by happening inside a UIScrollView, the size of three UIImageViews (there are many images, and as I scroll through them I constantly swapping out the images in the UIImageViews).
When a scroll is finished, I want to switch out the image in the (visible) middle UIImageView, three times, then back to the original view. I'm trying it thus:
- (void) doAnimation {
// get the animation frames, along with the current image
NSString *swap1 = #"first.png";
NSString *swap2 = #"second.png";
UIImage *original = currentPage.image;
UIViewAnimationOptions myOptions = UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction;
[UIView animateWithDuration:2.0 delay:2.0 options:myOptions
animations:^{ [currentPage setImage:[UIImage imageNamed:swap1]]; }
completion:^(BOOL finished) {
[UIView animateWithDuration:2.0 delay:2.0 options:myOptions
animations:^{ [currentPage setImage:[UIImage imageNamed:swap2]]; }
completion:^(BOOL finished) {
[UIView animateWithDuration:2.0 delay:2.0 options:myOptions
animations:^{ [currentPage setImage:[UIImage imageNamed:swap1]]; }
completion:^(BOOL finished) {
[currentPage setImage:original]; }]; }]; }];
}
When I run this, there is no duration, no delay, it all happens at once, almost too fast for the eye to see. Could this be because "currentPage" is a UIImageView? (Similar to this question?)
There's no delay because UIImageView.image isn't an animateable property. As such, the UIView animation machinery will have no animations to set up and will just call your completion block immediately.
What sort of animation did you expect? You can attach a CATransition object to the underlying layer to get a simple cross-fade, Just use [imageView.layer addAnimation:[CATransition animation] forKey:nil] to get the crossfade with the default values (you can customize the timing by modifying properties of the CATransition before attaching it to the layer). To achieve the subsequent animations, you can either use the delegate property of CAAnimation (CATransition's superclass) to learn when it's done and fire your second one, or you could just use -performSelector:withObject:afterDelay: to start your next animation step after a user-defined delay. The delegate method is going to be more accurate with regards to timing, but the performSelector method is a bit easier to write. Sadly, CAAnimation doesn't support a completion block.
Another approach for you to transition from one image view to another is by using the block animation function transitionFromView:toView:duration:options:completion as discussed in "Creating Animated Transitions Between Views". You would do this instead of animateWithDuration to change images.