iPhone - UIView Animation not looping correctly - iphone

Hey all, got a little problem here and can't figure out what I am doing wrong. I am trying to animate a UIView up and down repeatedly. When I start it, it goes down correctly and then back up but then immediately shoots to the "final" position of the animation. Code is as follows:
UIImageView *guide = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image.png"]];
guide.frame = CGRectMake(250, 80, 30, 30);
[self.view addSubview:guide];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:2];
[UIView setAnimationRepeatAutoreverses:YES];
[UIView setAnimationBeginsFromCurrentState:YES];
guide.frame = CGRectMake(250, 300, 30, 30);
[UIView setAnimationRepeatCount:10];
[UIView commitAnimations];
Thanks in advance!

This is an old question, but in case anyone is looking at this you can just use a fractional repeat count to end on the desired state. If you're animating from A to B and using auto reverse, each repeat will look like this A -> B -> A. Since you want to end on B on the last animation and not A, just do this to make the last repeat only go halfway (to B):
[UIView setAnimationRepeatCount:10.5];
That's it!

It's a 'feature', not a bug. It's really annoying, but it looks like unless Apple says otherwise, this is how UIView Animations are expected to work.
See this answer: setAnimationRepeatAutoreverses not behaved as I expected

You may set setAnimationDidStopSelector to a custom function and in the custom function you can set up more animations (even recursively). See my answer to another question here.
Usually I will define an animation structure (AnimationPath in the example) to coordinate the whole animation (say, by a counter).

Related

Animation not Shown When Calling it First Time

I have one simple animation:
- (void)showMenu
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationDelay:0.0];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
self.scrollView.frame = CGRectMake(296, -150, 432, 356);
[self.buttonMenu setImage:[UIImage imageNamed:#"menu_arrow_up.png"] forState:UIControlStateNormal];
[self.view bringSubviewToFront:self.scrollView];
[UIView commitAnimations];
}
When I call this animation the first time, just the image of my button changes,
the position of my scrollview does not change.
Everything works correctly, after I called my showMenu method twice.
I call showMenu. the image of the button changes. position of scrollview not
I call another method to reset everything. image of button changes. position of scrollview not
I call showMenu. the image of the button changes. position of scrollview changes
from now on it works every time I call showmenu
the second method is closeMenu:
- (void)closeMenu
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationDelay:0.0];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
self.scrollView.frame = CGRectMake(296, -317, 432, 356);
[self.buttonMenu setImage:[UIImage imageNamed:#"menu_arrow_down.png"] forState:UIControlStateNormal];
[UIView commitAnimations];
}
So why does it work just when I click it twice?
I had the same problem when i perform a UIAnimation and simultaneously adding a UITableView programmatically. The frame of UIView changes but it will not update in display.
To solve this problem call your method using performSelector:withObject:afterDelay: with a delay of 0.
If you have autolayout turned on, you may want to wary about changing the frame of a view. Unfortunately, when the constraints are reapplied (which triggered by a myriad of seemingly innocuous actions, such as setting a text label, etc.), your manually set frame will be replaced with values governed by the constraints.
You're probably not seeing this problem manifest itself the second time time you call this routine because you're probably not doing anything immediately thereafter that causes constraints to be reapplied. Even with autolayout on, you can sometime change a frame manually, and it will work, but as soon as constraints are reapplied, the frame may get reset.
There are two solutions:
If using auto layout, you can animate the changing of constraint constants. See the latter part of this answer for an example of how to create an IBOutlet for constraint and then changing the constant property within an animation block.
You can also simply disable autolayout.

Load a XIB with custom Transition?

Is it possible (at all) to load a XIB with custom AnimationTransition?
What I have done, is creating an animation that "Covers" the screen, and what I want is that after that animation has done playing, I would like it to display the new XIB.
I cant seem to find any proper solution to this...Any ideas?
To be more clear: Press a button --> Play Animation (cover screen) --> Load XIB.
Hello again! Yes, the last way you described is the way I am doing it. I have two UIViews (Might be wrong already there), that are placed off-bounds on each side, (like x.-160.y.0 and x.320y.0)
-(IBAction) leftDoor{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1];
[UIView setAnimationDelegate:self];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
closeDoor1.center = CGPointMake( closeDoor1.center.x +160, closeDoor1.center.y);
[UIView commitAnimations];
}
-(IBAction) rightDoor{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1];
[UIView setAnimationDelegate:self];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
closeDoor2.center = CGPointMake( closeDoor2.center.x -160, closeDoor2.center.y);
[UIView commitAnimations];
}
So, what I am going to do is not to "split" the current view and then open a new XIB, the effect I am searching for is a "closing door" effect, thats why I used UIView ( thought I place graphics on top of those, like two ImageViews). Then, for loading the new XIB...This is where im really puzzled. My first way of trying this out was to make three IBActions, including the two I mentioned above, and then apply all three (multiple actions) to a button. So for switching views I did something like this: `-(IBAction) newViewDisplay:(id)sender{
theView *newViewController = [[theView alloc]
initWithNibName:#"theView" bundle:nil];
[self.view addSubview:newViewController.view];
}
`
As you said, this might be over my head, but if I just got some directions, I´ll walk miles to make this work. It would really give my app a facelift. A huge thanks for taking time to answer my question, All the best/Andy
What are you covering the screen with?
Think of it this way, (it sounds like) you have 2 views, the old one and the new one that is stored in this xib. The animation is secondary.
You need to load the new view, then display it (can be off-screen), then animate it (move it) into the place you want it to go. If you want to split it into two parts, one at the bottom and one at the top of the screen, then meet in the middle, I think that's both complicated and above you skill level. (Not trying to be mean here).
If you are trying to do a split animation as described, it can be done, but you need to 'fake it'. It would involve taking a 'screenshot' (of sorts), splitting it, moving these two images so they meet with an animation, loading the view underneath, and then removing the images. Tricky stuff.
Do you have to have this sort of animation?
If you could post the code you have, I can rearrange it, and add to it, for you.
You'll need to clarify what it is exactly you want to do, though.
UPDATE 3: I added another option for autoreverse.
//You can add both doors into one animation block.
//Create an animation block. Ease Out is better for this animation but you can change it.
[UIView animateWithDuration:1.0 delay:0.0 options:(UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAutoreverse) animations:^{
closeDoor1.center = CGPointMake( closeDoor1.center.x +160, closeDoor1.center.y);
closeDoor2.center = CGPointMake( closeDoor2.center.x -160, closeDoor2.center.y);
}completion:^(BOOL finished){
if (finished) {
//When finished, create the VC if it doesn't exist, and insert it below the 'closed door'.
if (newViewController == nil) {
UIViewController *newViewController = [[UIViewController alloc] initWithNibName:#"theView" bundle:nil];
}
[self.view insertSubview:newViewController.view belowSubview: closeDoor2];;
[self.closeDoor1 removeFromSuperview];
[self.closeDoor2 removeFromSuperview];
}
}];

Flip animation is ignoring duration, i.e. I can't control speed

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.

iPhone: How to resize a png file during an animation

Is there any way to resize (in my case increase the width) of an PNG file in an animation? This would need to result into a distortion of the original PNG (in my case, I try to get the PNG fatter).
My code so far is:
pageShadow = [UIImage imageNamed:#"pageshadow.png"]; // load our image resource
pageShadowView = [[UIImageView alloc] initWithImage:pageShadow];
[UIView beginAnimations:#"shadowMove" context:nil]; // Begin animation
[UIView setAnimationDuration:4.5];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
//[pageShadowView setFrame:CGRectMake(0, 0, CGRectGetWidth(pageShadowView.frame), CGRectGetHeight(pageShadowView.frame))];
[UIView commitAnimations];
But I can't think of a command to increase the width of my PNG. I guess this is not possible as long as it is confined to an UIView, right? I'd be grateful for any suggestions of how to tackle this problem. Thanks, and sorry if this is really obvious or basic.
to do what you want you probably want to add a transform to the view.
try something like
[pageShadowView setTransform:CGAffineTransformMakeScale (2, 1)];

Animating UILabel Fade In/Out

I am going to end up with an array of RSS feeds, and would like a label or some such to display them at the bottom of the view. I would like to animate through each feed in the array.
This is what i have so far to animate, which, works for the fade, but only animates the last item of the array.
feed = [[UILabel alloc] initWithFrame:CGRectMake(0,380,320,43)];
[self.view addSubview:feed];
feed.alpha=1;
NSArray *feeds = [NSArray arrayWithObjects:[NSString stringWithFormat:#"1234567"],[NSString stringWithFormat:#"qwerty"],[NSString stringWithFormat:#"asdfgh"],nil];
for (NSString* f in feeds){
feed.text=f;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
[UIView setAnimationDuration:2.0f];
feed.alpha=0;
[UIView setAnimationDidStopSelector:#selector(animationDidStop:finished:context:)];
[UIView commitAnimations];
}
Im sure its simple.
Thanks
First off you should really consider a better naming convention. Calling a UILabel a feed isn't very helpful for the future when you have to come back and look at your code. I would name it feedLabel. Then when you iterate through your list of feeds, you can just for (NSString *feed in feeds) and it will make more sense. And so will feedLabel.text = feed;.
Anyhow, the issue I see with your code is that your are repeatedly setting the alpha to zero in your loop, but you never set it back to one. In other words, you're not making a change to the alpha value. It stays the same in every iteration.
So maybe you could clarify what you're trying to do. If you want to fade the text between changes in the text, you'll need a different animation and methodology. Instead of a loop, chain your animations such that when your didStopSelector, you set the text and start the next one. Something like:
- (void)performAnimation;
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
[UIView setAnimationDuration:2.0f];
feed.alpha=0;
[UIView setAnimationDidStopSelector:#selector(animationDidStop:finished:)];
[UIView commitAnimations];
}
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
feed.alpha = 1.0;
NSString *nextFeed = [self getNextFeed]; // Need to implement getNextFeed
if (nextFeed)
{
// Only continue if there is a next feed.
[feed setText:nextFeed];
[self performAnimation];
}
}
i tried your code, it fades out on first feed, but it doesn't enter the animationDidStop event. thats why it cant call performAnimation again. is there any set for animation (delegate or protocol etc..)