How to save Best Time score using NSUserDefault - ios

Hey guys I'm extremely new to programming and especially with Objective C so please bear with me. I been practicing my skills on an iPhone app project I have been working on and now am stuck on an issue with saving time. I have a simple slider puzzle game that has a timer that increases until you finish the puzzle. the time is represent in this format "00:00". i would like to be able to save the lowest time after completion and present it on the screen on the start of the app for reference on the time to beat. The problem I face however is that it keeps saving the time 0 as the lowest and nothing can beat that.
Below is code of my:
Timer
-(void)setTimer
{
self.secondsCount = 0;
self.gameCountDownTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(timerRun) userInfo:nil repeats:YES];
}
-(void)timerRun
{
self.secondsCount++;
NSString *timerOutput = [NSString stringWithFormat:#" %02.f : %02i", round(self.secondsCount / 60), self.secondsCount % 60 ];
self.timeLabel.text = timerOutput;
}
StartGame Button
- (IBAction)start:(id)sender {
// reset steps
self.secondsCount=0;
step = 0;
[self setTimer];
// set the view so that it can interact with touches
[board setUserInteractionEnabled:YES];
// set the label text to 'reset'
[startButton setTitle:#"Reset" forState:UIControlStateNormal];
// initialize the board, lets play!
[board playWithImage:image andSize:(boardSize.selectedSegmentIndex+3)];
}
End of Game
(void)puzzleBoardDidFinished:(PuzzleGameView *)puzzleBoard {
// add the full image with 0.0 alpha in view
if(self.secondsCount!=0){
self.finalTime = self.secondsCount;
NSLog(#"seconds is: %i",self.secondsCount);
[self saveTime];
if(self.finalTime<self.bestTime) {
NSLog(#"bestTime! is: %i",self.finalTime);
[self saveTime];
}
}
[self.gameCountDownTimer invalidate];
self.gameCountDownTimer = nil;
UIImageView *fullImage = [[UIImageView alloc]initWithImage:image];
fullImage.frame = board.bounds;
fullImage.alpha = 0.0;
[board addSubview:fullImage];
[UIView animateWithDuration:.4
animations:^{
// set the alpha of full image to 1.0
fullImage.alpha = 1.0;
}
completion:^(BOOL finished){
// set the view interaction and set the label text
NSLog(#"Congrats! You finish this %d x %d puzzle with %d steps", (boardSize.selectedSegmentIndex+3), (boardSize.selectedSegmentIndex+3), step);
[board setUserInteractionEnabled:NO];
[startButton setTitle:#"Start" forState:UIControlStateNormal];
}];
}
And Attempt to save the data
-(void)saveTime {
[[NSUserDefaults standardUserDefaults]setInteger:self.secondsCount forKey:#"BestTime"];
[[NSUserDefaults standardUserDefaults]synchronize];
}
This was in my viewdidload
self.bestTime = [[NSUserDefaults standardUserDefaults]integerForKey:#"BestTime"];
self.bestTimeLabel.text = [NSString stringWithFormat:#" %02.f : %02i", round(self.bestTime / 60), self.bestTime % 60 ];
As stated before Im pretty new so any advice and recommendations would be awesome. Im definitely eager to learn more about this stuff and would greatly appreciate if you guys could help with the journey. Thank You

It looks like [[NSUserDefaults standardUserDefaults]integerForKey:#"BestTime"] is 0 every time you assign its value to self.bestTime.
This code might help:
if(self.bestTime == 0){
self.bestTime = finalTime;
}

Your code looks ok. I'm pretty sure it's just the showing the value in NSLog that is not working.
Change:
self.bestTimeLabel.text = [NSString stringWithFormat:#" %02.f : %02i", round(self.bestTime / 60), self.bestTime % 60 ];
To:
self.bestTimeLabel.text = [NSString stringWithFormat:#" %02.f : %02i", round(self.bestTime / 60.0f), self.bestTime % 60 ];
For float variable: int / int yields integer which you are assigning to float and printing it so it's 0.00.

Related

Smooth Animation before NSTimer, But now it's Glitchy

I have a UIImageView rotating forever on my screen... I use the code below, which I found HERE:
- (void) spinWithOptions: (UIViewAnimationOptions) options {
[UIView animateWithDuration: 0.0f
delay: 0.0f
options: options
animations: ^{
Hand.transform = CGAffineTransformRotate(Hand.transform, M_PI / 30);
}
completion: ^(BOOL finished) {
if (finished) {
if (animating) { //if, keep spinning
[self spinWithOptions: UIViewAnimationOptionCurveLinear];
} else if (options != UIViewAnimationOptionCurveEaseOut) { //final spin
//[self spinWithOptions: UIViewAnimationOptionCurveEaseOut];
}
}
}];
}
- (void) startSpin {
if (!animating) {
animating = YES;
[self spinWithOptions: UIViewAnimationOptionCurveEaseIn];
}
}
- (void) stopSpin {
animating = NO;
}
It worked find and the rotation seems smooth...
I've since added a countdown timer, using the follow:
-(void)timerTick:(NSTimer *)timer{
ticks -= 0.1; //NSLog(#"Total Ticks: %.2f",ticks);
if(ticks < 0.0){ //3 seconds is up
Failed.image = [UIImage imageNamed:#"Failed.png"];
ticks = 0.0;
[self GameOver]; //out of time...
}
else {
FailedBeforeTimer.image = [UIImage imageNamed:#"Failed Before.png"];
}
CountDownTimer.text = [NSString stringWithFormat:#"%.1f",ticks];
}
-(void)startTimer{
ticks = 3.0;
timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(timerTick:) userInfo:nil repeats:YES];
}
-(void)stopTimer{
[timer invalidate];
ticks = 0.0;
}
Since adding this timer to my app, the rotation animation is really blocky and no longer a smooth rotation...
Any ideas what I should implement to solve this?
To check, I removed the timer and re-run the application, and sure enough, the animation was smooth again :(
UPDATE:
After some more trial and error, it seems to be this line of code that slows the process down: CountDownTimer.text = [NSString stringWithFormat:#"%.1f",ticks]; ie. When I don't display the countDownTimer text on the screen/update label on screen), the rotation is smooth still...
So confused... What can I do?
Animating with zero-duration animations does not make sense. That's not how UIView animation is supposed to work. My guess is that is the source of your problems. A zero duration animation will be jumpy by definition.
You should be animating your changes over some time-period. (Always use a non-zero duration.)
EDIT:
Note that your timerTick method is horribly inefficient. You load the same image into an image view on every tick. Don't do that. Set it up with the initial image, and then only change the image when your tick count reaches zero.
You should give CADisplayLink a try, it should be suitable for your case. It's easy to use, there are heaps of tutorials out there, just do a search.

UIButton won't generate randomly when score is added?

I am having a problem with my coding on Xcode. I am making an app in which a target appears, then you have to tap it, but the time allowed between each tap shortens gradually.
MY PROBLEM: whenever I try and make a UILabel update after a tap, the target goes back to it's original position on my view controller. I have all relevant code here:
-(IBAction)startButtonPressed:(id)sender {
startButton.hidden = YES;
Text.hidden = YES;
targX = arc4random() %769;
targY = arc4random() % 925;
Target.center = CGPointMake(targX, targY);
Target.hidden = NO;
}
-(IBAction)targetTapped:(id)sender {
[Time invalidate];
targX = arc4random() %769;
targY = arc4random() % 925;
[self scoreAdd:(id)sender];
Target.center = CGPointMake(targX, targY);
timeMax = 5 - (Score * 0.05);
if (Score >= 90) {
timeMax = 0.5;
}
Time = [NSTimer scheduledTimerWithTimeInterval:timeMax target:self selector:#selector(Lose:) userInfo:nil repeats:NO];
}
-(void)Lose:(id)sender {
Target.hidden = YES;
Text.hidden = NO;
}
-(void)scoreAdd:(id)sender{
Score = Score + 1;
scoreLabel.text = [NSString stringWithFormat:#"Score: %li", Score];
}
print
targX = arc4random() %769;
targY = arc4random() % 925;
in both methods , check whether you are getting proper points. And check generated points lies with in device screen.

Incremental number animation

I have a homepage that showing the value of incomes and expenses. Right now the values just appear on the the screen, however is was wondering whether i can make those number start from 0 and then have an animation to increment that value, like a stopwatch. Is it also possible to make the final number after the increment has finished to animate with a pulse". This is how i am making the label pulsate now but the label becomes bigger from the left had side and not from the center.
[self.view addSubview:_lblTotal];
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
scaleAnimation.duration = 0.4;
scaleAnimation.repeatCount = HUGE_VAL;
scaleAnimation.autoreverses = YES;
scaleAnimation.fromValue = [NSNumber numberWithFloat:0.8];
scaleAnimation.toValue = [NSNumber numberWithFloat:1.0];
[_lblTotal.layer addAnimation:scaleAnimation forKey:#"scale"];
The following open source UILabel subclass does the scrolling numbers bit you are looking for.
https://github.com/dataxpress/UICountingLabel
Example of the type of usage is:
myLabel.method = UILabelCountingMethodLinear;
[myLabel countFrom:50 to:100 withDuration:5.0f];
I would look to see how that is done and then extend it to add the pulse at the end once the animation for the counting has completed.
yes this is possible.
add a variable with the number to increment
#property (nonatomic) double currentValue;
#property (nonatomic, retain) NSTimer *timer;
initialize it in viewDidLoad
_currentValue = 0.00;
write a function updateNumber
-(void)updateNumber {
_currentValue = _currentValue + 0.01;
[_label setText:[NSString stringWithFormat:#"%.2f", _currentValue]];
if(_currentValue >= 100.0) { //example to stop incrementation on 100
_timer = nil; // stop the timer
// do your animation
}
}
in your viewDidAppear() or whenever you want to start the counting like this
if(_timer == nil) {
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0 //seconds
target:self
selector:#selector(updateNumber)
userInfo:nil
repeats:true];
}
You might want to separate the incrementing number and the pulse animation into two different questions.
On the question of the pulse, the code is correct but the reason it scales off-center is probably due to your view's frame which is bigger than its content, which appears to be right-aligned rather than centered.
If you make _lblTotal's frame fit the content, by calling [_lblTotal sizeToFit]; your pulse animation should work as expected. But then this assumes that you do the right-alignment yourself.
Here is an example code , which will surely help you but you have to customize the timing as it certainly suit my need:-
-(void)viewDidLoad
{
currentValueAmount=0; //provide initial value for your incremented label
targetFloatAmount=1300;//provide the target value up-to which the value will be incremented.
self.lblAmount.text=[NSString stringWithFormat:#"%.2f",currentValueAmount];
[self performSelector:#selector(animateIncrementLabel) withObject:nil afterDelay:0.5f]; // give some delay as per your choice as to load the whole UI first and then calling the method to increment.
}
-(void)animateIncrementLabel
{
self.lblAmount.transform=CGAffineTransformMakeScale(0.000f, 0.000f);
timer = [NSTimer scheduledTimerWithTimeInterval: 0.001 //calling timer , if the label value is very small then increase the no of Zero's for example 0.00001
target: self
selector: #selector(handleTimer:)
userInfo: nil
repeats: YES];
//animating the incremented text with some transform and bounce
[UIView animateWithDuration:2.1 delay:0.2f usingSpringWithDamping:0.4 initialSpringVelocity:0.3 options:0 animations:^{
self.lblAmount.transform=CGAffineTransformMakeScale(1.3, 1.3);
}];
}
- (void) handleTimer: (NSTimer *) timer
{
if(currentValueAmount>targetFloatAmount)
{
self.lblAmount.text=[NSString stringWithFormat:#"%.2f",targetFloatAmount];
[timer invalidate];
timer=nil;
[UIView animateWithDuration:0.5 delay:0.2f usingSpringWithDamping:0.4 initialSpringVelocity:0.3 options:0 animations:^{
self.lblAmount.transform=CGAffineTransformMakeScale(1.0, 1.0);
}];
}
else
{
currentValueAmount=currentValueAmount+0.0008;
self.lblAmount.text=[NSString stringWithFormat:#"%.2f",currentValueAmount];
}
}

NSTimer : Update Label after switch View doesn't work

I want to use NSTimer as a Countdown. The counter works fine;-)!
But I I switch to another viewController the timer doesn't runs in "foreground" - so the label isn't updated.... after coming back to this view.
My NSLOG shows that the timer is still runnig, like I want ;-)
So in whicht way I have to work, that my timer update my label?
Here is the code:
- (IBAction)startClock:(id)sender {
globalTimer = 20;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(updateCircularProgressBar)
userInfo:nil
repeats:YES];
}
- (void)updateCircularProgressBar
{
// Values to be passed on to Circular Progress Bar
if (globalTimer > 0 && globalTimer <= 1200) {
globalTimer--;
minutesLeft = globalTimer / 60;
secondsLeft = globalTimer % 60;
[self drawCircularProgressBarWithMinutesLeft:minutesLeft secondsLeft:secondsLeft];
NSLog(#"Time left: %02d:%02d", minutesLeft, secondsLeft);
}
else {
[self drawCircularProgressBarWithMinutesLeft:0 secondsLeft:0];
[timer invalidate];
}
}
I tested to run the "updateCircularProgressBar" in my viewDidLoad but this shows NO result...
THX for Help
UPDATE more with more Code:
/*-------------------- TIMER --------------------*/
// Draws the progress circles on top of the already painted background
- (void)drawCircularProgressBarWithMinutesLeft:(NSInteger)minutes secondsLeft:(NSInteger)seconds
{
// Removing unused view to prevent them from stacking up
for (id subView in [self.view subviews]) {
if ([subView isKindOfClass:[CircularProgressTimer class]]) {
[subView removeFromSuperview];
}
}
// Init our view and set current circular progress bar value
CGRect progressBarFrame = CGRectMake(0, 0, 180, 180);
progressTimerView = [[CircularProgressTimer alloc] initWithFrame:progressBarFrame];
[progressTimerView setCenter:CGPointMake(160, 210)];
[progressTimerView setPercent:seconds];
if (minutes == 0 && seconds <= 0) {
[progressTimerView setInstanceColor:[UIColor magentaColor]];
}
// Here, setting the minutes left before adding it to the parent view
[progressTimerView setMinutesLeft:minutesLeft];
[progressTimerView setSecondsLeft:secondsLeft];
[self.view addSubview:progressTimerView];
progressTimerView = nil;
}
The label is in another file (CircularProgressTimer) which controls the progessbar...

inserting time delay with cocos2d

I am trying to add several labels that appear sequentially with a time delay between each. The labels will display either 0 or 1 and the value is calculated randomly. I am running the following code:
for (int i = 0; i < 6; i++) {
NSString *cowryString;
int prob = arc4random()%10;
if (prob > 4) {
count++;
cowryString = #"1";
}
else {
cowryString = #"0";
}
[self runAction:[CCSequence actions:[CCDelayTime actionWithDuration:0.2] ,[CCCallFuncND actionWithTarget:self selector:#selector(cowryAppearWithString:data:) data:cowryString], nil]];
}
the method that makes the labels appear is this:
-(void)cowryAppearWithString:(id)sender data:(NSString *)string {
CCLabelTTF *clabel = [CCLabelTTF labelWithString:string fontName:#"arial" fontSize:70];
CGSize screenSize = [[CCDirector sharedDirector] winSize];
clabel.position = ccp(200.0+([cowries count]*50),screenSize.height/2);
id fadeIn = [CCFadeIn actionWithDuration:0.5];
[clabel runAction:fadeIn];
[cowries addObject:clabel];
[self addChild:clabel];
}
The problem with this code is that all the labels appear at the same moment with the same delay. I understand that if i use [CCDelayTime actionWithDuration:0.2*i] the code will work. But the problem is that i might also need to iterate this entire for loop and have the labels appear again after they have appeared the first time. how is it possible to have actions appear with delay and the actions dont always follow the same order or iterations???
Maybe i did not really understand what you want to do. But if you need some control when your labels appear (to iterate something) make something like this:
-(void) callback
{
static int counter = 0;
//create your label and label action here
// iterate through your labels if required
counter++;
if (counter < 6)
{
double time = 0.2;
id delay = [CCDelayTime actionWithDuration: time];
id callbackAction = [CCCallFunc actionWithTarget: self selector: #selector(callback)];
id sequence = [CCSequence actions: delay, callbackAction, nil];
[self runAction: sequence];
}
else
{
//calculate the result and run callback again if required
//don't forget to write counter = 0; if you want to make a new throw
}
}
The problem is that your are scheduling all the actions to fire off at the same time.
Changing
[self runAction:[CCSequence actions:[CCDelayTime actionWithDuration:0.2] ,[CCCallFuncND actionWithTarget:self selector:#selector(cowryAppearWithString:data:) data:cowryString], nil]];
for
[self runAction:[CCSequence actions:[CCDelayTime actionWithDuration:0.2 * i] ,[CCCallFuncND actionWithTarget:self selector:#selector(cowryAppearWithString:data:) data:cowryString], nil]];
should fix your problem

Resources