I try to use animationWithDuration and transitionWithView to create one big animation. I want to do something like this :
Move view A with animationWithDuration
After step1 is completed, move view B with animationWithDuration
After step2 is completed, show view C with transitionWithView (With OptionCurlDown)
I really look over the net, and i don't know how to correclty do that. My problem is that the step 3 start always at the same time as step A. If i do only step 1 and step 2, this is ok, i mean, step 2 starts only when step 1 is done. But i had no luck with step 3!
I also tried to nest transitionWithView inside an animationWithDuration, but the result is the same,
THank you
Update
The following is the code itself :
Main function:
[fileMenuController hide:0.2 andDelay:0.1];
[drawingToolController show:0.2 andDelay:0.2];
[penSizeMenuController showSubViewWithDuration:0.4];
fileMenuController hide function:
[UIView animateWithDuration:duration //begin animation
delay:delay
options:UIViewAnimationCurveEaseIn
animations:^{
[self.view setFrame:CGRectOffset([self.view frame], 0, -self.view.frame.size.height)];
}
completion:nil
];
drawingToolController show function:
[UIView animateWithDuration:duration //begin animation
delay:delay
options:UIViewAnimationCurveEaseIn
animations:^{
[self.view setFrame:CGRectOffset([self.view frame], 0, self.view.frame.size.height)];
}
completion:nil
];
penSizeController show function:
[UIView transitionWithView:self.view
duration:duration
options:UIViewAnimationOptionTransitionCurlDown
animations:^{ [self.view addSubview:subView] ;}
completion:nil];
self.view.alpha = 1;
penSizeController show always starts at the same time as fileMenuController hide
Update to resolve the problem
Following the idea of user523234 , i did that :
MainFunction
[fileMenuController hide:0.2 andDelay:0.1];
[drawingToolController show:0.2 andDelay:0.2];
[self performSelector:#selector(delayedPenSizeMenuShow)withObject:nil afterDelay:0.4)
MainFunction (newFunction)
-(void) delayedPenSizeMenuShow{
[penSizeMenuController showSubViewWithDuration:0.4];
}
This way, it works, penSizeMenuController is called after the 2 animations. But i am wondering is it is ok with the new block base philosophy with ios 4.0?
But well, at least i have somethin..
Related
I'm trying to animate a label embedded in a UIView.
this is the code :
-(void)displayText:(NSString*)text {
[label setText:text];
[UIView animateWithDuration:5.0
delay:0.0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
[labelView setAlpha:1.0];
}
completion:nil
];
[UIView animateWithDuration:5.8
delay:0.0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
[labelView setAlpha:0.0];
}
completion:nil
];
}
To verify, the method is called, i set a breakpoint.
The calls return immediatly but only the end of animations is displayed.
I wired the UIView to the controller.
Pls help, I'm stuck.
Thanks in advance !
Patrick
Correct,
When you animate views like this the animation doesn't actually happen on screen until the next pass of the runloop (i.e. once your method returns).
UIView will coalesce animations that are programmed sequentially.
Use the completion block to fade back out. The code looks a bit odd but it works great!
[UIView animateWithDuration:5.0
delay:0.0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
[labelView setAlpha:1.0];
}
completion:^(BOOL completed){
[UIView animateWithDuration:5.8
delay:0.0
options:UIViewAnimationOptionCurveEaseIn
animations:^{[labelView setAlpha:0.0];}
completion:nil];
}];
In response to your comments:
The animations won't start until the next run of the runloop. They won't start until your app finishes what its doing. If you wait in the loop you will have the same problem and also freeze up your interface. Consider using individual labels for each letter, and add a progressively bigger delay for each animation. All these animation instructions will be queued up at once and then played out over the course of the next however many seconds. Imagine you are like a movie director, you tell each actor what to do in the next scene. Then, once everyone knows what to do you sit back and yell "action" and watch it all play out.
UPDATE
: (I redid my functions so all the animations won't be as nested as before. Still no luck)
I have a piece of code where a try to, in this order:
Hide view A
Show view B
Show view C
The order is important!
The code is the following :
Main function:
[fileMenuController hide:0.2 andDelay:0.1];
[drawingToolController show:0.2 andDelay:0.2];
[penSizeMenuController showSubViewWithDuration:0.4];
fileMenuController hide function:
[UIView animateWithDuration:duration //begin animation
delay:delay
options:UIViewAnimationCurveEaseIn
animations:^{
[self.view setFrame:CGRectOffset([self.view frame], 0, -self.view.frame.size.height)];
}
completion:nil
];
drawingToolController show function:
[UIView animateWithDuration:duration //begin animation
delay:delay
options:UIViewAnimationCurveEaseIn
animations:^{
[self.view setFrame:CGRectOffset([self.view frame], 0, self.view.frame.size.height)];
}
completion:nil
];
penSizeController show function:
[UIView transitionWithView:self.view
duration:duration
options:UIViewAnimationOptionTransitionCurlDown
animations:^{ [self.view addSubview:subView] ;}
completion:nil];
self.view.alpha = 1;
My problem is the block penSizeController showSubView starts with the first animation (fileMenuController hide)!
The first two animations (fileMenuController hide and drawingToolController show) are working properly. When fileMenuController hide is done, drawingToolController starts.
So, does somebody know why the part in the penSizeController showSubView block starts at the same time as the first animation?
I'd imagine it's because the outer animation block doesn't have any animation – because the hide and show creates inner animation blocks – so it immediately calls the completion block.
Either remove the nested animation block in hide and show or add a parameter that disables animation for these nested animation actions.
There are two main documented methods of animating UIViews. One, is a deprecated process in which one makes multiple calls beginning with method beginAnimations:context: and the other, newer, suggested approach is block-based.
I have the following code in my application. However, only the older deprecated animation segment works. The newer, block-based approach works the first time, but every subsequent time skips directly to the end of the animation and shows me only the final frame immediately. Has anybody had any experienced with this?
-(void)updateImageViewSlider:(UIImage *)image {
mImageFeedSwipe.alpha = 0.0;
[mImageFeedSwipe setHidden:NO];
mImageFeedSwipe.frame = DEFAULT_IMAGEVIEW_RECT;
[mImageFeedSwipe setImage:image];
//
// The following animation code works fine.
//
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
mImageFeedSwipe.alpha = 1.0;
[mImageFeedSwipe setFrame:NEW_IMAGEVIEW_RECT];
[UIView commitAnimations];
//
// The following DOES NOT work except on the first run
//
int animationOptions = 0
| UIViewAnimationOptionCurveEaseInOut
| UIViewAnimationOptionBeginFromCurrentState
| UIViewAnimationOptionAllowUserInteraction;
[UIView animateWithDuration:1.0
delay:0
options:animationOptions
animations:^{
// Bring in the swiping image view...
mImageFeedSwipe.alpha = 1.0;
[mImageFeedSwipe setFrame:NEW_IMAGEVIEW_RECT];
}
completion:nil];
}
This is being run on the main thread via [self performSelectorOnMainThread...].
I was seeing this same problem until I realized that in between animations I was hiding the view and never changing view.hidden to NO again. Any chance that you are hiding your view or setting alpha to 0.0 in between animations?
So I attempt to flip between two images, but the duration is seemingly ignored.
I understand that Apple has to do some animation very quickly to keep the magic alive, but is there a way to slow down the flipping?
My current object hierarchy is game board contains many cards which inherit from UIView.
A card has a face and back that are UIViews that host a UIImage and other data
[UIView animateWithDuration:15.0
animations:^(void) {
cardOne.center =CGPointMake(100, 100);
cardTwo.center =CGPointMake(100, 100);
//low level calls, to directly perform the image swap
[cardOne.face removeFromSuperview];
[cardOne addSubview:cardOne.back];
[cardOne sendSubviewToBack:cardOne.face];
//calling the transitionFromView to do the image swap
[UIView transitionFromView:cardOne.face
toView:cardOne.back
duration:10
options:UIViewAnimationTransitionFlipFromLeft
completion:^(BOOL finished){ }];
Thanks for your feedback. I attempted a mix of all you input and it did end up working correctly. I don't yet know why though.
Thanks for your responses. I tried reverting to the old style and it worked. I have not figured out why as yet.
The non-working code is commented out and the working code is left uncommented.
Thanks again
/*
[UIView animateWithDuration:animationDuration
animations:^{
[self.face removeFromSuperview];
[self addSubview:self.back];
[self sendSubviewToBack:self.face];
}];
*/
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:kDefaultFlipSpeed];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft
forView:self
cache:YES];
[self.face removeFromSuperview];
[self addSubview:self.back];
[self sendSubviewToBack: self.face];
[UIView commitAnimations];
I cant ask in a comment, so:
Have you tried with less than 10 seconds? More precisely between 0-1 second. Something like 0.5 seconds. Tell me if you notice any change in duration with these values.
My quick answer would be:
Try setting 10 seconds as 10.0 or 10.0f.
Hope it helps.
I am working on one iPhone application in which I implemented one animation UIViewAnimationTransitionFlipFromLeft. Here my application works fine in the Portrait mode. It is doing the same animation as specified means Flip from Left to Right.
But when I am doing this UIViewAnimationTransitionFlipFromLeft in landscape mode then it is not rotating from left to right. Instead of it is rotating from top to bottom. This is really critical issue. Can you help me out to solve this.
The code I am using for iPhone application to rotate the view is as follows:
CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context];
[UIView setAnimationTransition: UIViewAnimationTransitionFlipFromLeft forView:self.view.window cache:NO];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:1.0];
[UIView commitAnimations];
[self.navigationController pushViewController:objSecond animated:YES];
Thanks,
Best Regards,
Gurpritsingh Saini
If you are using iOS 4.0 or later, the following will do exactly what you want (I just tested it out to make sure)
NewView *myNewView = [[NewView alloc] initWith.....];
[UIView transitionFromView:self.view toView:myNewView.view duration:1 options:UIViewAnimationOptionTransitionFlipFromLeft completion:nil];
//[self.navigationController pushViewController:myNewView animated:NO];
[myNewView release];
EDIT: I'm changing the above code a bit (nothing new, just commenting out the navigation controller because it's not necessary for this).
So there are several ways to go about this (as far as keeping track of the next view), but this is the easiest I can think of. You can already switch from view 1 to 2, so I'm going to explain how to get from 2 to 10 (or however many you need).
Basically, the view transition lasts too long for viewDidLoad to catch a call to go to the next view. So what we need to do is set up a timer that waits and sends a method to switch at a later time. So this is the code you would see in view 2 (and 3 and 4, etc.).
- (void)viewDidLoad {
// this gets called before animation finishes, so wait;
self.navigationController.delegate = self;
// you will need to set the delegate so it can take control of the views to swap them;
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(switchView) userInfo:nil repeats:NO];
}
I only wait 1 second until I call the switch method, but if you are loading a lot into your views, you may want to wait a bit longer. 1.5 seconds should be more than enough, but you can play around with that to see where it works and doesn't work.
Next, you have to call the next view in the switchView method.
- (void)switchView {
NextView *myNextView = [[NextView alloc] initWith ... ];
[UIView transitionFromView:self.view toView:nextView.view duration:1 options:UIViewAnimationOptionTransitionFlipFromLeft completion:nil];
[nextView release];
}
This worked perfectly for me. Just to make sure I was getting new views, I assigned tags to each view and added UILabels as subviews in each view's viewDidLoad method and each showed the number of its view. So hopefully this is what you needed. I'm sure you have more complex things you will need to do, but this will give you the animation and logic you need to get the look you want. (on a side note, viewDidAppear doesn't seem to get called when doing this, so it might be necessary to call it manually from viewDidLoad if you really need to use it, but otherwise it works fine)
You will have to manually add a transformation to your view; the flip transformation always operates as if the view controller were in portrait orientation.
Note that the context argument to +beginAnimations:context: is not meant to be a CGContextRef per se. You probably don't want to pass the current graphics context there. Pass NULL instead.
Try with this :
CABasicAnimation *rotateAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.y"];
CGFloat startValue = 0.0;
CGFloat endValue = M_PI;
rotateAnimation.fromValue = [NSNumber numberWithDouble:startValue];
rotateAnimation.toValue = [NSNumber numberWithDouble:endValue];
rotateAnimation.duration = 1.5;
[self.view.layer addAnimation:rotateAnimation forKey:#"rotate"];
CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context];
[UIView setAnimationTransition: UIViewAnimationTransitionFlipFromLeft forView:self.view cache:NO];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:1.0];
[UIView commitAnimations];
I think this will work out.
The Accepted answer by slev did not work for me, I got all sorts of errors after trying the code in my custom Segue. I found a method that not only works but is more precise as it doesn't require the use of a Timer. Here is MySegue.m:
#implementation FlipRightSegue
- (id)initWithIdentifier:(NSString *)iden source:(UIViewController *)sour destination:(UIViewController *)dest
{
self = [super initWithIdentifier:iden source:sour destination:dest];
return self;
}
- (void)perform
{
UIViewController *src = [self sourceViewController];
UIViewController *dst = [self destinationViewController];
//[UIView transitionFromView:src.view toView:dst.view duration:1 options:UIViewAnimationOptionTransitionFlipFromRight completion:nil];
//[UIView commitAnimations];
[UIView transitionWithView:src.navigationController.view duration:0.8 options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{
[src.navigationController pushViewController:dst animated:NO];
}
completion:NULL];
}
#end
I also have a second class for flips to the right.
Code was taken from this website: http://www.dailycode.info/Blog/post/2012/11/29/iOS-Custom-Flip-Segue-(portrait-and-landscape-layout)-xcode-4.aspx