I have a small UIView and want to let it move to center of screen first, and then, zoom to full screen.
But when I begin start an animation like
[UIView beginAnimations:#"animation1" context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(showDetailToFullscreen)];
self.currentDetailVC.frame = centerFrame;
[UIView commitAnimations];
And in the same controller, I have a method:showDetailToFullscreen
- (void)showDetailToFullscreen {
CGRect screenFrame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height);
[UIView beginAnimations:#"animation2" context:nil];
[UIView setAnimationDuration:0.5];
self.currentDetailVC.view.frame = screenFrame;
[UIView commitAnimations];
}
but when start, it still executed together.
I think the problem they are still in the same transaction. But how can I let these two animation executed one by one? Appreciate for any answer!
Try this instead of the setAnimationDidStopSelector:
[self performSelector:#selector(showDetailToFullscreen) withObject:nil afterDelay:0.5];
It's now considered much better to annimate views using the 'blocks' methods that can be found in the "Animating Views with Blocks" section in the UIView Class Reference.
In most of these methods you can specify a "completion" block of code that can be used to start off a second animation when the first animation completes. You could also call showDetailToFullscreen within this completion block.
I'd reccommend trying this method instead.
Are you targetting iOS 4 or later? If so, I recommend using the block-based animation methods instead. What you want to do is very easy:
Example from UIView docs:
[UIView animateWithDuration:0.2
animations:^{view.alpha = 0.0;}
completion:^(BOOL finished){ [view removeFromSuperview]; }];
Related
I have a UIButton in my code that moves steadily across the screen. Currently, when the user presses the button, the alpha changes to 0 and it just disappears. What I'd like to do is run a separate animation after the button is pressed/it has disappeared. Seems easy enough but the catch is I need to run the animation at the exact point of the button when it is pressed. And I'm drawing a blank on how to make this happen. Any help will be much appreciated! I'll post some relevant code below.
-(void)movingbuttons{
movingButton2.center = CGPointMake(x, y);
displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(moveObject)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
-(void)moveObject{
movingButton2.center = CGPointMake(movingButton2.center.x , movingButton2.center.y +1);
}
-(IBAction)button2:(id)sender {
[UIView beginAnimations:nil context:NULL];
[movingButton2 setAlpha:0];
[UIView commitAnimations];
}
Replace your button2 action with below code and implement the someOtherMethodWithAnimation method with the animation you want :)
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1];
movingButton2.alpha = 0;
[UIView commitAnimations];
[self performSelector:#selector(someOtherMethodWithAnimation) withObject:nil afterDelay:1.0];
Replace your animation with:
[UIView animateWithDuration:0.25
animations:^{
self.movingbutton2.alpha = 0.0;
// modify other animatable view properties here, ex:
self.someOtherView.alpha = 1.0;
}
completion:nil];
Just a nit picking point, but is the button in your view controller's .xib file correctly hooked up to your IBOutlets and IBActions?
UPDATE:
You are not limited to modifying that one button in your method. You add what ever code you want in the animation block (see the udated axample above).
UIView animatable properties, there is an animation section there.
Another approach could be (I am just using alpha as an example):
[UIView animateWithDuration:1.0
animations:^{
self.movingButton2.alpha = 0.0;
}
completion:^{
[UIView animatateWithDuration:0.25
animations:^{
self.someOtherView.alpha = 1.0;
}
completion:nil];
}];
This will more likely guarantee the animations will hapen one after another.
I have a UIScrollView which contains various subviews (UIImageViews, UILabels and standard UIViews). Some of the UIImageViews are partially covered by other UIViews.
However, when I fade out the UIScrollView, the partially covered parts of the UIImageViews are being exposed for the brief moment of the animation.
I want to be able to fade the scrollview and all it's contents at the same time in the same animation - i.e. not revealing any of the partially covered images.
If it's not possible, I can always add a UIView on top of all the other controls and fade it from alpha 0 upto 1 to hide everything, but I'm sure there's a way to perform a complete fade on a view and all it's subviews.
I tried this:
[UIView beginAnimations:nil context:NULL];
[scrollViewResults setAlpha:0.0f];
[UIView commitAnimations];
And I've tried this:
- (IBAction)questionGroupChanged:(UIButton*)sender {
[UIView beginAnimations:nil context:NULL];
[self fadeViewHierarchy:scrollViewResults toAlpha:0.0f];
[UIView commitAnimations];
}
- (void)fadeViewHierarchy:(UIView*)parentView toAlpha:(float)alpha {
[parentView setAlpha:alpha];
for (UIView *subView in parentView.subviews) {
[self fadeViewHierarchy:subView toAlpha:alpha];
}
}
But I've still not cracked it. Any ideas?
This happens because of the way the compositor works. You need to enable rasterization on the view's layer when fading it in/out:
view.layer.shouldRasterize = YES;
You should probably only enable this for the duration of the animation because it will take up some extra memory and graphics processing time.
Mike's answer is the correct one and he deserves all credit for this. Just to illustrate, it might look like:
- (void)fadeView:(UIView*)view toAlpha:(CGFloat)alpha
{
view.layer.shouldRasterize = YES;
[UIView animateWithDuration:0.75
animations:^{
view.alpha = alpha;
}
completion:^(BOOL finished){
view.layer.shouldRasterize = NO;
}];
}
Thus, using your scrollViewResults, it would be invoked as:
[self fadeView:scrollViewResults toAlpha:0.0f];
Did you try with UIView class methods +animateWithDuration:* (available on iOS 4 and +)
Like :
- (void)fadeAllViews
{
[UIView animateWithDuration:2
animations:^{
for (UIView *view in allViewsToFade)
view.alpha = 0.0;
}
completion:^(BOOL finished){}
];
}
I am trying to perform multiple UIView animations one after the other. However, I've heard that it's bad practice to perform multiple UIView animations one after the other, and that I should instead use Core Animation. I tried this code:
//First Animation
[UIView beginAnimations:#"animation1" context:nil];
[UIView setAnimationDuration:2];
nwView.backgroundColor = [UIColor redColor];
nwView.frame = CGRectMake(CGRectGetMidX(screenSize),
CGRectGetMinY(screenSize),
width,
height);
nwView.transform = CGAffineTransformMakeRotation(45.0f);
[UIView commitAnimations];
//Second Animation
[UIView beginAnimations:#"second animation" context:nil];
[UIView setAnimationDuration:2];
nwView.transform = CGAffineTransformMakeScale(0.5, 0.33);
nwView.backgroundColor = [UIColor purpleColor];
[UIView commitAnimations];
But it only does the second animation. I know this question is similar to UIView two animations coexisting, but it has a slightly different context.
I don't think there is anything wrong with doing 2 animations in a row using UIView blocks. Just make sure you start your second animation in the completino block of the first animation.
Without blocks (your example) it is not working as you will have to set a delegate to the animation or set a selector for setAnimationDidStopSelector. There you should start the second animation.
But again, nothing wrong in doing animations with blocks (it is the preferred way).
If you are looking to do multiple animations right after another this is not the way to do it. The code you posted with execute at almost the same time, meaning the animations would be performed at about the same time.
Instead what you should do is set the delegate for the first animation, and then do the first animation. Then when the animationDidStop method is called for the first animation, you should do the second animation. This makes sure they are one after another.
This is how you would do it, assuming you call doMyAnimations to start the animation.
-(void)doMyAnimations{
//First Animation
[UIView beginAnimations:#"animation1" context:nil];
[UIView setAnimationDuration:2];
[UIView setAnimationDelegate:self];
nwView.backgroundColor = [UIColor redColor];
nwView.frame = CGRectMake(CGRectGetMidX(screenSize),
CGRectGetMinY(screenSize),
width,
height);
nwView.transform = CGAffineTransformMakeRotation(45.0f);
[UIView commitAnimations];
}
- (void)animationWillStart:(NSString *)animationID context:(void *)context{
}
- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context{
if([animationID isEqualToString:#"animation1"]){
//Second Animation
[UIView beginAnimations:#"second animation" context:nil];
[UIView setAnimationDuration:2];
nwView.transform = CGAffineTransformMakeScale(0.5, 0.33);
nwView.backgroundColor = [UIColor purpleColor];
[UIView commitAnimations];
}
}
Keep in mind that the nwView would have to be accessible throughout the entire class. If it isn't you can either make it an instance variable, or find another way to get access to it in the animationDidStop method.
You can use blocks for this purpose and get a very clean result.
NSMutableArray* animationBlocks = [NSMutableArray new];
typedef void(^animationBlock)(BOOL);
// getNextAnimation
// removes the first block in the queue and returns it
animationBlock (^getNextAnimation)() = ^{
animationBlock block = (animationBlock)[animationBlocks firstObject];
if (block){
[animationBlocks removeObjectAtIndex:0];
return block;
}else{
return ^(BOOL finished){};
}
};
//add a block to our queue
[animationBlocks addObject:^(BOOL finished){;
[UIView animateWithDuration:1.0 animations:^{
//...animation code...
} completion: getNextAnimation()];
}];
//add a block to our queue
[animationBlocks addObject:^(BOOL finished){;
[UIView animateWithDuration:1.0 animations:^{
//...animation code...
} completion: getNextAnimation()];
}];
//add a block to our queue
[animationBlocks addObject:^(BOOL finished){;
NSLog(#"Multi-step Animation Complete!");
}];
// execute the first block in the queue
getNextAnimation()(YES);
Taken from: http://xibxor.com/objective-c/uiview-animation-without-nested-hell/
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelay:4];
[UIView setAnimationDuration:5];
navigation.frame = CGRectOffset(navigation.frame, 0, 430);
[UIView commitAnimations];
Code block above works fine when called manually on viewDidLoad function or with button tap. But when i try to call that block within a selector function like below, animation doesnt effect. Object jumps to position. What would cause to that?
if ([delegate respondsToSelector:#selector(carouselDidEndScrollingAnimation:)])
{
[delegate carouselDidEndScrollingAnimation:self];
}
- (void) carouselDidEndScrollingAnimation:(iCarousel *)carousel{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:5];
navigation.frame = CGRectOffset(navigation.frame, 0, 430);
[UIView commitAnimations];
}
icarousel github
Still don't know the real problem but
[self performSelector:#selector(makeAnim) withObject:nil afterDelay:0.0001];
solved my problem.
Hope helps anyone.
Still needs a better solution.
Below is code that I'm using to swap views
MySecondViewController *tempVC = [[MySecondViewController alloc] initWithNibName:#"MySecondView"];
[self presentModalViewController:tempVC animated:YES];
[tempVC passDataWithString:#"a string" andColor:yellowcolor];
How can I get the cool page turning animation (as with iBook or ROAD RAGE SIGN)
I don't want the user to actually tap and drag his finger on the screen. I want him to push a button and the animation occurs by itself.
Have you looked at UIViewAnimationTransition values? Use them in a UIView animation block. eg:
[UIView beginAnimations:nil context:nil];
[UIView setAnimationTransition: UIViewAnimationTransitionCurlUp
forView:self.view cache:YES];
[UIView commitAnimations];