CGPoint center animation "nullified" by CGAffineTransform - objective-c

i'm new to Objective-C and Xcode... I'm following a tutorial that explain how to move a car(a ImageView) to the top of the screen, rotate it, an then back to the bottom of the display.
This is the code:
- (IBAction)testDrive:(id)sender {
CGPoint center = CGPointMake(_car.center.x, self.view.bounds.origin.y + _car.bounds.size.height/2 +100);
[UIView animateWithDuration:3
animations:^ { _car.center = center;}
completion:^(BOOL finished){[self rotate];}];
}
- (void) rotate{
CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI);
void (^animation)() = ^() { _car.transform = transform;
};
void (^completion)(BOOL) = ^(BOOL finished){
[self returnCar];
};
[UIView animateWithDuration:3 animations:animation completion:completion];
}
The problem is that the car moves to the top, but then it's position is resetted at the bottom of the screen, like the center was resetted to the default center value, and only then is rotated.. I can't find a solution, need your help!

When you do an animation, its effects are temporary, and by default they don't actually mutate your layers/views. Typically, you would explicitly write the "end" values into the layers/views in the completion block. You will need to do this for each animation in your "chain" of animations. With your code, I suspect you'd get the desired outcome if you did this:
- (IBAction)testDrive:(id)sender {
CGPoint center = CGPointMake(_car.center.x, self.view.bounds.origin.y + _car.bounds.size.height/2 +100);
[UIView animateWithDuration:3
animations:^ { _car.center = center;}
completion:^(BOOL finished){ _car.center = center; [self rotate];}];
}
- (void) rotate{
CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI);
void (^animation)() = ^() { _car.transform = transform;
};
void (^completion)(BOOL) = ^(BOOL finished){
_car.transform = transform;
[self returnCar];
};
[UIView animateWithDuration:3 animations:animation completion:completion];
}
You'll also want to make a corresponding change in -returnCar.
The idea here is that the animation effects only the "presentation layer" but not the "model layer." If you want the model layer to reflect the "after" state of the animation, you need to explicitly do that.

After smashing my head for hours i found the solution to this problem, that was due to AutoLayout.
This is the working code
self.topConstraint.constant = self.topConstraint.constant-self.topConstraint.constant;
[self.car setNeedsUpdateConstraints]; // 2
[UIView animateWithDuration:3
animations:^{
[self.car layoutIfNeeded]; // 3
}
completion:^(BOOL finished){[self rotate]; }
];
Thanks

Related

iOS - UIView animateWithDuration not working properly

I'm trying to do some animation to my UIProgressView when i finished upload a photo to my server. When i set the duration to 5.0 or 10.0 seconds, it it not different and the completition is called immediately.
[UIView animateWithDuration:5.0 animations:^{
self.progressBar.progress = 1.0;
} completion:^(BOOL finished) {
NSLog(#"kykny udah");
if (finished) {
NSLog(#"finished");
Post *post = [mappingResult.dictionary objectForKey:#""];
[self.posts insertObject:post atIndex:0];
NSLog(#"caption: %# , items: %#",post.caption, post.items);
CGRect frame = self.progressView.frame;
frame.size.height = 0;
self.progressView.frame = frame;
[self.tableView reloadData];
}
}];
how come this issue happens ? It is just like a normal sequence code in reality
Try adding the following line after updating your frame changes like
self.progressBar.progress = 1.0;
[self.view layoutIfNeeded];
This will make your animation effect for the time you specified in animation duration.
If you want to show just the progress for 10sec then
// PROGRESS VIEW IS JUST A VIEW WITH SOME BACKGROUND COLOR WITH FRAME CGRectMake(0,0,0,10)
[UIView animateWithDuration:5.0 animations:^{
self.progressView.frame = CGRectMake(0,0,100,10); // adjust the frame to get the linear animation view
} completion:^(BOOL finished) {
NSLog(#"kykny udah");
if (finished) {
.....
self.progressView.frame = CGRectMake(0,0,100,0);
.....
}
}];

Keep the End Position of Animation

I tried everything, but everytime its the same problem:
i want to move a button for y = 276. when the animation ends, my buttons jumps back to the startposition, but i want that the endanimationposition is the new position, and the button has to stay there, until a new animation is called.
tried with
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1];
CGPoint p = _cell00.center;
p.x += 100;
_cell00.center = p;
[UIView commitAnimations];
[UIView beginAnimations:nil context:nil];
same issue like
[UIView animateWithDuration:1.0 animations:^{
_cell00.frame = CGRectOffset(_cell00.frame, 0, 20);
}];
or
[UIView animateWithDuration:0.5 animations:^{
_cell00.center = CGPointMake(0, 20.0);
}];
EDIT:
I call this Animation with my Button called statisticsBUttonPressed.
- (IBAction)statisticsButtonPressed:(id)sender {
if (statOrGame == 0) {
statOrGame = 1;
}else {
statOrGame = 0;
}
NSLog(#"statOrGame? %d", statOrGame);
[self animateView:(UIButton *)sender];
}
-(void) animateView:(UIButton *)sender {
sender.userInteractionEnabled = NO;
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone)
{ // The iOS device = iPhone or iPod Touch
NSLog(#"IS IPHONE 5");
CGSize iOSDeviceScreenSize = [[UIScreen mainScreen] bounds].size;
if (iOSDeviceScreenSize.height == 568)
{
if (statOrGame) {
[UIView animateWithDuration:1.0 animations:^{
_cell00.frame = CGRectOffset(_cell00.frame, 0, 20);
}];
}
}
}
}
The problem is that you're using auto layout. You can't (just) change the frame of a view that is positioned by auto layout, because what positions the view is its constraints. You need to change the constraints. You can do this at the end of the animation, or you can simply animate the change of constraints.

uiview animateWithDuration cant hide in same toggle method

Trying to hide and show a view from a single method but it seems like it should work to show and hide but its instant.
- (void)toggleMyView:(BOOL)show
{
CGRect orig = self.originalMyViewFrame; // original frame of the view
CGRect offScreen = CGRectMake(orig.origin.x, self.view.frame.size.height, orig.size.width, orig.size.height);
if (show) {
self.myView.frame = offScreen; // set off screen
self.myView.hidden = NO; // unide the view
[UIView animateWithDuration:0.5 animations:^{
self.myView.frame = orig; // animate to original position
}];
} else {
[UIView animateWithDuration:0.3 animations:^{
self.myView.frame = offScreen; // animate off screen
} completion:^(BOOL finished) {
self.myView.hidden = YES; // hide when animation finished
}];
}
}
If I remove the hide logic it animates in nicely.
- (void)toggleMyView:(BOOL)show
{
CGRect orig = self.originalMyViewFrame; // original frame of the view
CGRect offScreen = CGRectMake(orig.origin.x, self.view.frame.size.height, orig.size.width, orig.size.height);
if (show) {
self.myView.frame = offScreen; // set off screen
self.myView.hidden = NO; // unide the view
[UIView animateWithDuration:0.5 animations:^{
self.myView.frame = orig; // animate to original position
}];
} else {
self.myView.hidden = YES; // hide when animation finished
}
}
I dont see why adding the hide logic breaks.
Could this be a memory retain cycle problem?

how to spin an image continuously

I have an wheel image in .png format, I want to know how can i animate so that it rotates continuously, I searched on stackoverflow and found certain snippets of code which helped me rotate my image but it wouldn't rotate continuously, it would just rotate for a few seconds and stop, the code as follows
the code in viewdidload
UIImageView *imageToMove =
[[UIImageView alloc] initWithImage:[UIImageimageNamed:#"horo_circle.png"]];
[self.view addSubview:imageToMove];
[self rotateImage:imageToMove duration:5.0
curve:UIViewAnimationCurveEaseIn degrees:180];
and the animation
- (void)rotateImage:(UIImageView *)image duration:(NSTimeInterval)duration
curve:(int)curve degrees:(CGFloat)degrees
{
// Setup the animation
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:duration];
[UIView setAnimationCurve:curve];
[UIView setAnimationBeginsFromCurrentState:YES];
// The transform matrix
CGAffineTransform transform =
CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(degrees));
image.transform = transform;
// Commit the changes
[UIView commitAnimations];
}
and the following lines after the import
#define M_PI 3.14159265358979323846264338327950288 /* pi */
#define DEGREES_TO_RADIANS(angle) (angle / 180.0 * M_PI)
You are better of doing this with a CABasicAnimation:
if ([self.spinnerOverlay animationForKey:#"SpinAnimation"] == nil) {
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
animation.fromValue = [NSNumber numberWithFloat:0.0f];
animation.toValue = [NSNumber numberWithFloat: 2*M_PI];
animation.duration = 10.0f;
animation.repeatCount = INFINITY;
[self.spinnerOverlay.layer addAnimation:animation forKey:#"SpinAnimation"];
}
In this code I check whether the animation is all ready set, not need to set it again.
The spinnerOverlay is in your case the UIImageView you want to rotate.
To stop the animation:
[self.spinnerOverlay.layer removeAnimationForKey:#"SpinAnimation"];
The code bellow will spin a UIImageView called iconView continiously until rotate == NO.
The image will always return to its original position.
- (void)performRotationAnimated
{
[UIView animateWithDuration:0.5
delay:0
options:UIViewAnimationOptionCurveLinear
animations:^{
self.iconView.transform = CGAffineTransformMakeRotation(M_PI);
}
completion:^(BOOL finished){
[UIView animateWithDuration:0.5
delay:0
options:UIViewAnimationOptionCurveLinear
animations:^{
self.iconView.transform = CGAffineTransformMakeRotation(0);
}
completion:^(BOOL finished){
if (_rotate) {
[self performRotationAnimated];
}
}];
}];
}
This is a variation on the same theme.
(Total duration: 2 * 0.5 = 1 sec for a spin)
Works like magic!
Here is another way to do it with a timer.
In your viewController.h file, you'll need to declare your timer and image:
#interface ViewController : UIViewController
{
IBOutlet UIImageView *spinningImage;
NSTimer *spinTimer;
}
Next, in your ViewController.m, you add this code and can make the spin go faster by lowering the time interval or by increasing the transform interval.
-(void) viewDidLoad {
spinTimer = [NSTimer scheduledTimerWithTimeInterval:.01 target: self selector:#selector(spinVoid) userInfo:nil repeats:YES];
}
-(void) spinVoid {
spinningImage.transform = CGAffineTransformRotate(spinningImage.transform, 0.001);
}
Hope this helps!
Now that we're at iOS6, please consider switching to block based animations (that are available since iOS 4.0) rather than the classical method. Try this:
[UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionRepeat animations:^{
image.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(degrees));
} completion:nil];
Using the method you've chosen for animations :
[UIView setAnimationRepeatCount:10000000];
or, you can specify the UIViewAnimationOptionRepeat flag in the blocks method animateWithDuration:delay:options:animations:completion:

I want to flash an UIImageview on the iPad, how do I do that?

I want to let a UIImageView flash several times.
Currently I don't know how to do that.
Actual code:
-(void)arrowsAnimate{
[UIView animateWithDuration:1.0 animations:^{
arrow1.alpha = 1.0;
arrow2.alpha = 1.0;
arrow3.alpha = 1.0;
NSLog(#"alpha 1");
} completion:^(BOOL finished){
[self arrowsAnimate2];
}];
}
-(void)arrowsAnimate2{
[UIView animateWithDuration:1.0 animations:^{
arrow1.alpha = 0.0;
arrow2.alpha = 0.0;
arrow3.alpha = 0.0;
NSLog(#"alpha 0");
} completion:^(BOOL finished){;}];
}
later on I call it like this:
for (int i = 0;i < 10; i++){
[self arrowsAnimate]; }
This gives me 10x alpha 1, and then 10x alpha 0. In the middle we see only one animation.
Any suggestions?
Thanks.
There is a simpler way to achieve a flashing animation using only 1 animation block:
aview.alpha = 1.0f;
[UIView animateWithDuration:0.5f
delay:0.0f
options:UIViewAnimationOptionAutoreverse
animations:^ {
[UIView setAnimationRepeatCount:10.0f/2.0f];
aview.alpha = 0.0f;
} completion:^(BOOL finished) {
[aview removeFromSuperview];
}];
The trick is to use [UIView setAnimationRepeatCount:NTIMES/2]; *_inside_* your animation block.
No need for extra functions or extra loops.
Use
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
and pass the UIViewAnimationOptionRepeat and probably UIViewAnimationOptionAutoreverse in your options. You shouldn't need to provide a completion block and only perform the first animation.
Edit: here is some sample code for an image that fades in and out indefinitely.
[UIView animateWithDuration:1.0
delay:0.0
options:(UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse)
animations:^{
self.myImageView.alpha = 1.0;
}
completion:NULL];
Edit 2: I see you actually need to flash it 10 times only. I wasn't able to do that with blocks actually. When the completion block executed, the animation seemed to complete instantly the remaining 9 times. I was however able to do this with just the old-style animations quite easily.
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationRepeatCount:10.0];
[UIView setAnimationRepeatAutoreverses:YES];
self.myImageView.alpha = 1.0;
[UIView commitAnimations];
Edit 3: I found a way to do this with blocks.
- (void)animate
{
if (self.animationCount < 10)
{
[UIView animateWithDuration:1.0
animations:^{
self.myImageView.alpha = 1.0;
}
completion:^(BOOL finished){
[self animateBack];
}];
}
}
- (void)animateBack
{
[UIView animateWithDuration:1.0
animations:^{
self.myImageView.alpha = 0.0;
}
completion:^(BOOL finished){
self.animationCount++;
[self animate];
}];
}
Above blinking may not work when your app go background and foreground at time of blinking.
Instead of these you can take a transparent image and your actual image and animate your ImageView
UIImageView *imageview=[UIImageView new];
imageview.animationDuration=1;
imageview.animationImages = your array of images;
[imageview startAnimating];
You need to wait for an animation to complete before launching a new one. You could chain your completion block in animate2 to go back to animate, and stop based on a counter property, implementing your loop in the animate/completion blocks instead of a separate loop.