Determining time it takes a downward-moving animation to reach a spot - iphone

I have animated an image to drop straight from the top of the screen to the bottom and I want to be able to determine the time it takes for the image to reach a certain point on the screen. Normally, this would be easy to do by multiplying the y-coordinate of the point you want by the duration of the animation, and then dividing it by the total amount of 'pixels' your image moves from start to finish. However, objective-c animations start off slowly, accelerate, and then decelerate before stopping--which means I cannot use this method to calculate the time it takes. So is there any way I can determining time it takes a downward-moving animation to reach a spot?
Edit: With animations, supposedly there's no way of determining the point of an object at any point during the animation--the only information available, which you provide, is the beginning point and end point

If you are using animateWithDuration:delay:options:animations:completion: you can set the options parameter to UIViewAnimationOptionCurveLinear. The default has a funky curve but UIViewAnimationOptionCurveLinear is just what it sounds like; linear. No speed ups, no speed downs. Also, yes, there is a way to access the location of the animated view. You access its presentation layer (the layer used to present animations) and use its frame's position.
EDIT: Here's an example of block based animation that computes the distance from a view to the destination using the Pythagorean theorem and animates the view to the destination with a linear curve.
UIView* view = [[UIView alloc] init];
CGPoint destination = CGPointZero;
[UIView animateWithDuration: sqrt(pow(view.frame.origin.x - destination.x, 2) + pow(view.frame.origin.y - destination.y, 2)) / pixelsPerSecondVelocity
delay: 0
options: UIViewAnimationOptionCurveLinear
animations:^(void) {
view.frame = CGRectMake(destination.x,destination.y,view.frame.size.width,view.frame.size.height);
}
completion:^(BOOL finished) {
// nothing
}];
If you are targeting below iOS 4 you can just add the code below after the begin but before the commit.
[UIView setAnimationCurve:UIViewAnimationCurveLinear];

Related

How easy to pass value from decibel reading to the dynamic needle graphic

Just wondering how easy to do something like this in Iphone.
will Like to how to make the needle move
Thanks for reading and comments
You should be able to put the needle in an UIImageView. Every view now has an associated transformation, this is basically a matrix, which decides how original points are mapped to the screen. There are quite simple ways to do a simple manipulation of this:
UILabel *lblTest = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
lblTest.text = #"Testing";
[view addSubview:lblTest];
lblTest.transform = CGAffineTransformMakeRotation(M_PI / 4.0);
will result in the label being rotated by 45 degrees (which is a quarter of PI in radians; you can convert degrees to radians by multiplying by M_PI and dividing by 180.0). Using this you already have the methods at hand to animate the needle whenever a new data point comes in.
Note that this can also be animated:
[UIView animateWithDuration:5.0 animations:^{
lblTest.transform = CGAffineTransformMakeRotation(M_PI_4);
}];
This would slowly rotate the view to 45 degrees over the course of 5 seconds. There are some tutorials out there for animations, e.g. How to use UIView animation tutorial. And the framework already allows for some advanced things, e.g.:
[UIView animateWithDuration:1.0 delay:1.0 options:UIViewAnimationCurveEaseInOut|UIViewAnimationOptionAutoreverse|UIViewAnimationOptionRepeat animations:^{
lblTest.transform = CGAffineTransformMakeRotation(M_PI_4);
} completion:^(BOOL finished) {
}];
Will "smooth" beginning and end of the rotation, automatically reverse back and repeat this over and over. This results in the label "wiggeling" back and forth after waiting for 1 second.
You can use this to introduce some smooth effects when jumping between values. If you for example sample data only every 250ms, you might use that time to introduce some animation.
Very easy if you know what you're doing - apply A-weighting filter, RMS, apply time weighting filter, convert to dB. The major problem is calibrating the microphone.
However, if you haven't done audio processing with biquads before and you don't know anything about it, it's probably not that easy.
It depends how much experience you have in objc :)
But representing analog value with a gauge like this
is not too complicated.
This link for example should get you started...

Is there an easy way to have animation overshoot its target and then come back?

Is there an easy way to have animation overshoot its target and then come back?
So let's say I would like to scale a circle from 50% to 110% then back to 100%, creating an almost cartoony effect.
I think Flash or some jQuery plugin has something like this built-in as an easing option. Does iOS have anything similar or does it need to be done manually?
You don't have to do it manually. You can do it almost automatically with UIView animation methods.
You can set your target size to 110%, and set the animation to reverse and "repeat". You set the repeat count to 0.58 in this case. That is, do half of a full out and back cycle, then 1/6 of that more.
That's not quite all there is to it. If you just do that, after the animation completes, it would snap back to 110%. So you need to set it back to 100% to keep it at the target position.
Like this:
// make it 50% size initially; maybe you already did that
circleView.transform = CGAffineTransformMakeScale(0.5,0.5);
[UIView animateWithDuration:1.0 / 0.58 // actual duration 1.0s
animations:^{
[UIView setAnimationRepeatCount:0.58];
[UIView setAnimationRepeatAutoreverses:YES];
circleView.transform = CGAffineTransformMakeScale(1.1,1.1);
}
completion:^(BOOL finished){
circleView.transform = CGAffineTransformIdentity;
}
]
You'll have to do it manually. Take a look at CAKeyframeAnimation. You can specify different values to use at different points in the animation. Easing is supported by setting the timingFunctions property. The hardest part will be figuring out the right values to get the effect you want.
It's just two animations -- start the second one in a block when the first is complete. The easing options have to do with how they accelerate/decelerate into the animation.
Here's the basic idea
http://objcolumnist.com/2010/09/19/core-animation-using-blocks/
In your code, you would want to start the next animation in the finish block.

How to rotate a needle (for a sound level meter) about it origin?

Hi so this is my situation up to now. I've added a UIImageView to Interface Builder and set it to a .png image of a needle (Needle.png), I've also connected it to the corresponding IBOutlet in Xcode.
In my viewDidLoad: method I set the anchor point,
- (void)viewDidLoad {
[super viewDidLoad];
[needle.layer setAnchorPoint:CGPointMake( 0.5, 1 )];
}
And I've also created a button in Interface Builder and connected it to an IBAction in Xcode, this button performs an animation block,
- (IBAction)animate {
[UIView beginAnimations:#"rotate" context:nil];
[UIView setAnimationDuration:1.0];
self.needle.transform = CGAffineTransformMakeRotation(10 * M_PI / 180);
[UIView commitAnimations];
}
So when I press the animate button my needle image rotates by 10° counter-clockwise, so thats a start. But what I really want to do it to have the needle constantly rotating itself to the correct angle based off a constantly changing value.
For example if I have a decibel value that gives me the current decibel level in the room, I'd like that needle to reflect that value (i.e. 10° == 10dB), and since this value is constantly changing I'd want my needle to rotate clockwise or counter-clockwise accordingly.
Does anyone know how I could do that? I'd also appreciate it if someone could post some source code to help me out.
But what I really want to do it to
have the needle constantly rotating
itself to the correct angle based off
a constantly changing value.
Change the rotation value when the "constantly changing value" changes.
What is the stimulus to the change of this value? Place the code to change the rotation there.
If the value is a KVO - you could use that mechanism to change the rotation when the KVO changes.
Also - sounds like you may want to do the change in an animation block, to make the needle movements more fluid. Say for example the value only changes once a second - make the change in an animation block that makes it change over the course of a second. They movement of the needle will be less "jerky" that way.
Be forewarned that if you are doing animation, set the setAnimationBeginsFromCurrentState - so if you change values (and re-animate) halfway through an existing animation, it will "do the right thing".

iPhone animation based on input values (touches) not time

For an animation effect perfectly suited to an animation group approach as shown in Brad Larson's answer here, I need the animation to proceed according to inputs. Specifically touch and position of detected touches. It is easy to handle touchesMoved: and to set the position of the elements for every touch but it just isn't smooth like the core animation approach.
Imagine a marble in a grooved track. I want to push the marble along to any position at any speed in one direction or the other. The animation has to do something like that, moving a visual element along a path in response to touches. CAKeyframeAnimation has the path bit exactly but seems to always want to base the transition from frame to frame on time elapsed, not on any other factor, and in one direction.
31 January update - Thanks all for the responses so far however none is really solving the problem. I have a circular menu that is dragged to select an option. All of it needs to move together and I have worked around it by using an view that has a rotational transform applied and the inverse rotational transform applied to its subviews so the icons all rotate with appropriate ferris wheel orientation. It really looks better when the icons are animated along a slightly ovoid path though... the marble description is an attempt to make clear what I'm trying to do. Better perhaps to imagine magnets oriented to repel all travelling in a groove - move one and its neighbours move too but not necessarily in the direction that the dragged magnet moves as the path curves.
Right now the problem is one of following a simple path created with one circle but I'd really like to know how to animate objects along an arbitrary path, position controlled purely by touch with no calculations involving velocity or direction.
You may be able to use the hierarchical nature of timelines in layer trees to achieve what you’re looking for. Objects implementing CAMediaTiming, which include both CAAnimation and CALayer, inherit a timespace from their parent, which they can modify (via scaling, shifting and repeating) and propagate to their children. By setting the speed property of a layer to 0.0 and adjusting the timeOffset property manually, you can decouple that layer (and all of its sublayers) from the usual notion of time.
What you’d do in your case is define the animations for all your menu items to animate their position along your desired CGPath from time t0 to t1, with the appropriate timeOffset on each animation to keep your items appropriately spaced. Note that a beginTime of 0.0 on an animation is usually interpreted as starting from the time the animation was added to its layer, so if you want t0 to be 0.0, you'll probably have to set it to a tiny epsilon > 0.0.
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
animation.beginTime = 1e-100;
animation.duration = 1.0;
animation.fillMode = kCAFillModeBoth;
animation.removedOnCompletion = NO;
animation.path = path;
animation.calculationMode = kCAAnimationPaced;
animation.timeOffset = timeOffset;
You’d then set the speed property to 0.0 on a parent layer (containing only these menu items) and update its timeOffset to values between t0 and t1 in response to your touch events.
This approach has two potential caveats. Because you’ve taken over the nature of time on this layer subtree, you probably won’t be able to animate other properties at the same time. Additionally, if you want coasting behavior for a fast flick, you’ll probably need to animate time forward on your own.
If you are targeting iOS 4.0 or greater then you can use the new block based class methods to start animatable changes to view objects. So from your touch event you can initiate an animatable change to properties on the view:
[UIView animateWithDuration:1.0 animations:^{
yourView.alpha = 0.0; // fade out yourView over 1 second
}];
N.B. This could just as easily be a change to another property on the view, like its location. You can animate the following properties on the view this way:
#property frame
#property bounds
#property center
#property transform
#property alpha
#property backgroundColor
#property contentStretch
If you are targetting earlier versions of iOS you will need to use UIView beginAnimations and commitAnimations methods to create an animation block:
[UIView beginAnimations:nil context:context];
[UIView setAnimationDuration:1.0];
yourView.alpha = 0.0;
[UIView commitAnimations];
This stuff works really well and once you start using it, you will have to be careful you don't get addicted. ;)
Update for comment:
You can bind the position of the marble to the location of the touch event. Once you get the touchesEnded event you can then animate the location of the marble using an animation block.
velocity = distance/time. You can try by giving delay according to touches moved and time. You can calculate time between touchesBegan and touchesEnded methods.
I'm not entirely certain I understand exactly what you want to do but... Why not just draw the marble wherever the user's finger is? No animation required.
If you want to keep the marble moving after the user lets go, you need to maintain a concept of momentum and use that to animate the marble slowing down. You could use either CoreAnimation for that or a timer.
I would make a velocity calculation in touches moved, and add it to a local variable that a block can mutate via a timer.
in other words, in touches moved make a velocity calculation bearing in mind the direction of a previous velocity. In touches ended, fire a block that translates the 'marble' decaying the velocity as you disire. If the user moves the marble again, modify the local variable, this will in turn speed up the animation of the marble or slow it down/change direction depending on on the direction of the touch.

reasonable expectations for CALayer number of property animations concurrently?

I'm using UIView animation blocks to animate CALayer properties (backgroundColor in this case) on multiple layers on the display at once.
All layers are opaque, and I'm animating everything in one block, essentially like this
[UIView beginAnimations:#"outer" context:nil];
float duration = .25;
float offset = 0.0;
for( NSArray *viewsInPass in viewQueue ) {
for( UIView *innerView in viewInPass ) {
[UIView beginAnimations:#"inner" context:nil];
[UIView setAnimationDelay:offset];
[UIView setAnimationDuration:duration];
[innerView.layer setBackgroundColor:[newColor CGColog]];
[UIView commitAnimations];
}
offset += duration;
}
[UIView commitAnimations];
Once this has 4-5 concurrent layers animating their background color it get very choppy and the device essentially starts missing its render rate entirely and just freezes to the end of the remaining animations. All views do not overlap and they are all opaque, and are generally about 20x20 pixels.
I was a little shocked at how non-performant this is, especially after reading so many gratifying things about Quartz 2D etc. I feel like I must be missing something fundamental here!
Help!
Opaqueness in the views is the single biggest performance impact - if you can keep them from overlapping and entirely opaque through the whole rendering process (i.e. no transparency anywhere in that view sequence, including the background), you'll get slightly better perf.
I managed to get a single large animation up at ~30 fps (i.e. whole screen), but ran into memory limitations (original iPhone/iPod) that ultimately killed me beyond that. If you want more than about what you're getting there, you'll need go into OpenGL land - either through a gaming framework or directly. The overhead of the objective-C calls in that loop to enable changing the offset at the same time that you're cycling through images will ultimately kill you.
Edit: [removed]
Since you have each animation start as the previous one ends, you could start each in an NSTimer or the delegate didFinish callback instead of queueing all the animations at once. That would put less demand on the animation system.
You should try to make sure that no views are queued with different colors at the same time.
It turns out the issue is that all you need to add some sort of epsilon between the offsets you start with. You end up with n*2 animating at the tail of each pass and the start of the next just for an instant. For some reason this makes the animation system barf even if it's just a processing slice they share.
Making it be
offset += duration * 1.05;
resolved the jerkiness between passes that clogged up the animations.