I and many others have a issue mathematically causing a function to be called more and more often. My goal is to call the code inside the if statement more and more often. The function is called every .01 seconds. I would like the first time it runs is at 1 second, then faster and faster until it holds off at about .3. I need to know what to put in the SOMETHING.
The Function is called every .01 seconds by a NSTimer.
The code is:
-(IBAction)redmaker:(id)sender{
refreshrate = refreshrate+1;
if(SOMETHING){
int rand =arc4random() %65;
UIButton *button = (UIButton *)[self.view viewWithTag:rand];
button.backgroundColor = [UIColor colorWithRed:255 green:0 blue:0 alpha:1];
button.enabled = YES;
deathtimes[rand] = 10;
rate = rate+1;
refreshrate = 0;
}
You should use an NSTimer to call your method. Define an NSTimer in your header file.
Class.h
NSTimer *timer;
double interval;
Class.m
//put the following two lines in viewDidLoad, or some other method
interval = 1.0
timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:#selector(redmarker:) userInfo:nil repeats:NO];
-----
//put this at the bottom of your if statement
if (interval > 0.3)
{
[timer invalidate];
//change this value to something greater to call the method faster
interval -= 0.05;
timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:#selector(redmarker:) userInfo:nil repeats:NO];
}
You may experience an issue that causes your game to slow down. If that occurs, then it is possible that the main thread is unable to handle the timer, the buttons, and other actions all at the same time. You will want to look into using Grand Central Dispatch.
Repeating timers always use the same time interval. You can't change it.
If you want a timer on a decreasing interval, create a non-repating timer that triggers a new timer with the same selector each time it fires. Use an instance variable to hold the interval, and subtract some amount from the interval value each time it fires.
As for your "if (SOMETHING)", nobody else can tell you the conditions in your code that would decide what to do.
Can't you use Grand Central Dispatch with a recursive method like this:
#import "ViewController.h"
#interface ViewController ()
{
CGFloat fireTime;
}
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
fireTime = 1.0;
// initial call to method
[self foo];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)foo
{
NSLog(#"Hello at, timer fired off after %lf", fireTime);
if (fireTime > 0.3)
{
// decrement timer
fireTime -= 0.1;
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(fireTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self foo];
});
}
#end
Related
I am making a game and I need to know if you can make a if statement that makes it like this:
if object.hidden = YES for 5 seconds{
do these things
}
Could someone please tell me if this is possible, and if so how this would work?
Put a timestamp on your object at the time it was hidden. Then convert your test into a "how long has this object been hidden". Pseudocode (since I don't actually have experience with objective C):
if hidden and elapsed time since hidden > 5 seconds:
do stuff
You can use CACurrentMediaTime(), for an accurate time interval.
sample:
CFTimeInterval start = CACurrentMediaTime();
for(int i=0; i<=10000; i++) {
NSLog(#"%i", i);
}
CFTimeInterval elapsed = CACurrentMediaTime() - start;
NSLog(#"elapsedTime : %f", elapsed);
Make sure you've added QuartzCore framework in your traget's settings
You could subclass Object and build that functionality in. Something like:
#interface ObjectSubclass : Object
#property (nonatomic, strong) NSTimer *timer;
#end
#implementation ObjectSubclass
-(void)setHidden:(BOOL)hidden{
[super setHidden:hidden];
if (hidden){
if (self.timer) {
[self.timer invalidate];
}
self.timer = [NSTimer timerWithTimeInterval:5
target:self
selector:#selector(doStuff:)
userInfo:nil
repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:self.timer
forMode:NSDefaultRunLoopMode];
} else {
[self.timer invalidate];
self.timer = nil;
}
}
I'm trying to create a simple countdown timer app for myself. So far I've figured out how to create the countdown timers with a stop/reset action on them for a single button I've got attached to the timer.
However, I would like to add multiple timers to the same page and I'm not really sure how to do about making extra calls for the timers. Each of the timers would have it's own number to count down from (7 minutes for one, 3 minutes for the other, etc). These are set intervals that the user is not able to change. Google hasn't really worked out for me on how to do this so I'm hoping someone can at least guide me in the right direction. Below is my code snippets:
ViewController.h
#interface ViewController : UIViewController {
IBOutlet UILabel *firstCountdownLabel;
NSTimer *firstCountdownTimer;
bool timerActive;
int secondsCount;
}
- (IBAction)start:(id)sender;
- (void)timerRun;
#end
ViewController.m
#interface ViewController ()
#end
#implementation ViewController
- (void) timerRun {
secondsCount = secondsCount - 1;
int minutes = secondsCount / 60;
int seconds = secondsCount - (minutes * 60);
NSString *timerOutput = [NSString stringWithFormat:#"%2d:%.2d", minutes, seconds];
firstCountdownLabel.text = timerOutput;
if (secondsCount == 0) {
[firstCountdownTimer invalidate];
firstCountdownTimer = nil;
}
}
//- (void) setTimer {
- (IBAction)start:(id)sender {
secondsCount = 420;
if (timerActive == NO) {
timerActive = YES;
self->firstCountdownTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timerRun) userInfo:nil repeats:YES];
}
else {
timerActive=NO;
[self->firstCountdownTimer invalidate];
self->firstCountdownTimer = nil;
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// [self setTimer];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Google doesn't help in showing you how to implement original application ideas.
If you want multiple timers, define multiple timer instance variables:
#interface ViewController : UIViewController
{
IBOutlet UILabel *timer1Label;
IBOutlet UILabel *timer2Label;
IBOutlet UILabel *timer3Label;
NSTimer *timer1;
NSTimer *timer2;
NSTimer *timer3;
int timer1Count;
int timer2Count;
int timer3Count;
bool timer1Active;
bool timer2Active;
bool timer3Active;
}
Then create a separate IBAction for each button that starts each of the timers:
- (IBAction)startTimer1:(id)sender
{
timer1Count = 420;
if (timer1Active == NO)
{
timer1Active = YES;
timer1 = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(timer1Run:)
userInfo:nil
repeats:YES];
}
else
{
timer1Active=NO;
[timer1 invalidate];
timer1 = nil;
}
}
- (void) timer1Run: (NSTimer*) timer
{
timer1Count -= 1;
int minutes = timer1Count / 60;
int seconds = timer1Count - (minutes * 60);
NSString *timerOutput = [NSString stringWithFormat:#"%2d:%.2d", minutes, seconds];
timer1Label = timerOutput;
if (timer1Count == 0) {
[timer2 invalidate];
timer2 = nil;
}
}
Duplicate the above code for each timer, using "timer2" and "timer3" in place of "timer1". Change the time counts for each one to the desired values. (I changed the names from "firstTimer" to "timer1" because it's easier to edit the code to support multiple timers that way.
I did not write 3 versions of each method for you because you need to figure this out rather than copy & pasting in code that you don't understand.
It would be possible, and require less code, to use the same IBAction method for all your start timer buttons, and have the code check the tag on the button to decide which timer to start.
The code might look like this:
- (IBAction)startTimer1:(id)sender
{
int tag = [sender tag];
switch (tag)
{
case 1: //timer 1
//Put code to start timer 1 here
break;
case 2: //timer 2
//put code to start timer 2 here
break;
}
}
But that might be a bit over your head at the moment.
By the way, forget you ever saw the "self->variable" syntax. it is slower and more error-prone than just referring to the instance variable directly. using object->variable syntax also allows you to access the instance variables of other objects, which is bad practice. You should always use properties to access the instance variables of objects other than yourself.
Also, the timer method should take a single parameter, a timer. I corrected the timer method in the above code.
Create a class as YourTimer with few properties like
NSString *timerLabel;
NSTimer *timer;
NSInteger timerCounter;
Now create an array of YourTimer objects. Then you can access it easily.
This will be modular, maintainable and reusable code, as may be later on you need one more identifier to be with all timers, hence wrap them in one class and use it.
I've created a custom Timer Class with public API allowing access to a property timeLeft, and allowing the calling class to start and pause the timer, as well as a Boolean isTimerPaused.
I need the Timer to be initialized and started, and paused inside of my game loop for various situations. So I've gone ahead with initializing my timer in my game (model) as such:
#define timerDuration 10
self.timer =[[Timer alloc] initWithTimerDurationInSeconds:timerDuration];
Here is a look at my Timer API:
#interface Timer : NSObject
-(id)initWithTimerDurationInSeconds:(NSTimeInterval)duration;
-(void)startTimer;
-(void)pauseTimer;
#property (nonatomic, getter = isTimerPaused)BOOL timerPaused;
#property (nonatomic)NSTimeInterval timeLeft;
#end
and my Timer implementation
#import "Timer.h"
#interface Timer ()
#property (nonatomic)NSTimeInterval timeRemaining;
#property (nonatomic)NSTimeInterval duration;
#property (strong, nonatomic) NSTimer *timer;
#property (strong, nonatomic)NSDate *targetTime;
#end
#implementation Timer
-(id)initWithTimerDurationInSeconds:(NSTimeInterval)duration
{
self = [super init];
if (self)
{
if (duration) _duration = duration;
else NSLog(#"Must Initialize Timer With Duration");
}
return self;
}
-(void)startTimer
{
self.targetTime = [NSDate dateWithTimeIntervalSinceNow:self.duration];
self.timer = [NSTimer scheduledTimerWithTimeInterval:.05 target:self selector:#selector(updateTimerLabel) userInfo:nil repeats:YES];
self.timerPaused = NO;
}
-(void)pauseTimer
{
self.timerPaused = !self.isTimerPaused;
if (self.isTimerPaused)
{
self.timeRemaining = [self.targetTime timeIntervalSinceNow];
[self.timer invalidate];
self.timer = nil;
}
else
{
self.targetTime = [NSDate dateWithTimeIntervalSinceNow:self.timeRemaining];
self.timer = [NSTimer scheduledTimerWithTimeInterval:.05 target:self selector:#selector(updateTimerLabel) userInfo:nil repeats:YES];
}
}
-(void)updateTimeLeft
{
self.timeLeft = [self.targetTime timeIntervalSinceNow];
if (self.timeLeft <=0)
{
[self.timer invalidate];
ago", -timeLeft];
}
}
#end
This all in theory will work great as I can start and stop my timer as needed inside of my game loop, and i can access time left to update my timer UILabel from my Controller.
My issue is this, If the timer were in my controller, I could simply update the label inside of the updateTimeLeft method. With the timer in the model how do I go about refreshing UI Elements continuously. My thought was to have some sort of continuous timer in my controller that would update the UILabel with the timeLeft property from the timer, but that seems inefficient and prone to being slightly inaccurate.
Thanks!
With the timer in the model how do I go about refreshing UI Elements continuously?
Move the timer out of your model. Seriously.
Every graphic representation of MVC you will come across has a nice solid line between the View layer and the Model layer, and there's a reason for that. If the model knows too much about the view, then your application becomes overly data-driven and bloated. The same goes for the opposite. Plug the timer into your controller, and route updates to the model from the controller to prevent any leakage of responsibilities into the wrong parts of your application.
I'm trying to make multiple buttons fall down the screen all at once, at different speeds. But when I have my if statement check if it passed through the value, all the other buttons disappear with it. I'm also incrementing the score to go -1 each time a button passes through the Y value. Any help is appreciated, thanks.
- (void)b1Fall
{
b1.center = CGPointMake(b1.center.x, b1.center.y+6);
if (b1.center.y >= 506) {
[self updateScore];
b1.center = CGPointMake(44, 11);
}
}
- (void)b2Fall
{
b2.center = CGPointMake(b2.center.x, b2.center.y+7);
if (b2.center.y >= 506) {
[self updateScore];
b2.center = CGPointMake(160, 11);
}
}
- (void)b3Fall
{
b3.center = CGPointMake(b3.center.x, b3.center.y+8);
if (b3.center.y >= 506) {
[self updateScore];
b3.center = CGPointMake(276, 11);
}
}
- (void)updateScore
{
healthLabel.text = [NSString stringWithFormat:#"%d", [healthLabel.text intValue]-1];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// REMOVE AFTER TESTING
b1Timer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:#selector(b1Fall) userInfo:nil repeats:true];
b2Timer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:#selector(b2Fall) userInfo:nil repeats:true];
b2Timer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:#selector(b3Fall) userInfo:nil repeats:true];
}
A few things:
1.) Put these animations in viewDidAppear instead of viewDidLoad - you want the animation to begin when the user is actually looking at the buttons, not when the view is loaded into memory, where the user may not be able to see it (viewDidLoad).
2.) Don't use NSTimer for animations, use this:
[UIView animateWithDuration:0.5
delay:1.0
options: UIViewAnimationCurveEaseOut
animations:^{
}
completion:^(BOOL finished){
// loop your animation here.
}];
3.) If you want to make a game, I don't recommend using this approach. All animations using UIKit are going to perform on the main thread and block the user flow. What you want is a framework like Cocos2D where the animations are doing in the GPU and ASYNC game logic is readily supported. UIKit isn't a good choice for game development in general.
I just want to run a certain method for 20 seconds when user presses start button and stop it by it's own rather than using a button to do it. But it should also trigger when ever the user press start button after 20 seconds in next turn.
How can i use NStimer to implement this?
Sorry for not posting any codes
Thanks in advance!
// Assume ARC - otherwise do the proper retains/releases
{
NSTimer *timer; // keep a reference around so you can cancel the timer if you need to
NSDate *timerDate;
}
- (IBAction)buttonPressed:(id)sender
{
// disable button til the time has passed
[sender setEnabled:NO];
// make sure its diabled til we're done here
timerDate = [NSDate date];
NSTimeInterval interval = 1; // 1 gets you a callback every second. If you just wnat to wait 20 seconds change it
// schedule it
timer = [NSTimer ]scheduledTimerWithTimeInterval:time target:self selector:#selector(myTimeRoutine:) userInfo:nil repeats:YES];
}
- (void)myTimeRoutine:(NSTimer *)timer
{
// do something ...
NSTimeInterval interval = -[timerDate timeIntervalSinceNow]; // return value will be negative, so change sign
if(interval >= 20) {
// I'm done now
myButton.enabled = YES; // so it can be tapped again
[timer cancel]; // dont want to get any more messages
timer = nil; // ARC way to release it
timerDate = nil;
}
}