How to know when UIView animations have all completed - ios

A custom view I created is being animated by its container - in my view I update portions of the subviews with other animations (for instance a UICOllectionView)
I see that this is throwing errors
*** Assertion failure in -[UICollectionView _endItemAnimations
Digging around I see that my view has animations attached to it:
<GeneratorView: 0x15b45950; frame = (0 0; 320 199); animations = { position=<CABasicAnimation: 0x145540a0>; }; layer = <CALayer: 0x15b49410>>
So now before performing animation operations I check:
NSArray *animationKeys = [self.layer animationKeys];
for (NSString *key in animationKeys) {
CAAnimation *animation = [self.layer animationForKey:key];
}
and I see that animation objects are returned:
at this point I would like to "wait" until all animations have completed before updating self.
I see that I can add myself as the CAAnimation delegate.
But this is a bit messy and hard to track.
Is there an easier way using a UIView method - much higher level?

You can use UIView method to do the animation in the container:
[UIView animateWithDuration: animations: completion:];
[UIView animateWithDuration: delay: options: animations: completion:];
You should add properties to your custom view:
#property BOOL isAnimating;
In the container run the animation block and change view isAnimating property.
This method accept block which will be fired when the animation complete.
Example:
[UIView animateWithDuration:1.0 animations:^{
//Your animation block
view1.isAnimating = YES;
view1.frame = CGRectMake(50.0f, 50.0f, 100.0f, 100.0f);
// etc.
}completion:^(BOOL finished){
view1.isAnimating = NO;
NSLog(#"Animation completed.");
}];
Now it your view you can see if the view is animating:
if (self.isAnimating)
NSLog(#"view is animating");

Ithink you can you that :
#interface UIView(UIViewAnimationWithBlocks)
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion NS_AVAILABLE_IOS(4_0);
Hope that will help

Related

animateWithDuration not working inside GMSMapView delegate method

I am trying to animate a UIView with the code below, but I fail to understand why the code below does not work.
Works - self.searchBar animates smoothly // in all except for the said method
- (void) mapView:(GMSMapView *)mapView idleAtCameraPosition:(GMSCameraPosition *)position
{
[UIView animateWithDuration:.3
delay:0
options: UIViewAnimationCurveEaseOut
animations:^{
[self.searchBar setFrame:CGRectMake(self.searchBar.frame.origin.x, self.searchBar.frame.origin.y - self.searchBar.frame.size.height , self.searchBar.frame.size.width, self.searchBar.frame.size.height)];
}
completion:^(BOOL finished){
}];
}
Does Not Work - self.searchBar jumps without animating
- (void)mapView:(GMSMapView *)mapView willMove:(BOOL)gesture
{
[UIView animateWithDuration:.3
delay:0.0
options: UIViewAnimationCurveEaseOut
animations:^{
[self.searchBar setFrame:CGRectMake(self.searchBar.frame.origin.x, self.searchBar.frame.origin.y - self.searchBar.frame.size.height , self.searchBar.frame.size.width, self.searchBar.frame.size.height)];
}
completion:^(BOOL finished){
}];
}
I opened a bug on this issue with Google (https://code.google.com/p/gmaps-api-issues/issues/detail?id=10349), and this was their response:
Our understanding is that this is happening because the mapView:willMove: call is being made while already inside a CoreAnimation transaction. Our suggested work around is to wrap the UIView.animateWithDuration call within a dispatch_async back to the main thread. Something along the lines of:
dispatch_async(dispatch_get_main_queue(), ^{
// animation block here.
});
Does that help?
Placing the UIView.animateWithDuration block inside a dispatch_async block worked for me.
Also, this question is a duplicate of iOS layout animation ignores duration when fired on GMSMapView dragging, but I don't have enough rep to flag it as such. If anyone's keeping score, it should also be noted that Google's workaround is what Vishnu suggested in his answer. I didn't have enough rep to just comment on his answer either.
Use CABasicAnimation help me to solve this problem.
In my case I have to move UIView frame.
CABasicAnimation *moveAnimation = [CABasicAnimation animationWithKeyPath:#"movePosition"];
moveAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake([viewToMove center].x, [viewToMove center].y)];
moveAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake([viewToMove center].x, y)];
moveAnimation.delegate = self;
moveAnimation.duration = 1.0;
moveAnimation.speed = 3.0;
moveAnimation.cumulative = YES;
moveAnimation.repeatCount = 1.0;
moveAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
moveAnimation.removedOnCompletion = NO;
moveAnimation.fillMode = kCAFillModeForwards;
[viewToMove.layer addAnimation:rotationAnimation forKey:#"movePosition"];
Then in
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
save new position of viewToMove
You may try to add the animation inside operation main queue:
- (void)mapView:(GMSMapView *)mapView willMove:(BOOL)gesture
{
[[NSOperationQueue mainQueue] addOperationWithBlock:^ {
//Your animation code goes in here
}];
}

Showing UIView with bouncy animation

I want to create a bouncy animation like in the Skitch application - when i press a button, a subview (simple view with several buttons) will appear from the side and will bounce a bit until it stops.
I can't find any standard animation that does that, so how i can implement this animation?
You can try this :
// Hide your view by setting its position to outside of the bounds of the screen
// Whenever you want, call this :
[UIView animateWithDuration:0.5
delay:0
usingSpringWithDamping:0.5
initialSpringVelocity:0
options:UIViewAnimationOptionCurveLinear
animations:^{
// Set your final view position here
}
completion:^(BOOL finished) {
}];
This method is the same as the usual animateWithDuration: but it introduces a spring animation.
Use UIView method:
+ (void)animateWithDuration:(NSTimeInterval)duration
delay:(NSTimeInterval)delay
usingSpringWithDamping:(CGFloat)dampingRatio
initialSpringVelocity:(CGFloat)velocity
options:(UIViewAnimationOptions)options
animations:(void (^)(void))animations
completion:(void (^)(BOOL finished))completion
This will do what you want
Use the UIView method, If you want bouncing effect. Then you need to nested the animation process and give the frame as needed for that view. Here's the example:
CGRect frameOnStart = CGRectMake(200,200 ,60 ,60); //On start show small view
CGRect frameOnAnimate = CGRectMake(80,80 ,150 , 150); //while animate show large view
CGRect frameAfterAnimate = CGRectMake(100,100,120 ,120); //after animate large take it back to original frame. SO it loos like bouncing animation
// Whenever you want, call this :
[UIView animateWithDuration:0.5
delay:0
usingSpringWithDamping:0.5
initialSpringVelocity:0
options:UIViewAnimationOptionCurveLinear
animations:^{
// Set your on Animate position here
view.frame = frameOnAnimate;
}
completion:^(BOOL finished) {
[UIView animateWithDuration:0.5
delay:0
usingSpringWithDamping:0.5
initialSpringVelocity:0
options:UIViewAnimationOptionCurveLinear
animations:^{
// Set your final view position here
view.frame = frameAfterAnimate;
}
completion:^(BOOL finished) {
}]
}];
Thanks.

ios 7 UIView animateWithDuration: .. runs immediately

I have trouble with method for animation
I've created my own CalloutBubble for GoogleMap
#interface CalloutView : UIView
#property (nonatomic) MapMarker *marker;
#end
#implementation {
UIView *titleView;
UILabel *titleLabel, *addressLabel;
}
//another init methods aren't shown
- (void)setMarker:(MapMarker *)marker
{
_marker = marker;
titleLabel.text = marker.university.name;
addressLabel.text = marker.university.address;
[titleLabel sizeToFit];
titleLabel.minX = 0;
[titleLabel.layer removeAllAnimations];
if (titleLabel.width > titleView.width)
[self runningLabel];
}
- (void)runningLabel
{
CGFloat timeInterval = titleLabel.width / 70;
[UIView animateWithDuration:timeInterval delay:1.0 options:UIViewAnimationOptionOverrideInheritedDuration | UIViewAnimationOptionRepeat animations:^{
titleLabel.minX = -titleLabel.width;
} completion:^(BOOL finished) {
titleLabel.minX = titleView.width;
[self runningLabel];
}];
}
#end
In my viewController I create property
#implementation MapVC {
CalloutView *calloutView;
}
And then if I try create calloutView in any method all work fine with animation, but if I return view in Google map method
- (UIView *)mapView:(GMSMapView *)mapView markerInfoWindow:(GMSMarker *)marker
{
if (!calloutView)
calloutView = [[CalloutView alloc] initWithFrame:CGRectMake(0, 0, 265, 45.5)];
calloutView.marker = (MapMarker *)marker;
return calloutView;
}
My animation run immediately in calloutView, and completion called immediately too, and call runningLabel method again, So it is not work like it have to. All frame is good, and timInterval always more than 4 second. I tried to write static timeinterval like 10.0, but animation again run immediately, and flag finished in completion block always YES. So it called more than 100 times in one second =(
I create app in iOS 7. I tried to use different options for animation:
UIViewAnimationOptionLayoutSubviews
UIViewAnimationOptionAllowUserInteraction
UIViewAnimationOptionBeginFromCurrentState
UIViewAnimationOptionRepeat
UIViewAnimationOptionAutoreverse
UIViewAnimationOptionOverrideInheritedDuration
UIViewAnimationOptionOverrideInheritedCurve
UIViewAnimationOptionAllowAnimatedContent
UIViewAnimationOptionShowHideTransitionViews
UIViewAnimationOptionOverrideInheritedOptions
but there are no results.
What wrong in this Google map method, why my animation run immediately?
PS minX, width - my categories. minX set frame.origin.X . There are all good with this categories.
Remove UIViewAnimationOptionRepeat option Or comment the completion Block minX setting:
- (void)runningLabel
{
CGFloat timeInterval = titleLabel.width / 70;
[UIView animateWithDuration:timeInterval delay:1.0 options:UIViewAnimationOptionOverrideInheritedDuration | UIViewAnimationOptionRepeat animations:^{
titleLabel.minX = -titleLabel.width;
} completion:^(BOOL finished) {
//titleLabel.minX = titleView.width; //this is the problem, you should not set minX again while UIViewAnimationOptionRepeat also is animation option
[self runningLabel];
}];
}

UIViewAnimateWithDuration completion never called

I am using the UIView animateWithDuration:delay:options:animations:completion: method but the completion method is never getting called. Here is my code:
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^
{
//random lines of code
}completion:^(BOOL finished){
if (finished)
{
NSLog(#"FINISHED");
}
}];
EDIT: When I comment out the lines in my animations: it gets called???!!!
These are the lines:
CGFloat objectY = object.frame.origin.y;
objectY += speed;
object.frame = CGRectMake(object.frame.origin.x, objectY, 75, 75);
I am taking a guess - you want to animate the movement of a continuous gesture?
If yes, the animation never ends due the user interaction.
Just update the frame, no UIView Animation. Should work just fine.

Fade out subviews except one specific subview

I am trying to fade out whole view but one specific subview, which would emphasize the subview with the more clearer look. Let's say if self.view has 5 textfields and 2 UIView as subviews and I want 1 UIView to be emphasized. Is there a way to fade out/in the whole self.view with 5 textfields and 1 UIView without touching the emphasized UIView?
I have already tried to use method
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
such as:
[UIView animateWithDuration:0.75
animations:^{
self.view.alpha = 0.5f;
}
completion:^(BOOL finished){self.view.layer.shouldRasterize = NO;}];
But, it seemed like whole subviews are affected by the UIView animation above. Any advice or tip will be greatly appreciated.
If you lower the alpha of a UIView, all it's subview's alpha will be lowered. A solution to your problem would be to lower the alpha of each subview individually, except the one you want:
UIView emphasizedView = ...
[UIView animateWithDuration:0.75
animations:^{
for (UIView *view in self.view.subviews) {
if (view != emphasizedView) {
view.alpha = 0.5f;
} else {
view.alpha = 1;
}
}
}
completion:^(BOOL finished){self.view.layer.shouldRasterize = NO;}];

Resources