I am adding some basic animation to a card game I'm working on. (My first iPhone app.)
I am creating a custom UIView class "AnimationContainer", which flips from image1 to image2, while moving from rect1 to rect2. My ultimate intention is to have up to four of these containers doing their transitions simultaneously.
The problem I'm having is that the animation isn't showing image1... so only the last half of the flip transition appears.
However, if I reset the animation first by touching Reset, then everything works perfectly. In other words, if I press Flip again and again, I only get half the transition... but if I press Reset first, then everything works perfectly for one flip.
So, how can I get the animation to reset itself correctly?
Below is the code, a screenshot, and here's a link to the complete: Project Zip File 700k.
- (void)displayWithImage1 { //RESET button calls this
self.frame = rect1;
[image2 removeFromSuperview];
[self addSubview:image1];
[self setNeedsDisplay]; //no help: doesn't force an update before animation
}
- (void)runTheAnimation { //FLIP button calls this
[self displayWithImage1]; //<---this is what the reset button calls
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
[UIView setAnimationTransition:transition forView:self cache:NO];
self.frame = rect2;
[image1 removeFromSuperview];
[self addSubview:image2];
[UIView commitAnimations];
}
Thanks!
You need a drawing loop to pass in order to redraw the view before performing the animation. This code is an example of "draw this, and when the next event loop comes around, do this other thing." It's not uncommon to do this in UI code. Your first work-around is attempting the same thing, but in a much more complicated way.
- (void)_runTheAnimation {
// Moved here from -runTheAnimation
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
[UIView setAnimationTransition:transition forView:self cache:NO];
self.frame = rect2;
[image1 removeFromSuperview];
[self addSubview:image2];
[UIView commitAnimations];
}
- (void)runTheAnimation { //FLIP button calls this
[self displayWithImage1];
[self performSelector:#selector(_runTheAnimation) withObject:nil afterDelay:0.0];
}
Related
I have used this code:
[[self.view viewWithTag:100] setTransform:CGAffineTransformMakeScale(0.009, 0.009)];
UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
[UIView setAnimationRepeatAutoreverses:NO];
[UIView setAnimationRepeatCount:0];
[[self.view viewWithTag:100] setTransform:CGAffineTransformMakeScale(1, 1)];
[UIView commitAnimations];
in viewWillAppear of my popup view.
The code works fine on simulator and produces nice effect in which my view appears to grow from center, however on device the animation effect is absent its more like sleep of 0.5 and then all off a sudden view appears.
Since this view is used liberally i don't want to increase duration any help?
Actually the code sample works for me in viewDidLoad and viewWillAppear.Increased the time and animation works fine here.
Why use this method?Apple provides a new and better technique with blocks to do the view animations.
Try this
[[self.view viewWithTag:100] setTransform:CGAffineTransformMakeScale(0.009, 0.009)];
[UIView animateWithDuration:10.5 animations:^{
[[self.view viewWithTag:100] setTransform:CGAffineTransformMakeScale(1, 1)];
}];
My UIViewController gets the view it is controlling to completely rebuild itself with something like this:
[self.view rebuildYourself:bIsLandscape]; // this line is in the ViewController
The view itself then contains the following method:
-(void)rebuildYourself:(BOOL)isLandscape
{
NSArray *viewsToRemove = [self subviews];
for (UIView *v in viewsToRemove) {
[v removeFromSuperview];
}
[self addControls]; // adds lots of views
[self layoutControlsWithOrientation:isLandscape]; // frames the views
}
I would like to animate the entire transition. I have tried this:
[UIView animateWithDuration:1.0
delay:0.0
options:UIViewAnimationOptionTransitionCurlUp
animations:^{
[self.view rebuildYourself:bIsLandscape];
}
completion:^(BOOL finished){
}];
but the animation ignores the options value of UIViewAnimationOptionTransitionCurlUp and flows in from the top left corner every time.
and I have tried this:
[UIView beginAnimations:#"kkk" context:nil];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:self.view cache:YES];
[UIView setAnimationDuration:1.0];
[self.view rebuildYourself:bIsLandscape];
[UIView commitAnimations];
but in this case the transition does curl up nicely but the underlying view is blank until the transition has finished, and then the new view suddenly 'pops' into view, which spoils the effect of the transition.
Can anyone tell me the right/best way to animate a transition where the view rebuilds itself completely?
Thanks
Make two views and put the old view overtop of the new re-built view. Then 'Curl-up' the old view so that the new view is showing and remove the old view from memory.
I try to remove a view from it's superview after being animated offscreen. But it seems when I add the removeFromSuperview call after when the animation is supposed to end, the view would no animate at all but instead disappear instantly from the screen.
So why is there no animation when I add the [[self pickerView] removeFromSuperview]; to the method below ?
- (void) slidePickerOutView
{
[UIView beginAnimations:#"slidePickerOutView" context:nil];
[UIView setAnimationDelegate:self];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.2];
[UIView setAnimationDidStopSelector:#selector(slidePickerOutViewEnded:finished:context:)];
CGRect r = CGRectMake(0, 480, 320, 244);
[[self pickerView] setFrame:r];
[UIView commitAnimations];
}
- (void) slidePickerOutViewEnded:(NSString *)id finished:(BOOL) finished context:(void *) context
{
//Stop observing the 'Done' button
[[self pickerView] removeObserver:self forKeyPath:#"valueSelectDone"];
[[self pickerView] removeObserver:self forKeyPath:#"selectedValue"];
[[self pickerView] removeFromSuperview];
[self setPickerView:nil];
}
Ok, I eventually found what was causing the animation not to start. The pickerView was being observed and whenever one if it's instance variables would change of value, the parent view would get notified and would start the animation to slide the pickerview offscreen.
But since the parentView was observing both old and new value, it got notified two times, first for the old and then for the new changed value. Thus the parentView was starting the animation two times right after each other as well, apparently this was causing the animation not to start at all.
fixed like this, just by commenting out the firstline which was sending the old value to its observer each time ValueSelectDone was changing value:
//[self willChangeValueForKey:#"valueSelectDone"];
valueSelectDone = flag;
[self didChangeValueForKey:#"valueSelectDone"];
I have some code in my iPhone app like that :
//fromView is a UIImageView.
//self is a UIView.
UIGraphicsBeginImageContext(fromView.bounds.size);
[fromView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *dummyFromImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView* dummyImageView = [[UIImageView alloc] initWithImage:dummyFromImage];
[self addSubview:dummyImageView];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView: dummyImageView cache:YES]; //line:9
[UIView commitAnimations];
The dummyImageView only show but never flip, if you change line9's dummyImageView to fromView, fromView do flip, Please tell me why?
I ask this question to Apple Developer Technical Support, they said,
"The basic problem is that because of the timing of your animation there is no "previous" state for Core Animation to animate from, as the view was just added to the view hierarchy, and so when the transition is attempted, all it can do is display the "final" state.
If you instead perform the flip on the next runloop iteration, then Core Animation will have had time to create an initial state for the view's layer, and thus the flip will occur correctly. You can do this by splitting your flip method in two and using -performSelector:withObject:afterDelay: like so:
"
-(IBAction)flip {
UIImageView* dummyImageView = [[UIImageView alloc] initWithImage:fromView.image];
dummyImageView.frame = fromView.frame;
[window addSubview:dummyImageView];
[self performSelector:#selector(animate:) withObject:dummyImageView afterDelay:0.0];
}
-(void)animate:(UIView*)view {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView: view cache:YES];
[UIView commitAnimations];
}
However, since you mention that "fromView" is a UIImageView, I wonder why you are using -renderInContext: – it is more efficient to simply use the same image that "fromView" is using and assign it as the image for your new UIImageView, as this saves both CPU time and memory, especially since I notice in your sample that the image is smaller than the view you are using.
[UIView setAnimationTransition:… forView: dummyImageView cache:YES]; //line:9
The view in the -setAnimationTransition:… method should be assigned to the view that contains the change. In your case, self.
The dummyImageView itself is not changed (exterior changes such as changing superview is irrelevant), so the animation can do nothing.
how can I implement the animation we see in the iPhone Music app's coverflow screen? when you click on a small view, it flips and scales up to another view? how can I do this? I can use core animation to flip and scale a view, but how can I do the transition to another view? thanks
You need an UIView as Container for the two UIViews (frontside/backside) and then remove/add these from/to the container as subviews while doing the animations in between:
UIView *flipContainer;
UIView *frontSide;
UIView *backSide;
//...
-(void)turnUp
{
[backSide removeFromSuperview];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:flipContainer cache:YES];
[UIView setAnimationDuration:1.0];
CGAffineTransform transform = CGAffineTransformMakeScale(1.2, 1.2);
flipContainer.transform = transform;
[UIView commitAnimations];
[flipContainer addSubview:frontSide];
}
-(void)turnDown
{
[frontSide removeFromSuperview];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:flipContainer cache:YES];
[UIView setAnimationDuration:1.0];
CGAffineTransform transform = CGAffineTransformMakeScale(1, 1);
flipContainer.transform = transform;
[UIView commitAnimations];
[flipContainer addSubview:backSide];
}
I'm trying the exact code you are doing - I get a zoom effect but no turn over. The only difference is that right before the turnUp code I add the flipContainer (with back showing) so then it can be flipped over.
// construct animation container
self.flipContainer = [[FlipContainer alloc] init];
[self.flipContainer.view setFrame:CGRectMake(clickedSquareX, clickedSquareY, 200, 200)];
[self.flipContainer.view addSubview:self.backside.view];
// add animation container
[self.myParentView.view addSubview:self.flipContainer.view];
// PROCEED to your turnUp code
The reason I'm doing this is I have a bunch of images in a horizontal UIScrollView and so to 'simulate' a 200x200 image flipping over and zooming to show detail I add my flipContainer with the backside showing the exact image over the exact spot of the pressed image. It should work shouldn't it? A bit confusing to me is the first line of your turnUp code you do:
[backSide removeFromSuperview];
..which would remove the view I just added.
I'm not sure if this is the right spot to put this question in - sorry if it isn't!