I'm creating this app that converts text to Morse code and then flash it out using the iPhone's flashlight. I have used string replacement to convert the content of a NSString to Morse code. I have found a script that turns the iPhone's flashlight on and off, with adjustable intervals using NSTimer. But I can't figure out how to add two different intervals, one for the morse "." and one for the morse "-". Can anyone help me?
- (void)viewDidLoad {
[super viewDidLoad];
int spaceTime;
spaceTime = 1;
int flashTimePrik;
flashTimePrik = 5;
strobeIsOn = NO;
strobeActivated = NO;
strobeFlashOn = NO;
flashController = [[FlashController alloc] init];
self.strobeTimer = [
NSTimer
scheduledTimerWithTimeInterval:spaceTime
target:self
selector:#selector(strobeTimerCallback:)
userInfo:nil
repeats:YES
];
self.strobeFlashTimer = [
NSTimer scheduledTimerWithTimeInterval:flashTimePrik
target:self
selector:#selector(strobeFlashTimerCallback:)
userInfo:nil
repeats:YES
];
}
- (void)strobeTimerCallback:(id)sender {
if (strobeActivated) {
strobeIsOn = !strobeIsOn;
strobeFlashOn = YES;
} else {
strobeFlashOn = NO;
}
}
- (void)strobeFlashTimerCallback:(id)sender {
if (strobeFlashOn) {
strobeFlashOn = !strobeFlashOn;
[self startStopStrobe:strobeIsOn];
} else {
[self startStopStrobe:NO];
}
}
Just use one timer, set the time interval based on the dot, dash or space interval. For "A" which is dot space dash
Turn on the light and set that timer to the dot interval.
When the timer fires, turn the light off and set the timer to the space interval.
When the timer fires, turn the light on and set the timer to the dash interval.
When the timer fires, turn the light off.
Use a bajillion timers. All single fire mode. Want an short call call to flash short. Then in the timer call back, create another timer for the next dash or dot. When there are no more signals to transmit in the array, you are done. Code is approximate....
- (void)lightTimerOffCallback:(id)sender {
turnLIGHTOFF
[NSTimer scheduledTimer:intervalbeforeStartingNextChar... selector(#startNextDotOrFlash) repeat NO]
}
- (void)startNextDotOrFlash:(id)sender {
if (there is a new dot or dash to do)
intervalToLeaveThisLightOn = 1.0 : 0.1 ? isDot;
turnLIGHTON
[NSTimer scheduledTimer:intervalToLeaveThisLightOn... selector(#lightTimerOffCallback) repeat NO]
}
Don't need the timer in an iVar.
Related
I'm trying to make a cocos2D game in which every time the player touches the screen, the background scrolls to the left thus simulating moving forwards.
The "background" consists of 2 very long rectangular panels called pane1 and pane2 linked together in a chain. When the screen is touched, I use CCActionMoveTo to move both panes to the left, and when one pane is completely off the screen, I move it back around to the other side to create an infinite loop.
The problem is the background scrolling animation takes .2 seconds, and if the player just mashes the screen a lot, it messes up everything. And, sometimes these two panes experience different amounts of lag so that they desync.
How do I set a delay on a function so that it can only be called once per designated time period? In other words, I want to add a "cooldown" to the function that handles player touch.
This function is called every time the scene is touched:
- (void)playerMove {
CCActionMoveTo * actionMove1 = [CCActionEaseOut actionWithAction:
[CCActionMoveTo actionWithDuration:.2
position:ccp(pane1.position.x - 150, 0)]
rate: 1.5];
CCActionMoveTo * actionMove2 = [CCActionEaseOut actionWithAction:
[CCActionMoveTo actionWithDuration:.2
position:ccp(pane2.position.x - 150, 0)]
rate: 1.5];
CCActionCallFunc * actionCallGenerateTerrain = [CCActionCallFunc actionWithTarget: self selector:#selector(generateTerrain)];
counter++;
if(pane1InUse){
[pane1 runAction: [CCActionSequence actionWithArray:#[actionMove1, actionCallGenerateTerrain]]];
[pane2 runAction: actionMove2];
}
else
{
[pane1 runAction: actionMove1];
[pane2 runAction: [CCActionSequence actionWithArray:#[actionMove2, actionCallGenerateTerrain]]];
}
}
-(void) generateTerrain {
if (counter%8 == 0){
pane1InUse ^= YES;
CCLOG(#"%#", pane1InUse ? #"YES" : #"NO");
if (pane1InUse){
CCLOG(#"Generating Terrain 2 ...");
pane2.position = ccp(pane1.position.x+pane1.boundingBox.size.width, 0);
}
else{
CCLOG(#"Generating Terrain 1 ...");
pane1.position = ccp(pane2.position.x+pane2.boundingBox.size.width, 0);
}
}
}
Also you may have noticed I'm using this weird block of code because ideally I would like actionMove1 and actionMove2 to be executed simultaneously, and once they are both done, then execute actionCallGenerateTerrain, but I don't know how to implement that:
if(pane1InUse){
[pane1 runAction: [CCActionSequence actionWithArray:#[actionMove1, actionCallGenerateTerrain]]];
[pane2 runAction: actionMove2];
}
else
{
[pane1 runAction: actionMove1];
[pane2 runAction: [CCActionSequence actionWithArray:#[actionMove2, actionCallGenerateTerrain]]];
}
}
You probably want to put pane1 and pane2 in a ccNode , call it combinedPanes (this will make pane1 and pane2 always move in sync). Then perform a single action on combinedPane. Also add a boolean state property, call it moveEnabled;
id movePane = [CCActionMoveTo ... ]; // whatever you have for pane1 or pane2
id moveComplete = [CCActionBlock actionWithBlock:^{
[self generateTerrain];
self.moveEnabled = YES;
}];
self.moveEnabled = NO;
[movePane runAction:[CCActionSequence actions:movePane,moveComplete,nil]];
and use moveEnabled to allow/deny the touch processing that detected a touch and triggered this move code. During the move, this will drop touches and effectively block your hysterical user tapping like nuts.
-(void) playerMove {
if (self.moveEnabled){
//
// the rest of your logic
// ...
}
}
and in init (or if you detect this condition already, place it there).
self.moveEnabled = YES;
One simple way to prevent a function being called twice within a time period is to wrap it in an if statement like:
if ([[NSDate date] timeIntervalSinceDate:lastUpdated] > minDelay)
{
// call function here
lastUpdated = [NSDate date];
}
where lastUpdated is initialized using [NSDate date] and minDelay is the required minimum delay in seconds.
I m trying to write a loop to check that whenever audio player.currenttime increases by 2 seconds then it should execute update view method
- (void)myTimerMethod{
NSLog(#"myTimerMethod is Called");
myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(checkPlaybackTime:)
userInfo:nil
repeats:YES];
}
- (void)checkPlaybackTime:(NSTimer *)theTimer
{
float seconds = audioplayer.currenttime;
NSLog(#"Cur: %f",audioPlayer.currentTime );
if (seconds = seconds + 2){
[self update view];
}
- (void)UpdateView{
if (index < [textArray count])
{
self.textView.text = [self.textArray objectAtIndex:index];
self.imageView.image = [self.imagesArray objectAtIndex:index];
index++;
}else{
index = 0;
}
}
what is the correct way to write if audio player.currenttimer increases by 2 seconds then do this.
NSLog for current time always shows 0.00. Why is that. It should increase as the audioplayer is playing.
Thanks for help.
What i understood from your given explanation that you want to increment the time-interval something like this
Timer calls after 0.55
Timer calls after 0.60
Timer calls after 0.65
Timer calls after 0.70
& so on.
If that is what you are looking to do. Then i think you can do this way that by changing repeats:YES to repeats:NO so that the timer doesn't repeat, and then in onTimer, just start a new timer with a longer interval.
You need a variable to hold your interval so that you can make it a bit longer each time through onTimer.
Also, you probably don't need to retain the timer anymore, as it will only fire once, and when it does, you'll get a new timer.
float gap = 0.50;
[NSTimer scheduledTimerWithTimeInterval:gap target:self selector:#selector(onTimer) userInfo:nil repeats:NO];
-(void) onTimer {
gap = gap + .05;
[NSTimer scheduledTimerWithTimeInterval:gap target:self selector:#selector(onTimer) userInfo:nil repeats:NO];
}
Hope this helps you
First, try using your float "seconds" in your NSLog rather than the current time.
NSLog(#"Cur: %f", seconds);
current time is not a float, it's an NSTimer object so you would have to use %# in your NSLog text so
NSLog(#"Cur: %#",audioPlayer.currentTime );
Should work as well.
Assuming your audioPlayer is set up correctly, if you're looking for when the timer is at 2 seconds, your if statement will be
if(seconds == 2){
[self update view];
}
if you're looking for each time the timer hits an even number, i.e. 2, 4, 6, etc. your if statement will be
if(seconds % 2 == 0){
[self update view];
}
The % in an if statement is the modulo sign: http://www.cprogramming.com/tutorial/modulus.html
Also, your current if statement is assigning rather than checking the seconds variable. To check it, you need == not =. However, your current if statement will never be true since you're checking a variable by itself + 2. To put this another way, if seconds equals 2, your if statement is asking if 2 == (2+2) or if it it is 4, it's asking if 2 == (4+2). This statement cannot validate as true.
Hope this helps!
Right now I have a NSArray with x amount of coordinates inside; I have a sprite that needs to go to each of the points to go along the selected path. I tried using a for loop, but that does it in such quick succession that it appears to just teleport to the final destination. I've tried a with selectors but I can't get those to work either. Anyone know how to do this?
you have to use an NSTimer
#implementation whatever
{
int count;
NSTimer *myTimer;
CCSprite *mySprite;
NSArray *locationArray;
}
and then start the timer fromsomewhere...
count=0
//1.0 is one second, so change it to however long you want to wait between position changes
myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(movealong) userInfo:nil repeats:YES];
And then it calls this until it goes through all the object
-(void)movealong{
//assuming array is full of CGPoints...
//mySprite.position = [locationArray objectAtIndex:count];
//try this then
CGPoint newPoint = [locationArray objectAtIndex:count];
mySprite.position = ccp(newPoint.x,newPoint.y);
//I think maybe THIS is what you need to do to make it work...
//if you want it to not jump directly there
//you can also use CCMoveTo
/*// under actionWithDuration: put the same amount of time or less of what you had
// under scheduledTimerWithTimeInterval in your NSTimer
CCFiniteTimeAction *newPos = [CCMoveTo actionWithDuration:1.0 position:[locationArray objectAtIndex:count]];
[mySprite runAction:newPos];
*/
count++;
if(count >= locationArray.count){
[myTimer invalidate];
myTimer = nil;
}
}
Create a method that will place your sprite to position that is at some index (for example, _i) in the positions array. And at the end of this method call it again with delay, using CCDelayTime and CCCallFunc actions in sequence. And do not forget to increment index. Smth like
// somewhere in code to start move
_i = 0;
[self setNewPosition];
// method that will set the next position
- (void) setNewPosition
{
sprite.position = // get position at index _i from your array here
_i++;
BOOL needSetNextPosition = // check if _i is inside bounds of your positions array
if( needSetNextPosition )
{
id delay = [CCDelayTime actionWithDuration: delayBetweenUpdate];
id callback = [CCCallFunc actionWithTarget: self selector: #selector(setNewPosition)];
id sequence = [CCSequence actionOne: delay two: callback];
[self runAction = sequence];
}
}
This is just example, but I hope, you can adapt it for your needs.
Hello every time i try to record the date with CFAbsoluteTimeGetCurrent(); my app ignores the rest of the buttons, it's as if it takes over all memory and blocks all user input. I was wondering what i am doing wrong? I thought of making a function showtime() but i dont know how to pass values between functions from toggleRecording to showtime so that my method would work if this would even solve the problem. Below is my code:
- (IBAction)toggleRecording:(id)sender
{
// Start recording if there isn't a recording running. Stop recording if there is.
[[self recordButton] setEnabled:NO];
if (![[[self captureManager] recorder] isRecording]){
[[self captureManager] startRecording];
/* figure out a day to record for every half a second
while([[[self captureManager]recorder] isRecording]){
CFTimeInterval startTime = CFAbsoluteTimeGetCurrent();
NSLog(#" time is %i", startTime);
}
*/
}
else
[[self captureManager] stopRecording];
}
-(void)showtime:(id)sender{
while([[[self captureManager]recorder] isRecording]){
CFTimeInterval startTime = CFAbsoluteTimeGetCurrent();
NSLog(#" time is %f", startTime);
}
}
An application has to run the event loop to receive the events. While your own code is doing something else (here it's doing a "while" loop), events are queued and not delivered until your code returns.
Schematically, an application is doing something like:
while(1) {
event = _UIReceiveNextQueuedEvent();
_UIProcessEvent(event); // <- this calls -showTime:
}
If you want to record time while not blocking the loop, you will have to schedule an NSTimer every 0.5s and invalidate it as soon as recording is turned off.
Something like:
- (void)showTime:(id)sender
{
if ([[[self captureManager]recorder] isRecording]) {
[NSTimer scheduledTimerWithTimeInterval:0.5f target:self selector:#selector(timerFired:) userInfo:nil repeats:YES];
}
}
- (void)timerFired:(NSTimer *)timer
{
if ([[[self captureManager]recorder] isRecording]) {
CFTimeInterval startTime = CFAbsoluteTimeGetCurrent();
NSLog(#" time is %f", startTime);
} else {
[timer invalidate];
}
}
I've created a countdown timer and it works great.. but when it reaches zero it continues counting down.. so it shows -1, -2, -3 etc. How do I prevent it from doing this?
This is my code from the implementation file..
#implementation ViewController
-(IBAction)start {
myTicker = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(showActivity) userInfo:nil repeats:YES];
}
-(IBAction)stop {
[myTicker invalidate];
}
-(IBAction)reset {
time.text = #"0";
}
-(void)showActivity {
int currentTime = [time.text intValue];
int newTime = currentTime -1;
time.text = [NSString stringWithFormat:#"%d", newTime];
}
I'm guessing that I'm missing some code somewhere?
Quick fix is to change:
int newTime = currentTime - 1;
into:
int newTime = (currentTime > 0) ? currentTime - 1 : 0;
That will stop the countdown timer from going below zero. If that's all you need, that should suffice.
It won't turn off the timer or execute an action when it reaches zero. For that, you'll need to add something like the following block after that line above:
if ((currentTime > 0) && (newTime ==0)) {
// optionally turn off timer
fireZeroEvent ();
}
and provide the fireZeroEvent() function to do whatever you need to do.
I say optionally turn off the timer since you may want to leave it running so you can restart the countdown by just be setting time.text without have to recreate the timer.
For that reason, the fireZeroEvent() is only called on the transition from 1 to 0, not every time the timer fires after it reaches zero.