UILabel animating number change - iphone

I've got an UILabel that displays users score. And score changes from time to time, is there a way to animate this change, to slowly increment this number from its current value to its result value?
Something like http://josheinstein.com/blog/index.php/2010/02/silverlight-animated-turbotax-number-display/ but for objective-c.

Use a CADisplayLink to change the text property of custom subclass of UILabel over some period of time. You'll probably want to use a NSNumberFormatter for prettier output.
// Create instance variables/properties for: `from`, `to`, and `startTime` (also include the QuartzCore framework in your project)
- (void)animateFrom:(NSNumber *)aFrom toNumber:(NSNumber *)aTo {
self.from = aFrom; // or from = [aFrom retain] if your not using #properties
self.to = aTo; // ditto
self.text = [from stringValue];
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:#selector(animateNumber:)];
startTime = CACurrentMediaTime();
[link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)animateNumber:(CADisplayLink *)link {
static float DURATION = 1.0;
float dt = ([link timestamp] - startTime) / DURATION;
if (dt >= 1.0) {
self.text = [to stringValue];
[link removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
return;
}
float current = ([to floatValue] - [from floatValue]) * dt + [from floatValue];
self.text = [NSString stringWithFormat:#"%i", (long)current];
}

AUIAnimatedText has all you was asking for. That is replacement for UILabel with reach text animating possibilities ,

Related

Reset NSTimer after navigating to another view

Currently I am developing a game which needs a stop watch say 1 minute,when user taps the play button it enters the game screen,I want the to run the stop watch count down of 1 minute 01:00,00:59,00:58 etc. For that reason I searched and found NSTimer would be the ideal choice to implement a stop watch.Hence I took a label,created an instance of NSTimer,assigned time interval and started decrementing the value in timer label,i.e.:
-(void)viewDidAppear:(BOOL)animated
{
self.stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(viewWillAppear:) userInfo:nil repeats:YES];
[super viewDidAppear:YES];
}
-(void)viewWillAppear:(BOOL)animated
{
static int currentTime = 60;
int newTime = currentTime--;
int minutesRemaining = newTime / 60; // integer division, truncates fractional part
int secondsRemaining = newTime % 60; // modulo division
self.timerLabel.text = [NSString stringWithFormat:#"%02d:%02d", minutesRemaining, secondsRemaining];
if ([self.timerLabel.text isEqualToString:#"00:00"])
{
[stopWatchTimer invalidate];
}
[super viewWillAppear:YES];
}
The problem here is the timer starts running 01:00,00:59,00:58 and goes on,say at 53rd second,I navigated to another view and came back,it is running from 00:53,00:52 and so on,but I want it to run from 01:00 and for that I implemented invalidating NSTimer in viewDidDisappear i.e.
-(void)viewDidDisappear:(BOOL)animated
{
if ([stopWatchTimer isValid])
{
[stopWatchTimer invalidate];
self.stopWatchTimer = nil;
}
[super viewDidDisappear:YES];
}
Still the same issue exists!
Done lots of Research on the issue and found no answer useful and working.
Can some one please guide me,any help is appreciated.
Thanks every one in advance :)
You are use the selector viewWillAppear for the timer. viewWillAppear is a method on a viewController that gets called when the view appears on screen, you should not call it yourself. Instead, create your own method that decrements the timer:
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateTime:) userInfo:nil repeats:YES];
currentTime = 60; // This could be an instance variable
self.timerLabel.text = #"01:00";
}
-(void)updateTime {
int newTime = currentTime--;
int minutesRemaining = newTime / 60; // integer division, truncates fractional part
int secondsRemaining = newTime % 60; // modulo division
self.timerLabel.text = [NSString stringWithFormat:#"%02d:%02d", minutesRemaining, secondsRemaining];
if ([self.timerLabel.text isEqualToString:#"00:00"])
{
[self.stopWatchTimer invalidate];
}
}
-(void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
if ([self.stopWatchTimer isValid])
{
[self.stopWatchTimer invalidate];
self.stopWatchTimer = nil;
}
}
With this technique the timer is responsible for calling every second, but you will probably have timing issues after some seconds. To have a more accurate timing, you should store the time that you started the countdown and then on every updateTime call, compare the current time with the stored started time.
Add to your interface:
#property (nonatomic) NSTimeInterval startTime;
then in the implementation:
-(void)viewDidAppear:(BOOL)animated
{
self.startTime = [NSDate timeIntervalSinceReferenceDate];
self.duration = 60; // How long the countdown should be
self.stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(updateTime)
userInfo:nil
repeats:YES];
self.timerLabel.text = #"01:00"; // Make sure this represents the countdown time
}
-(void)updateTime {
int newTime = self.duration - (round([NSDate timeIntervalSinceReferenceDate] - self.startTime));
int minutesRemaining = newTime / 60; // integer division, truncates fractional part
int secondsRemaining = newTime % 60; // modulo division
self.timerLabel.text = [NSString stringWithFormat:#"%02d:%02d", minutesRemaining, secondsRemaining];
if (newTime < 1)
{
[self.stopWatchTimer invalidate];
/* Do more stuff here */
}
}
-(void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
if ([self.stopWatchTimer isValid])
{
[self.stopWatchTimer invalidate];
self.stopWatchTimer = nil;
}
}
Some extra (unrelated) comments on the code:
when implementing viewDidDisappear: pass the animated parameter to the call to super:
[super viewDidDisappear:animated];
when implementing viewDidAppear: pass the animated parameter to the call to super, also, make sure you call super first this in the method, before doing anything else

Reduce number in text box by one for each button tap

I'm relatively new to Objective-C so this might be really simple to do: I have a text box in my application that displays an ammo count, so each time the user taps a fire button the number in the text box will go down by one (12 > 11 > 10, etc) to 0. I have tried using for and if statements, but they are not working (I may have used incorrect syntax). This is what I'm using right now, but obviously I need the
- (IBAction)fire {
[ammoField setText:#"11"];
}
- (IBAction)reload {
[ammoField setText: #"12"];
}
The simplest way would be to convert the text to a number, decrement that and the reset the text, i.e. replace the code in the fire method with:
NSInteger ammoCount = [ammoField.text integerValue];
ammoCount--;
ammoField.text = [NSString stringWithFormat:#"%d", ammoCount];
But don't do this, it will make baby Steve Jobs cry.
A better way would be to add a new variable to the class of type UIInteger that tracks the the number of bullets, i.e.:
// in interface
NSInteger _ammoCount;
...
// in implementation
- (IBAction)fire {
_ammoCount--;
if (_ammoCount <= 0) {
_ammoCount = 0;
fireButton.enabled = NO;
}
[ammoField setText: [NSString stringWithFormat:#"%d", _ammoCount]];
}
- (IBAction)reload {
_ammoCount = 12;
[ammoField setText: [NSString stringWithFormat:#"%d", _ammoCount]];
fireButton.enabled = YES;
}
Oh, and don't forget to call reload at some point early on to ensure _ammoCount and ammoField get initialised.
Set Instance Integer
int x;
set value of it
x = 12;
do change in method
- (IBAction)fire {
[ammoField setText:[NSString stringWithFormat:#"%i",x]];
x--;
}
set the value of count in viewdidload with an int variable
fire method decrease the count by 1
and reload method to return value to 12
log or use values accordingly
Try this:-
int i;
-(void)ViewDidLoad
{
i=12;
}
- (IBAction)fire
{
[ammoField setText:[NSString stringWithFormat:#"%d",i]];
i--;
}
- (IBAction)reload {
i = 12;
[ammoField setText: [NSString stringWithFormat:#"%d", i]];
}
Hope it will work for you. Thanks :)

How to put a decimal point according to the will of the user in a calculator in iphone?

I have a calculator in which i would like to put the decimal point according to the button press for the decimal point. I get the decimal point but if I enter another digit the decimal pint vanishes and is overwritten .
The code is mentioned below for the decimal press:-
-(IBAction)decimalPressed:(id)sender{
calculatorScreen.text = [calculatorScreen.text stringByAppendingString:#"."];
}
For the digit press it is :-
-(IBAction)buttonDigitPressed:(id)sender{
currentNumber = currentNumber*10 + (float)[sender tag];
calculatorScreen.text = [NSString stringWithFormat:#"%g",currentNumber];
}
How can i do something like 23 then "." then 45. The result would be 23.45
I've recently done a calculator app and could understand your problem. Another thing you want to take note about the point is that you do not want to have multiple point in your calculation, e.g. 10.345.1123.5. Simply put, you want it to be a legal float number as well.
With that said, you can use a IBAction (remember to link it to your storyboard or xib file)
-(IBAction)decimalPressed:(UIButton *)sender
{
NSRange range = [self.display.text rangeOfString:#"."];
if (range.location ==NSNotFound){
self.display.text = [ self.display.text stringByAppendingString:#"."];
}
self.userIsInTheMiddleOfEnteringANumber = YES;
}
While it might be possible we are doing on the same project, it could also be entirely different (you starting from scratch by yourself) so i will go through some of the codes
you could replace UIButton with the default id, but it is better to static replace it to make clear clarification for yourself, or anyone else who view your code.
NSRange as the name implies, mark the range, and the range will be ur display text of calculation (e.g. 1235.3568), and the range of string it is targeting in this case is "."
therefore, if NSNotfound (rangeOfString "." is not found in the text range) you will append the current display text with "." with the function stringByAppendingString:#".", there is no else, so no function will take place if "." is already found, which solve the problem of multiple point on the display.
userIsInTheMiddleOfEnteringANumber is a BOOL to solve the problem of having 0 in ur display (e.g. 06357), if you have a method to change it, then replace my method name with your own.
Try with below code:
-(IBAction)buttonDigitPressed:(id)sender
{
UIButton *pressedButton = (UIButton *)sender;
calculatorScreen.text = [calculatorScreen.text stringByAppendingFormat:#"%d",pressedButton.tag];
currentNumber = [calculatorScreen.text floatValue];
}
-(IBAction)ButtonDot
{
decimalChecker = 10;
calculatorScreen.text = [NSString stringWithFormat:#"$ %g.", currentSavings];
decimalChecker=1;
}
-(IBAction)buttonDigitPressed:(id)sender
{
if (decimalChecker ==1)
{
currentDecimal = currentDecimal*10 + (float)[sender tag];
calculatorScreen.text = [NSString stringWithFormat:#"$ %g.%g", currentSavings,currentDecimal];
}
else
{
currentSavings = currentSavings*10 + (float)[sender tag];
calculatorScreen.text = [NSString stringWithFormat:#"$ %g",currentSavings];
}
}
This is my solution:
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController {
float result;
IBOutlet UILabel *TextInput;
int currentOperation;
float currentNumber;
BOOL userInTheMiddleOfEnteringDecimal;
}
- (IBAction)buttonDigitPressed:(id)sender;
- (IBAction)buttonOperationPressed:(id)sender;
- (IBAction)cancelInput;
- (IBAction)cancelOperation;
- (IBAction)dotPressed;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (IBAction)buttonDigitPressed:(id)sender {
if(!userInTheMiddleOfEnteringDecimal)
{
currentNumber = currentNumber*10 + (float)[sender tag];
TextInput.text = [NSString stringWithFormat:#"%d",(int)currentNumber];
}
else{
TextInput.text= [TextInput.text stringByAppendingString:[NSString stringWithFormat:#"%d",[sender tag]]];
currentNumber = [TextInput.text floatValue];
}
}
- (IBAction)buttonOperationPressed:(id)sender {
if (currentOperation == 0) result = currentNumber;
else {
switch (currentOperation) {
case 1:
result = result + currentNumber;
break;
case 2:
result = result - currentNumber;
break;
case 3:
result = result * currentNumber;
break;
case 4:
result = result / currentNumber;
break;
case 5:
currentOperation = 0;
break;
default:
break;
}
}
currentNumber = 0;
TextInput.text = [NSString stringWithFormat:#"%.2f",result];
if ([sender tag] == 0) result = 0;
currentOperation = [sender tag];
userInTheMiddleOfEnteringDecimal = NO;
}
-(IBAction)cancelInput{
currentNumber = (int)(currentNumber/10);
TextInput.text = [NSString stringWithFormat:#"%.2f",currentNumber];;
}
-(IBAction)cancelOperation{
currentNumber = 0;
TextInput.text = #"0";
userInTheMiddleOfEnteringDecimal = NO;
}
- (IBAction)dotPressed{
if(!userInTheMiddleOfEnteringDecimal){
userInTheMiddleOfEnteringDecimal = YES;
TextInput.text= [TextInput.text stringByAppendingString:#"."];
}
}
#end
Hope this helps.. Another way of solution...
after pressing the "." all values after will be divided as following
the first number ur press after pressing "." will be divided by 10 the second by 100 and so on
so editing ur function it would be like this
-(IBAction)buttonDigitPressed:(id)sender{
currentNumber = currentNumber + (float)[sender tag] / 10.0;
calculatorScreen.text = [NSString stringWithFormat:#"%g",currentNumber];
}
i think in that solve your problem
http://www.datasprings.com/resources/articles-information/iphone-sdk-getting-started-example-code
There is sample code of calculator.

Cocos2d schedule interval decreaser

I have made a simple timer but in trying to increase the timers speed over time, so I basically want the timer to have an interval of 1.0 and every 10 seconds I want that timer's interval to decrease by 0.1
How can I go about doing that?
[self schedule:#selector(tick:)interval:1.0];
That's in the init section
-(void)tick:(ccTime)dt{
myTime = myTime+1;
totalTime = myTime;
[timeLabel setString:[NSString stringWithFormat:#"%i", totalTime]];
}
That's the timer.
It's a basic timer which has an interval of 1.0 second, however I would like the interval to decrease by 10% every 10 seconds.
in .h file
#property(nonatomic,readWrite)CGFloat lastUpdatedInterval;
in.m file, initialize self.lastUpdatedInterval = 1.0;
then call
[self schedule:#selector(tick:)interval:self.lastUpdatedInterval];
in update loop,
-(void)tick:(ccTime)dt
{
if(myTime>9 && (myTime%10 == 0))
{
[self unschedule:#selector(tick:)];
self.lastUpdatedInterval = self.lastUpdatedInterval - (self.lastUpdatedInterval*0.1);
[self schedule:#selector(tick:)interval:self.lastUpdatedInterval];
}
myTime = myTime+1;
totalTime = myTime;
[timeLabel setString:[NSString stringWithFormat:#"%i", totalTime]];
}

AVPlayer Video SeekToTime

I'm using AVPlayer for play my video using slider and Some Buttons.
Here is my methods for moving forward and backward using buttons.
-(IBAction)MoveForward
{
//int value = timeSlider.value*36000 + 10;
//CMTime newTime = CMTimeMakeWithSeconds(value, playspeed);
//CMTime newTime = CMTimeMake(value,(playspeed*timeSlider.maximumValue));
CMTime newTime = CMTimeMakeWithSeconds(timeSlider.value, playspeed);
newTime.value += 60;
[player seekToTime: newTime];
}
-(IBAction)MoveBackward
{
CMTime newTime = CMTimeMakeWithSeconds(timeSlider.value-1, playspeed);
[player seekToTime: newTime];
}
My Problem on this is the time to Seek is not working properly. That navigate to next frame based on seconds. I need to move next frame minutely. Help me...
I don't really understand your code, you don't really need separate methods to move forwards and backwards, you can use the same one for both. I've got a working AVPlayer Movie Player, I'll show you how I did the slider part.
-(IBAction)sliding:(id)sender {
CMTime newTime = CMTimeMakeWithSeconds(seeker.value, 1);
[self.player seekToTime:newTime];
}
-(void)setSlider {
sliderTimer = [[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateSlider) userInfo:nil repeats:YES] retain];
self.seeker.maximumValue = [self durationInSeconds];
[seeker addTarget:self action:#selector(sliding:) forControlEvents:UIControlEventValueChanged];
seeker.minimumValue = 0.0;
seeker.continuous = YES;
}
- (void)updateSlider {
self.seeker.maximumValue = [self durationInSeconds];
self.seeker.value = [self currentTimeInSeconds];
}
- (Float64)durationInSeconds {
Float64 dur = CMTimeGetSeconds(duration);
return dur;
}
- (Float64)currentTimeInSeconds {
Float64 dur = CMTimeGetSeconds([self.player currentTime]);
return dur;
}
And that's it, there are two gotchas in this code, first, the duration property returns a CMTime variable, you have to convert it to a float, also, this returns the raw number of seconds, you have to convert it to h:mm:ss if you want to display time labels. Second, the updateSlider method is triggered by a timer every second.
Good Luck.
The following snippet of code worked for me:
CMTime videoLength = self.mPlayer.currentItem.asset.duration; // Gets the video duration
float videoLengthInSeconds = videoLength.value/videoLength.timescale; // Transfers the CMTime duration into seconds
[self.mPlayer seekToTime:CMTimeMakeWithSeconds(videoLengthInSeconds * [slider value], 1)
completionHandler:^(BOOL finished)
{
dispatch_async(dispatch_get_main_queue(), ^{
isSeeking = NO;
// Do some stuff
});
}];