Choppy UIView animation - ios

I use a UIView animation to randomly animate 5 squares (UIButtons) around the screen. Depending on a user selection, there are anywhere from 2 to 5 squares visible. When only 2 are visible, the other three's hidden values get set to YES, so they are actually still animating (right?), they just aren't visible. But when only 2 are visible, the animation is smooth, but when all five are visible, the animation gets choppy. I'm not really sure how to describe it, because the squares are still moving at the correct speed and moving to the correct points; the choppiness isn't terrible, just bad enough to be noticeable. Is there any way to get rid of it? This is the code I use to animate the squares:
Edit: changed animations to block:
[UIView animateWithDuration:animationSpeed
delay:0
options:UIViewAnimationOptionCurveLinear
animations:^{
view.center = destPoint;
}
completion:^(BOOL finished){
if([view isEqual:squareThree])
[self moveBadGuys];
}
];
/*for(UIButton* button in squareArray) {
if(!shouldMove)
return;
[UIView beginAnimations:#"b" context:nil];
[UIView setAnimationDuration:animationSpeed];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
view.center = destPoint;
[UIView commitAnimations];
}*/
Edit: the view presenting this is the third in a stack of three UIViewController presented with
ViewController* controller = [[[ViewController alloc] init] autorelease];
[self presentModalViewController:controller animated:NO];
Does this way of presenting views eat up memory?

There are a few things that can cause this. It always comes down to how complex the content is. Also, simulator can be really bad about handling animation, so be sure you are testing on real hardware.
Are there large images on the buttons? Are the buttons casting shadows? Those things can slow it down.
Also- use block based animation. Not the old begin-commit methods.

Not exactly sure why it's slow, but have you tried nesting the thing differently?
if(!shouldMove)
return;
[UIView beginAnimations:#"b" context:nil];
[UIView setAnimationDuration:animationSpeed];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
for(UIButton* button in squareArray) {
view.center = destPoint;
}
[UIView commitAnimations];
does (almost - the logic is a bit different in the !shouldMove case, but that's a different story) the same, but in a cleaner way.

Related

Flick a UIScrollView programmatically

When a user makes a flick gesture on a UIScrollView, the UIScrollView gets a momentum and starts moving, then slow down and finally stop.
But how can I make this happen programmatically? I mean without a finger flicking, the UIScrollView just start moving automatically and then slow down to a speed of 0.
In my app I have made my UIScrollView unlike a normal UIScrollView (say it looks like a roller), so I want make a hint to the user that he can scroll it (and then everything get started!)
I have googled a lot but there seemed no way to solve my problem. The setContentOffset just couldn't make the natural "slow down and stop at somewhere ahead" effect.
Any idea would be appreciated.
Thanks in advance.
Try, something like this >
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDelay:.8];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[UIView setAnimationDuration:(abs(1-3)*0.3)];
self.myScroll.contentOffset = CGPointMake(0, 500);
[UIView commitAnimations];
It is not currently what you need, but you can customise this code, and may be all be ok)
or use this code>
[UIView animateWithDuration:2.
delay:0.3
usingSpringWithDamping:1.
initialSpringVelocity:7.
options:UIViewAnimationOptionCurveEaseInOut animations:^{
//Animations
self.myScroll.contentOffset = CGPointMake(0, 500);
}
completion:^(BOOL finished) {
//Completion Block
}];
I think it is like you want(animation with damping like swipe effect)

View not animating with Autolayout

I've been searching around SO and have found some related posts, but none that have (yet) solved my problem.
I have a view setup in my storyboard, with a constraint hooked up through an IBOulet. Upon a certain action, the view should move up (or down to it's initial position). For the life of me, I cannot get it to work properly. Prior to autolayout, I ran this bit of code:
- (void)animateStarBackground:(NSString *)animationID
finished:(NSNumber *)finished
context:(void *)context
myView:(UIView *)myView
xPos:(float)xPos
yPos:(float)yPos {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.2];
[UIView setAnimationDelay:0.5];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDelegate:self];
myView.center = CGPointMake(xPos, yPos);
[UIView commitAnimations];
}
It used to work great, but anyway... we all know that this doesn't work in Autolayout anymore. So I changed it to the following:
-(void)animateButton:(UIView *)myView {
[UIView animateWithDuration:8.0f animations:^{
_viewConstA.constant = 45.0;
[myView layoutIfNeeded];
}];
}
When the action occurs, instead of starting in it's initial position as set in the Storyboard, it starts off as a 1x1 pixel view in the top left hand corner and animates downward into it's final resting place (instead of moving up 15 pixels as I had intended it to).
Now, if I take out the [myView layoutIfNeeded]; portion, the action positions the view in exactly the right place... BUT it doesn't animate there. It's just instantly there as the view appears.
The first screen grab is the initial location.
2nd grab is what's supposed to be the views final resting place.
3rd is the view animating from the wrong spot and direction.
Thoughts / comments? What the heck am I doing wrong? I thought this would be so easy... I've got to be missing something obvious.
When I call the following block-based animation from viewDidLoad, it animates the frame from CGRectZero to the final location. But if I call this from viewDidAppear, it works just as expected.
- (void)animateMovementByConstraint
{
[UIView animateWithDuration:0.5
animations:^{
self.topLayoutConstraint.constant = 100;
[self.view layoutIfNeeded];
}];
}
(In this example, topLayoutConstraint is an IBOutlet that I defined in Interface Builder for the top vertical constraint on the control that I wanted to animate.)
With auto layout, since I've started initiating my animations in viewDidAppear, I haven't had any problems.

Animate image location different after returning to page

I have a tabbed application for iPad. On the first tab I have a separate menu with a pointer that highlights the currently active menu button. The pointer (a UIImage) is animated into position after tapping the button.
The problem is when you leave the highlighted image on any button that's not the original/default button and move to another tab in the application, then back again, the image has moved back to the default location. However, when you tap another button the image moves in the wrong direction. I think it's because the image moves back but the old coordinates are retained, or the other way around? It's a bit hit and miss and behaves differently testing in the simulator compared to directly on the iPad.
I think I need to start with absolute coordinates and stick with them throughout the process. But I can't get my head around how to achieve it with the CGAffine functions.
Here is my current code
- (IBAction)diplayForm1:(id)sender {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
buttonHighlightImage.transform = CGAffineTransformMakeTranslation(0, 0);
[UIView commitAnimations];
}
- (IBAction)diplayForm2:(id)sender {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
buttonHighlightImage.transform = CGAffineTransformMakeTranslation(0, 80);
[UIView commitAnimations];
}
- (IBAction)diplayForm3:(id)sender {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
buttonHighlightImage.transform = CGAffineTransformMakeTranslation(0, 160);
[UIView commitAnimations];
}
EDIT - - -
#Mundi I tried the code you suggested. I'm assuming by newFrame you mean define a new UIImageView and property which I did in the .h (I called it 'sfh'), then #synthesize in the .m.
This is my code in the .m
-(void)viewWillAppear:(BOOL)animated {
// The "pointer" and the "old" frame are the same so I thought it pointless to do
// selectedFormHighlight.frame = selectedFormHighlight.frame;
// I tried anyway and also tried this and many other combinations
selectedFormHighlight.frame = sfh.frame;
}
-(void)viewDidAppear:(BOOL)animated {
[UIView beginAnimations:nil context:NULL];
[UIView animateWithDuration:0.3 animations:^{
selectedFormHighlight.frame = CGRectMake(0,0,0,0);
}];
[UIView commitAnimations];
}
When I run this the pointer animates off the screen (top-left) and doesn't come back. I have tried every possible combination I can think of in viewWillAppear and viewDidAppear but it makes no difference. I tried removing the other lines in viewDidAppear and it still does the same thing.
I think you're suggesting to animate the pointer from the original (default) position when the user returns from another screen. I don't want that, it should simply remain in the same spot where they left it without animation. Or at least put it back there without them realizing it. The only animation is when they choose to tap a different button within that view.
The reason you are experiencing this is that the actual view object and its presentation layer get mixed up when the view is hidden and redisplayed. The solution is to change the view properties rather than animate the layers.
Thus, you do not need to use affine transforms. Just set the new position of the pointer view directly by changing its frame or center.
The more up-to-date format to do this is with block based animations:
[UIView animateWithDuration:0.5 animations:^{
pointer.frame = newFrame;
}];
Also, it seems very inefficient to have three methods just differing by one variable. You could just multiply the desired y position by 80.
Edit:
Another reason your button resets is because that is where it was put when the view is instantiated. Just set the position of your view in viewWillAppear of your view controller and then animate it to the new location in viewDidAppear.
I appreciate the help from #Mundi but couldn't get it to work that way. I eventually arrived at the following code.
// This initially sets the frame with a variable value that
// corresponds to the current child view
-(void)viewDidAppear:(BOOL)animated {
selectedFormHighlight.frame = CGRectMake(0, self.getY, 250, 4100);
}
// Button actions (converted to a single action instead of one for each button)
// This was done by referencing a tag added to each button (the button tags
// match the children's tags)
-(IBAction)goToForm:(id)sender {
UIButton *btn = (UIButton *)sender;
self.currentChild = self.formViewControllers[btn.tag];
[UIView beginAnimations.nil context:NULL];
[UIView animateWithDuration:0.3 animations:^{
selectedFormHighlight.frame = CGRectMake(0, self.getY, 250, 4100);
}];
[UIView commitAnimations];
sfh.frame = selectedFormHighlight.frame;
}
// This returns the 'y' (vertical center position) of the pointer
// frame for each of the child views
-(int)getY {
switch(_currentChild.view.tag) {
case 1:
return -2005;
break;
case 2:
return -1925;
break;
default:
return -1845;
break;
}
}

How do I move an image from the top of the screen to the bottom and have it stay there in Xcode / iOS?

I have been trying to build an entry effect for a logo to come from the top of the screen to the bottom and remain there when a new view loads in my application. I have seen all of the tutorials that use NSTimer to bounce an image but once my logo hits the bottom it needs to exit. I'm going to read up on animation block codes to see if my solution resides there.
Apologies I'm a new be and am very grateful for the assistance.
Set logo frame to top and then:
[UIView beginAnimations: #"moveLogo" context: nil];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:1.0];
[UIView setAnimationCurve: UIViewAnimationCurveLinear];
logoToMove.frame = CGRectMake( final frame at the bottom );
[UIView commitAnimations];
logoToMove is your logo, give it an outlet and hook it in xib.
So you will set the initial frame and in the animation - the final frame. The animation will do the rest of the job.
Change UIViewAnimationCurveLinear to a desired one if you don't like that. Also the duration to speed up or slow down the movement.
To remove the view at the end of your animation, the easiest way would be to use blocks :
logoToMove.frame = topRect;
[UIView animateWithDuration:duration
animations:^{
logoToMove.frame = bottomFrame;
}
completion:^(BOOL finished) {
[logoToMove removeFromSuperview];
}
];
Doing it like that gives you control over the animation and on what to do once it's finished in a single method

Best method to get text to blink in iPhone OS?

I want my text box to blink (like the old LCD clocks).
Right now, I'm calling a myriad of NSTimers and selectors that wait, change the alpha, wait, then change it back. Even with this, it looks really bad, and I'm thinking I have to put an NSTimer to gradually change the alpha, but from what I hear they are not meant for things of that precision.
My thoughts are there must be a way to do this a lot better than how I am currently implementing it. It feels like hack.
Using an animation delegate might make it less "hacky":
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(animationDidStop:finished:context:)];
[myLabel setAlpha:0.0];
[UIView commitAnimations];
And then you can have your didStopSelector restart the animation:
- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
[self displayLabel];
}
Depending on the animationID, you could take different actions, etc. Using UIView's setAnimationDelay might come in handy as well.
UIView also has a setDuration call for animations:
[UIView setAnimationDuration:0.1];
If you are building for iOS4, check the documentation since you should be using block-based animation calls rather than these delegate based ones.
you can set the alpha of the lable with annimation just like
to hide lable with animation
[UIView animateWithDuration:0.2 delay:0.1 options:UIViewAnimationOptionCurveEaseOut animations:^{
lblDistance.alpha=0;
} completion:^(BOOL finished) {
if (finished) {
}
}];
to show lable with animation
[UIView animateWithDuration:0.2 delay:0.1 options:UIViewAnimationOptionCurveEaseOut animations:^{
lblDistance.alpha=1;
} completion:^(BOOL finished) {
if (finished) {
}
}];
this is the best way anyone can animate and create a blinking lable........
I would use an NSTimer, but instead of messing with alpha channels i would either not draw the text (if that's even possible with Apple's very attribute-limited SDK), or if that's not possible you could always draw something on top of it (like a rectangle).
Using this approach of drawing something over your text would yield better performance.
Though some (okay most) would consider this a ugly hack, let me just say this, "If it looks right, it is right."
NSTimers and changing the alpha is a perfectly acceptable way of doing it - that's certainly what I do. If you are having problems, perhaps a code sample might help us see where the issue is?

Resources