CGAffineTransformMakeScale animation not working - ios

I have a viewcontroller with a view that I am dismissing using a UIView animation to scale it down to 0 before removing it. My code for dismissing it is:
[UIView animateWithDuration:_dismissAnimationDuration
delay:0.0
options:UIViewAnimationOptionCurveEaseIn
animations:^(void) {
_menuContainerView.transform = CGAffineTransformMakeScale(0.0, 0.0);
}
completion:^(BOOL finished){
if ([_delegate respondsToSelector:#selector(popUpMenuDidClose)])
{
[_delegate popUpMenuDidClose];
}
[self.view removeFromSuperview];
[self removeFromParentViewController];
}];
That works perfectly when building from XCode 5 onto devices running both iOS 7 and iOS 8. But, as soon as I build to iOS 8 from XCode 6 (beta 6 and beta 7) the view just cuts away instead of animating. If that wasn't weird enough as soon as I change the target scale to (0.001, 0.001) it animates fine regardless of XCode version. Any ideas as to why I can't animate to an actual (0.0, 0.0) scale with XCode 6?

So after speaking with a developer at Apple the reasoning I got back was that some base frameworks need to work with the inverses of transform matrices quite often, and since there is no inverse for the zero matrix the animation just returns out to avoid crashing. Hopefully this post has helped others who ran into a similar situation.

If acceptable for you, set the scale values to 0.01 like so:
_menuContainerView.transform = CGAffineTransformMakeScale(0.01, 0.01);
Reference

Related

UIView's animations stopped when adding a subview

Here is the view that I created using Storyboard:
Clouds, avatar image button, and other text buttons, etc. are its subviews. In its viewDidAppear method, I added several animation methods which cause the clouds to "float" horizontally, for example:
[UIView animateWithDuration:4.0f
delay:0.0f
options:(UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat | UIViewAnimationOptionAllowUserInteraction)
animations:^{
_cloud1.frame = its new frame so that it looks to float horizontally;
}
completion:nil];
Now I have another subview which I created using a XIB file. It's basically a custom-sized subview which will appear when the avatar image button is tapped (we can think of it as "editing profile screen" in social apps). I added it with this animation block (please note that it justs appear on top of the purple circle, which is actually not removed):
_editProfileViewController = [[EditProfileViewController alloc] initWithNibName:nil bundle:nil];
[self addChildViewController:_editProfileViewController];
[_editProfileViewController didMoveToParentViewController:self];
[UIView transitionWithView:self.view duration:0.3 options:(UIViewAnimationOptionTransitionCrossDissolve) animations:^
{
[self.view addSubview:_editProfileViewController.view];
} completion:NULL];
Problem: When I do that, all clouds stop animating (while the transition still works).
Any ideas? Thank you.
After hours banging my head against the wall, it seems that this is an iOS simulator bug. Everything works ok on device.
Note for anyone who faces the same problem:
OS: Mavericks 10.9.4
Xcode: 5.1.1
iOS Simulator target: iOS 7.1

Animation of CGAffineTransform in iOS8 looks different than in iOS7

I'm trying to find a reason why animation of UIView transform property looks different in iOS 8 than iOS 6/7.
For a simple example, prior to iOS 8:
myView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, 1.57);
[UIView animateWithDuration:5 animations:^{
myView.transform = CGAffineTransformTranslate(plane.transform, 100, 0);
}];
gives expected result, "myView" is rotated 90 degrees and moves down, but in iOS8 when translation is animated it starts at a point that I couldn't find explanation for (which breaks the animation).
Does anyone know the explanation for it? Thanks in advance!
CGAffineTransformIdentity behaves differently on ios7 and ios8. This has to do with auto-layout and size classes. The solution is to remove constraints that conflict with the animation on ios7.
// solve the constraint-animation problem
if(NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) {
// iOS7 remove constraints that conflict with animation
if (self.centerYAlignment != nil) {
self.view.removeConstraint(self.centerYAlignment) //is an IBOutlet
}
} else {
// iOS8 constraint animations are fine
}
I think the reason is just iOS8 bug, but I use CAAnimation instead, and it works as expected on iOS8.
I had problems with jerky rotation transform in iOS7 as well. Solved this by nesting my rotated view inside a container and centering the rotated view inside.
I'm also experiencing the same issue with scaling. I guess it could be the same with rotation. Could you try this?
myView.transform = CGAffineTransformConcat(myView.transform , CGAffineTransformMakeRotate(1.57));
[UIView animateWithDuration:5 animations:^{
myView.transform = CGAffineTransformTranslate(plane.transform, 100, 0);
}];
Maybe it's also necessary to use CGAffineTransformMakeTranslate and CGAffineTransformConcat that as well, I'm not sure.
The worst part about this is: You would have to do if/else on iOS versions, because this would look weird on iOS 7. I hope this is getting fixed by Apple before or with iOS 8 release.
I agree with Pbk that it has to do with size classes in io8. uiviewcontrollers need to be resized with uitraitcollections depending on the device orientation. Otherwise, you get a uiviewcontroller in portrait mode, while the phone is in landscape mode, when you try to rotate it. So the correct steps are to rotate AND override uitraitcollections
This isn't entirely related, but I was struggling with CGAffineTransformScale not working at all on iOS7 in a fairly complicated animation. It turns out my problem was iOS7 cannot calculate CGAffineTransformScale with CGAffineTransformRotate at the same time. In iOS7, the last animation call you make is the only one that gets animated, so only the rotation was occurring. This bug is fixed in iOS8.
My solution is to simplify my animation for iOS7, only turning on the fancy stuff in iOS8:
//Pre-animation setup:
CGFloat radians = (M_PI/180) * (-15); //Get a human-readable number in degrees
self.badgeImage.alpha = 0; //Start the image as invisible
self.badgeImage.transform = CGAffineTransformScale(self.badgeImage.transform, 1.5, 1.5); //Start the image as scaled bigger than normal
if(NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_7_1) { //See below. We will not be rotating the image in iOS7
self.badgeImage.transform = CGAffineTransformRotate(self.badgeImage.transform, radians); //Rotate the image if iOS8
}
//Animation Pieces:
//Fade in
[UIView animateWithDuration: 0.5
delay:0
options:0
animations:^{
self.badgeImage.alpha = 1.0f; //Return image to opaque
}
completion:NULL];
//Scale with bounce
[UIView animateWithDuration: 1.1
delay:0
usingSpringWithDamping:0.3 //Not as good as Android's bounce interpolator, but I'll take it
initialSpringVelocity:-1.0f //A negative velocity here makes the animation appear more like gravity than spring
options:0
animations:^{
self.badgeImage.transform = CGAffineTransformScale(self.badgeImage.transform, 0.67, 0.67); //Return image to its original size. These arguments are relative to its current scale.
}
completion:NULL];
//Rotation
if(NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_7_1) { //This second animation call negates the first one on iOS7, so remove it.
[UIView animateWithDuration: 0.9
delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
self.badgeImage.transform = CGAffineTransformRotate(self.badgeImage.transform, (radians * -1)); //Rotate the image back to its original orientation if iOS8
}
completion:NULL];
}
Of course, you can still combine multiple effects in iOS7 if you use the confusingly-named CGAffineTransformMakeScale() function. For instance, in the pre-animation setup, you can set both a rotation AND a scale, then set call CGAffineTransformMakeScale(1,1) to reset the image to its original metrics (MakeScale's arguments are specific, not relative - even more confusing!). This isn't always preferable, such as my example above where "bouncing" the animation would also bounce the rotation.

Constraint animation not working on device but on simulator

Im using a UIView animations to move up/down a view (Using Autolayout) .
Animation works on simulator but it doesn't on actual device. What could be the problem? Code to do the animation is here
[self layoutIfNeeded];
[UIView animateWithDuration:.5 animations:^{
self.animationConstraint.constant = -190;
[self bringSubviewToFront:self.containerView];
[self.containerView layoutIfNeeded];
}];
I have checked the memory usage, but it doesn't cross more than 2% at the time of doing the animations.
Is the simulator device the same like the actual device in regard to iOS and display size?

iOS 7 animation block not being called

I have the following code to animation some view in my app:
void (^animate)() = ^() {
CGRect leftFrame = centerFrame;
leftFrame.origin.x -= centerFrame.size.width;
newViewController.view.frame = centerFrame;
oldViewController.view.frame = leftFrame;
};
if (animated) {
[UIView animateWithDuration:0.3f
delay:0.0f
options:nil
animations:animate
completion:^(BOOL finished){}];
} else {
animate();
}
This animates correctly on iOS 6, however on iOS 7 there is no animation. Oddly the code inside the block does get called and the view does update, just without the animation duration taken into account.
Is there a reason why this block isn't getting called?
I had the same issue: an animation block that works like a charm in iOS6 is executed without the animation in iOS7.
Not sure if this might help you but I moved the animation trigger to viewDidAppear: and now its being animated.
So for you this might look like this:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
animate();
// do your other stuff here...
}
Your animation may be colliding with another competing animation block, perhaps one called by UIKit during a transition. Instead of passing nil to the animation options, use:
UIViewAnimationOptionOverrideInheritedOptions | UIViewAnimationOptionBeginFromCurrentState
and see if this helps.
I had exactly the same regression between iOS6 and iOS7.
In my case, it was due to the fact that animateWithDuration: was called before the UIView it animates appeared on screen.
In iOS7 it appears that you shouldn't call animateWithDuration: before -(void)viewDidAppear:(BOOL)animated is called on the controlling UIViewController.
I was having the same issue. What it turned out for me is I needed to reference a different part of the UITableViewCell I was animating than I did with iOS 6. It might be a similar issue for you.
It is probably autolayout in iOS 7 which is giving you troubles. I just had the same problem. Instead of animating the frame, you need to animate the constraints.
See Are NSLayoutConstraints animatable? for how to do it.
try using :
[UIView setAnimationsEnabled:YES];

Having problems allowing interaction in UIView animation

I have the following block of code to fade out an introView(UIView)
// Hide intro view after 5 seconds
[UIView animateWithDuration: 1.0
delay: 5.0
options: (UIViewAnimationOptionAllowUserInteraction |UIViewAnimationOptionCurveLinear)
animations: ^{
introView.alpha = 0;
}
completion: ^(BOOL finished) {
[introView removeFromSuperview];
}];
I have a skip button inside the introVew but there is no interaction whatsoever, am I missing something? I have to add this is a Universal app targeting 3.2 and I'm using XCode 4.2
Pretty sure this is impossible pre-4.0:
UIView userInteractionEnabled Docs
During an animation, user interactions are temporarily disabled for
all views involved in the animation, regardless of the value in this
property. You can disable this behavior by specifying the
UIViewAnimationOptionAllowUserInteraction option when configuring the
animation.
There seems little point in targeting 3.2 in an app you haven’t released yet.
Are you setting your button alpha to 0?
If yes here is an interesting thing about animation.
What you see on the screen during the animation is not what the application sees.
The moment you set your alpha to 0, the alpha is 0 for that view, even if you are still seeing it on the screen.
Also, a view that has an alpha lower that 0.05 (don't recall the exact number) won't get touch event.
What you can do is to implement the - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event of that view's superview. or the touchesEnded... as you like.
(Assuming that your not setting it's alpha to 0.)
So you can test for touche that occur where the button is, or just remove that button and let any touch on the screen cancel your animation.
You may also be interested in this post:
Core Animation, unexpected animated position and hitTest values
I found another circumstance which could cause this. I haven't seen this answer anywhere else. It does not deal with alpha at all.
If you use a delay in the call to UIView.animate(), then even if you specify the .allowUserInteraction option, the view does NOT receive touches during the delay period. I have no idea why, but I could help it by moving the code block to another function, and using a performSelector after the same delay seconds, and in the block I run the code without delay.
I had the same problem with a button that I animated with changing the alpha. Cueing off VinceBurn's answer...
What you see on the screen during the animation is not what the application sees. The moment >you set your alpha to 0, the alpha is 0 for that view, even if you are still seeing it on the >screen.
AND view that have an alpha lower that 0.05 (don't recall the exact number) won't get touch >event.
… the simple solution of just making the minimum alpha 0.1 instead of 0.0 worked for me:
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse | UIViewAnimationOptionAllowUserInteraction
animations:^{
self.myButton.alpha = 0.1f;
}
completion:^(BOOL finished){
}]
Button registered the touchUpInside all the time with no additional method needed, and there was virtually no difference in appearance from taking the alpha to zero.
This won't work in iOS 3.2 since Blocks are only available in iOS4
you will have to use the standard animation techniques, in a separate thread so that you don't block the interface
[UIView beginAnimations:nil context:nil];
[UIView setAnimationTransition: UIViewAnimationTransitionFlipFromLeft forView:view cache:YES];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:1.0];
[view1 setHidden:TRUE];
[UIView commitAnimations];

Resources