animations ios objective-c - iphone

I have a global variable p.
I have a function that draws (or redraws) my complex UIView objects depending on the value of this global variable.
When I increase p my uiview objects needs to be redrawed. I need this be done using animation.
double pInitialValue=0.5;
double pNewValue=1.0;
//somewhere before animation we must call [self redraw] to draw our view with p=pInitialValue;
//then, when we call animate function.
//we change p to pNewValue
//and every step must be redrawed using [self redraw]
p=pNewValue; //using animation p must slowly grow from pInitialValue to pNewValue
[self redraw]; //and ofcourse user must see every step of animation so we need to call redraw function
for example I need an animation with duration 4.
That means that during this 4 seconds my p must grow from pInitialValue to pNewValue and in every step my redraw function must be called
Help me, please. How can it be done?

You can use a timer to increase your p value. Do that like this: have an instance variable in your class defined like
NSTimer *timer;
After that just before you want your animation to happen do:
CGFloat timerInterval = 1 / 30; // This means 30 frames per second. Change this as you want.
timer = [NSTimer scheduledTimerWithTimeInterval:timerInterval target:self selector:#selector(increaseP) userInfo:nil repeats:YES];
Than have a method called increasePlike this:
- (void)increaseP
{
if (p < maxPValue) {
// Increase p here and redraw your things
}
else {
[timer invalidate];
}
}
Let me know if you have any question and if this works for you.

Related

Scroll UISlider Automatically

So currently I have a UISlider in a UIViewcontroller that is meant to start animations within subviews when the user slides.. Basically when the user slides I have this battery with a filling in it that fills the empty battery image with a bar to indicate power within a cell, and the user can slide to see the energy the battery has at certain times of the day.
At the moment, when the View loads I would like the UISlider to AUTOMATICALLY start sliding from the beginning of the slider and scroll to the end within, lets say 5 seconds.
I implemented a loop that cycles through all the values of the uislider using this loop
for (int i = 0; i < [anObject count] - 2; i++)
{
sleep(.25);
NSUInteger index = (NSUInteger)(slider.value + 0.5); // Round the number.
[slider setValue:index animated:YES];
}
[anObject count] - 2 is equal to 62 at this time of day but will change and increment every 15 seconds because I'm fetching data from a server.
But that aside, why doesn't this work? The loop?
EDIT:
So heres what I did with NSTIMER
[NSTimer timerWithTimeInterval:0.25 target:self selector:#selector(animateSlider) userInfo:nil repeats:NO];
and animateSlider looks like this:
- (void)animateSlider:(NSTimer *)timer
{
NSLog(#"Animating");
NSUInteger index = (NSUInteger)(slider.value + 0.5); // Round the number.
[slider setValue:index animated:YES];
}
But no luck... Why isn't NSTimer "firing"..... I remmeber vaguely there was a method that FIRES an nstimer method but not sure if that's needed...
EDIT:
Ahh it does need "Fire"....
NSTimer *timer = [NSTimer timerWithTimeInterval:0.25 target:self selector:#selector(animateSlider) userInfo:nil repeats:NO];
[timer fire];
But for some reason it only fires once.... Any ideas ?
"for some reason it only fires once..."
If you changed the NSTimer set up to this:
NSTimer *timer =
[NSTimer scheduledTimerWithTimeInterval:0.25
target:self
selector:#selector(animateSlider:)
userInfo:nil
repeats:YES];
This would schedule the timer on the current run loop immediately.
And since the "repeats" parameter is "YES", you'd then repeat the timer every quarter second, until you invalidate the timer (which you should do when the ending condition is reached, like when the slider reaches its destination).
P.S. You'd need to change the selector method declaration of your timer's target slightly. According to Apple's documentation, "The selector must correspond to a method that returns void and takes a single argument. The timer passes itself as the argument to this method."
So declare "animateSlider" like this instead:
- (void)animateSlider: (NSTimer *) theTimer;

Xcode NSTimer get position object every millisecond

I need to get the position of the _propArrow (UIImageView) every .1sec at all time and use a NSLog to verify in the method (void)runScheduledTask
but does not operate every .1 sec.
only indicates the start and end of the movement of the animation
This is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
_propArrow.frame = CGRectMake(144, 291, 33, 71);
aTimer = [NSTimer scheduledTimerWithTimeInterval:.1 target:self selector:#selector(runScheduledTask) userInfo:nil repeats:YES];
}
- (void)runScheduledTask {
NSLog(#"Position: %#", NSStringFromCGRect(_propArrow.frame));
}
- (IBAction)fire:(id)sender {
[UIView animateWithDuration:1
delay:0
options:UIViewAnimationCurveEaseInOut
animations:^{
_propArrow.frame = CGRectMake(144, 0, 33, 71);
}
completion:^(BOOL finished){
}];
}
First, according to the NSTimer docs, that class has an effective resolution of about 50-100 ms. So using it to do anything at 1 ms intervals is bound to fail.
Second, even if NSTimer could handle 1 ms intervals, you're not even asking it to do that:
aTimer = [NSTimer scheduledTimerWithTimeInterval:.05...
Your code schedules the timer to run ever 0.05 s, which is to say every 50 ms.
Third, the screen doesn't update at anything close to 1 ms intervals, so the actual position of your image on the screen will change much less often than that.
So, what exactly are you trying to accomplish here? If you want to know where the object would be in the real world with continuous motion, you can calculate that for any point in time.
Because in most UIKit animated properties, the new value gets set immediately from the perspective of your code; it's the displayed value that actually animates. Reading animatable properties like UIView.frame should never give you an intermediate value (the main exception is UIScrollView which does its own thing for animated scrolling).
If you want "approximately what's being displayed on screen", you need to access the CoreAnimation "presentation layer":
#import <QuartzCore/CALayer.h>
- (void)runScheduledTask {
NSLog(#"Position: %#", NSStringFromCGRect(_propArrow.layer.presentationLayer.frame));
}
Also consider using CADisplayLink if you want a callback that runs every frame.

objective-c: Animate button before timer ends

I'm working on a very simple iPhone game that involves choosing the right colored button as many times in a row based on a randomized voice prompt. I have it set up so that if the button is one color and gets clicked, it always goes to a hard-coded color every time (e.g. if you click red, it always turns blue). The color change method is set up in an IBOutlet. I have a timer set up in a while loop, and when the timer ends it checks if the player made the right selection. The problem is that the button color change does not occur until after the timer runs out, and this causes a problem with the method used to check the correct answer. Is there a way to make this color change happen instantly? From what I've searched I know it has something to do with storyboard actions not occurring until after code executes, but I haven't found anything with using a timer. Here is a section of the method that calls the timer if the answer is correct:
BOOL rightChoice = true;
int colorNum;
NSDate *startTime;
NSTimeInterval elapsed;
colorNum = [self randomizeNum:middle];
[self setTextLabel:colorNum];
while (rightChoice){
elapsed = 0.0;
startTime = [NSDate date];
while (elapsed < 2.0){
elapsed = [startTime timeIntervalSinceNow] * -1.0;
NSLog(#"elapsed time%f", elapsed);
}
rightChoice = [self correctChoice:middleStatus :colorNum];
colorNum = [self randomizeNum:middle];
}
One of two things stood out
You're using a while loop as a timer, don't do this - the operation is synchronous.
If this is run on the main thread, and you code doesn't return, your UI will update. The mantra goes: 'when you're not returning you're blocking.'
Cocoa has NSTimer which runs asynchronously - it is ideal here.
So let's get to grips with NSTimer (alternatively you can use GCD and save a queue to an ivar, but NSTimer seems the right way to go).
Make an ivar called timer_:
// Top of the .m file or in the .h
#interface ViewController () {
NSTimer *timer_;
}
#end
Make some start and stop functions. How you call these is up to you.
- (void)startTimer {
// If there's an existing timer, let's cancel it
if (timer_)
[timer_ invalidate];
// Start the timer
timer_ = [NSTimer scheduledTimerWithTimeInterval:5.0
target:self
selector:#selector(onTimerFinish:)
userInfo:nil
repeats:NO];
}
- (void)onTimerFinish:(id)sender {
NSLog(#"Timer finished!");
// Clean up the timer
[timer_ invalidate];
timer_ = nil;
}
- (void)stopTimer {
if (!timer_)
return;
// Clean up the timer
[timer_ invalidate];
timer_ = nil;
}
And now
Put your timer test code in the onTimerFinish function.
Make an ivar that stores the current choice. Update this ivar when a choice is made and make the relevant changes to the UI. Call stopTimer if the stop condition is met.
In the onTimerFinished you can conditionally call and startTimer again if you desire.
Hope this helps!

How can I continually update when UISlider is past a certain value?

I'm implementing a UISlider that allows users to select a position on a football field. The idea of the slider is for the field to scroll forward when the slider value is above a certain value (the section with the arrow).
My issue is that I can only respond to the slider on UIControlEventValueChanged - so the field will only scroll forward when the user is actually moving the slider. I'd like it to move forward as long as the value is above a certain amount.
Any idea how I can do this? (I'm open to any suggestion, including an implementation that does not use a UISlider, composite implementations, etc.).
Here's the implementation:
The easiest way to handle this is with a timer. Add an NSTimer instance variable to your class, named—for the sake of the example below—moveTimer, then set up something like this:
- (void)sliderChanged:(UISlider *)slider
{
if(slider.value > 5)
{
if(moveTimer == nil)
{
moveTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self action:#selector(move) repeats:YES];
}
}
else
{
if(moveTimer != nil)
{
[moveTimer invalidate];
moveTimer = nil;
}
}
}
- (void)move
{
// update the background behind your slider
}
I found a solution that works for me:
[self performSelector:#selector(sliderMoved) withObject:nil afterDelay:0.5];
I call the sliderMoved method after every 0.5 seconds when slider.value > 0.5.
Try setting the continuous property of the slider to YES.

How can I create an NSTimer that repeats twice and then stops

I'd like to create an NSTimer that repeats twice then stops and invalidates itself. I'd rather not use a loop if that's possible. Any thoughts on how I could do this?
Create a static int inside your timer delegate function that is initialized to 0.
Increment it each time the delegate is called.
When the counter reaches the value you wish invalidate the timer.
This is something your timer's target should handle, not something the timer itself should handle. You can either install a repeating timer and have the target invalidate it the second time it fires, or you can install a one-shot timer, reinstall it after the first time it fires, and then not set it up again the second time.
Basically, you need a state machine state variable that can be accessed both from the routine that initializes the timer, and from the timer's target.
Set the state variable to allow the first call to the timer task to restart the timer, but in that call also set that state variable so that subsequent calls do not restart.
Note that this kind of state variable can be used for any number of timer task repetitions, by simply decrementing it.
State machines are pretty much how all (synchronous) digital chips and logic works.
I very much disagree with the Jeremy that this is something that the target should handle. In fact I disagree so much that I have created my own Timer class, based on NSTimer, that you can configure in detail.
- (void) doSomething: (Timer*) timer
{
NSLog(#"This is iteration %d", timer.currentIteration);
}
- (void) startDoingSomething
{
Timer* timer = [Timer new];
timer.interval = 5.0; // Fire every 5 seconds
timer.delay = 2.5; // Start firing after 2.5 seconds
timer.iterations = 3; // Only fire three times
timer.target = self;
timer.selector = #selector(doSomething:);
[timer schedule];
// Don't forget to release timer somewhere - the above is just an example
}
See http://github.com/st3fan/ios-utils
One solution might look similar to this.
Launching the timer
[NSTimer scheduledTimerWithTimeInterval:3 target:self selector:#selector(timerMethod:) userInfo:nil repeats:YES];
Handling the timer and repetitions
int repetitions = 2; //EDIT: remove static declaration (see below)
- (void)timerMethod:(NSTimer*)theTimer{
NSLog(#"Timer fired");
repetitions--;
if(repetitions == 0){
[theTimer invalidate];
NSLog(#"Timer stopped");
}
}
EDIT:
I removed the static modifier above to make a more generic example. The original intent of the static was to persist the timer across objects of similar type, a request that the OP did not make.