"scheduledTimerWithTimeInterval:" problem in cocos2d? - cocoa-touch

I am trying to developed a iPhone app by using cocos2d. I using "scheduledTimerWithTimeInterval" for calling a method that called a fixed time interval. But now time interval increases in gradually. for this reason the time is slow in gradually.
here is my code:
- (void) methodTime: (id) sender{
NSTimer *rat =[NSTimer scheduledTimerWithTimeInterval:(.5) target:self selector:#selector(rotation:) userInfo:nil repeats:YES];
}
- (void) rotation:(NSTimer *)theTimer{
NSLog(#"I m # %i", i);
i=i+10; // Here , i is a global int variable.
i=i % 1440;
if(i==0){
[theTimer invalidate];
}
else {
int rotationNum=i;
Sprite *sp = [Sprite spriteWithFile: #"1.png"];
sp.position=cpv(220,180.5);
sp.rotation=rotationNum;
[self add:sp];
}
}

It looks like every 0.5 seconds you are adding a sprite to some list of sprites. Eventually the list is getting very large and all that data causes your method to take longer than 0.5 seconds to execute. This causes the timer to fire fast as it can, which is not all that fast since its always waiting for your method to be finished with.
Without know more about your code, that's my best guess.

Don't use NSTimer. Check out cocos2d best practices.

I had trouble understanding your question. You haven't even used one quotation mark in your 'question'.
Is it possible that your code needs more than 0.5 seconds to execute? Especially because the amount of work done in the background increases each iteration. (More sprites.)
Try to make this function faster, for example load that PNG into memory and don't load it each time from the file. (That's better for memory anyway.)
Btw: That loop looks dangerously like an infinite loop. (Though could be that it works…)

If all you're doing is continually rotating the object, CATransforms and Core Animation might be a better bet.

I would use Cocos2d-iphone's built in intervals.
Go through the demo cocos project and you will see how things like this "should" be done in the cocos framework.

Related

Time Intervals in Objective C

I am very very new to Objective C and x-code. I just started learning it last week. so sorry if the question is too simple.
I am doing a game more like a visual novel so its very simple and story board based app, but I have a question regarding displaying my choices. I want the choices to appear after my cut scene ends. is there any way I can add something like time-interval breaks so they only appear after certain time. can some one please guide me on how to go about it.
You can use NSTimer:
[NSTimer scheduledTimerWithTimeInterval:2.0f
target:self
selector:#selector(displayChoiceTimerFired:)
userInfo:nil
repeats:NO];
This will schedule a timer that will execute the displayChoiceTimerFired: method after 2 seconds. (The argument of that method is the timer itself.)
Or you can also use NSObject's perform methods:
[self performSelector:#selector(displayChoice:) withObject:nil afterDelay:2.0f];
Depending on how you are displaying your cut scenes, the safer way to display these UI elements would be to somehow trigger an action when your cut scene ends. The problem with the NSTimer Implementation I can foresee is if, for whatever reason, your cut scene takes longer to play than your NSTimer interval is set to, then your UI Elements are displayed prematurely. Just my two cents on a different, but what I believe to be a more elegant approach.
[self performSelector:#selector(displayChoice:) withObject:nil afterDelay:1.0];
You can use this method...where after delay is number of seconds

How can I make an animated timer

Hi I need to make an animated timer like this:
for an iPhone Application using Objective C.
I really don't know what to do. Any Helps?
Thanks.
Have a look at DACircularProgress, it implements a circular progress view. As others said, you would increment the progress value with a NSTimer.
Just calculate the angle from the top using the ratio between elapsed time and the total time. Seems really simple. The timer can fire regularly and redo this calculation.
The image above looks like there are two circles with the same midpoint but slightly different radius superimposed on each other. The timer firing could also update this display.
It could also update the labels inside the circles, but I really do not understand "2 days 24 hours"....
That timer looks like it can be made using a pie chart with a mask on top of it.
Actually, you can use a png with the black circle transparent and draw the pie chart behind.
[edit] my answer assumes you know how to handle the timer part. If you want, I could also give you some info on that
This code starts a timer that would fire a function every two seconds
self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(timePassed) userInfo:nil repeats:YES];
You can then make a function like this
-(void)timePassed {
//draw a bigger pie chart than the previous
//you can store the previous size inside a class property or global var
}
Don't forget to stop the timer when you're done with it
[self.timer invalidate];
This is a relative question I answered before (if u are only focusing on using the NSTimer to do animation )
Objective c - NSTimer, CADisplayLink, Thread
Hope it helps you

Calling -setNeedsDisplay:YES from within -drawRect?

I am customizing my drawRect: method, which serves to draw a NSImage if it has been "loaded" (loading taking a few seconds worth of time because I'm grabbing it from a WebView), and putting off drawing the image till later if the image has not yet been loaded.
- (void)drawRect:(NSRect)dirtyRect
{
NSImage *imageToDraw = [self cachedImage];
if (imageToDraw != nil) {
[imageToDraw drawInRect:dirtyRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0 respectFlipped:YES hints:nil];
} else {
//I need help here
[self setNeedsDisplay:YES];
}
}
My question is how to do the latter. [self cachedImage] returns nil if the image is unavailable, but anytime within the next few seconds it may become available and at that time I want to draw it because the custom view is already on screen.
My initial instinct was to try calling [self setNeedsDisplay:YES]; if the image wasn't available, in hopes that it would tell Cocoa to call drawRect again the next time around (and again and again and again until the image is drawn), but that doesn't work.
Any pointers as to where I can go from here?
EDIT:
I am very much aware of the delegate methods for WebView that fire when the loadRequest has been completely processed. Using these, however, will be very difficult due to the structure of the rest of the application, but I think I will try to somehow use them now given the current answers. (also note that my drawRect: method is relatively light weight, there being nothing except the code I already have above.)
I currently have about 10+ custom views each with custom data asking the same WebView to generate images for each of them. At the same time, I am grabbing the image from an NSCache (using an identifier corresponding to each custom view) and creating it if it doesn't exist or needs to be updated, and returning nil if it is not yet available. Hence, it's not as easy as calling [view setNeedsDisplay:YES] from - (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame or another method.
My initial instinct was to try calling [self setNeedsDisplay:YES]; if the image wasn't available, in hopes that it would tell Cocoa to call drawRect again the next time around (and again and again and again until the image is drawn)
This would be incredibly inefficient, even if it worked.
anytime within the next few seconds it may become available and at that time I want to draw it
So, when that happens, call [view setNeedsDisplay:YES].
If you have no means of directly determining when the image becomes available, you'll have to poll. Set up a repeating NSTimer with an interval of something reasonable -- say 0.25 second or so. (This is also pretty inefficient, but at least it's running only 4 times per second instead of 60 or worse. It's a tradeoff between two factors: how much CPU and battery power you want to use, and how long the delay is between the time the image becomes available and the time you show it.)
my drawRect: method is relatively light weight, there being nothing except the code I already have above.
Even if you do nothing at all in -drawRect:, Cocoa still needs to do a lot of work behind the scenes -- it needs to manage dirty rects, clear the appropriate area of the window's backing store, flush it to the screen, etc. None of that is free.
Well, usually there is some delegate method that is called, when a download of something finishes. You should implement that method and call setNeedsDisplay:YES there.
The documentation for webkit:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/DisplayWebContent/Tasks/ResourceLoading.html#//apple_ref/doc/uid/20002028-CJBEHAAG
You have to implement the following method in your webview delegate:
- webView:resource:didFinishLoadingFromDataSource:
There you can call [view setNeedsDisplay:Yes]

Is there a way to programmatically "tap" UIControls such as segment buttons?

I hope that the answer to my question may be useful to a lot of developers and fear that it is a really stupid question: I have a routine that graphically and programmatically shuffle cards in 5 different ways (including cut and deal.) This is implemented from a UISegmentControl, replacing a 5-button Modal Alert. This all works very well but here is the problem: When it is the computer's turn to deal I built a for-loop inside a for-loop to trigger random number generated calls to the shuffling graphic routines a random number of times < 10. All of the calls seem to be executed for the entire for-loops sets before any graphics show up on the screen. Then I see face-down and face up cards moving all over the screen. Psychedelic, right? It seems that my control loop logic is executing without blocking or waiting or serializing the graphics. Remember that the graphics work fine when the user is tapping one segment at a time. I have been considering implementing a UITouchEvent to programmatically simulate tapping the buttons but I haven't found a single example. Is it possible to programmatically "tap" the segment "buttons"?
you can do something like:
- (void)simulateButtonPressed {
[button sendActionsForControlEvents:UIControlEventTouchDown];
button.highlighted = YES;
[NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:#selector(simulateButtonTimeElapsed:) userInfo:nil repeats:NO];
}
- (void)simulateButtonTimeElapsed:(NSTimer *)timer {
button.highlighted = NO;
[button sendActionsForControlEvents:UIControlEventTouchUpInside];
}

Observer pattern for stopwatch

I'm trying to implement a stopwatch based on the MVC model.
The stopwatch uses the NSTimer with the selector -(void) tick being called every timeout.
I've tried to make the stopwatch as a model for reusability but I've run into some design problems regarding how to update the view controller for each tick.
First I created a protocol with the tick method and made the view controller its delegate. The view controller then updates the views based on the timer's properties at each tick. elapsedTime is a readonly NSTimeInterval.
It works, but I'm thinking it might be bad design. I'm an Objective-C/Cocoa Touch beginner. Should I be using something like KVO? Or is there a more elegant solution for the model to notify the view controller that elapsedTime has changed?
The timer is a good way to make sure that you update your user interface periodically, but don't use it to keep track of time. NSTimer can drift, and any small errors can accumulate if you use a timer to accumulate seconds.
Instead, use NSTimer to trigger a method that updates your UI, but get the real time using NSDate. NSDate will give you millisecond resolution; if you really need better than that, consider this suggestion to use Mach's timing functions. So, using NSDate, your code might be something like this:
- (IBAction)startStopwatch:(id)sender
{
self.startTime = [NSDate date];
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(tick:)
userInfo:repeats:YES];
}
- (void)tick:(NSTimer*)theTimer
{
self.elapsedTime = [self.startTime timeIntervalSinceNow];
[self updateDisplay];
}
- (IBAction)stopStopwatch:(id)sender
{
[self.timer invalidate];
self.timer = nil;
self.elapsedTime = [self.startTime timeIntervalSinceNow];
[self updateDisplay];
}
Your code might be a little more sophisticated if you allow restarting, etc., but the important thing here is that you're not using NSTimer to measure total elapsed time.
You'll find additional helpful information in this SO thread.
I would recommend against KVO for this problem. It introduces a lot of complexity (and several annoying gotchas) for little benefit here. KVO is important in cases where you need to ensure absolutely minimal overhead. Apple uses it a lot in cases for low-level, high-performance objects like layers. It is the only generally-available solution that offers zero-overhead when there is no observer. Most of the time, you don't need that. Handling KVO correctly can be tricky, and the bugs it can create are annoying to track down.
There's nothing wrong with your delegate approach. It's correct MVC. The only thing you need to really worry about is that NSTimer doesn't make strong promises about when it's called. A repeating timer is even allowed to skip in some cases. To avoid that problem, you generally want to calculate elapsedTime based on the current time rather than by incrementing it. If the timer can pause, then you need to keep an accumulator and a "when did I last start" date.
If you need higher-accuracy or lower-cost timers, you can look at dispatch_source_set_timer(), but for a simple human-targeted stopwatch, NSTimer is fine, and an excellent choice for a simple project.
Lately, I have been using using blocks instead of plain old #selector's. It creates better and code and keeps the logic on the same location.
There's no native blocks support in NSTimer, but I used a category from https://gist.github.com/250662/d4f99aa9bde841107622c5a239e0fc6fa37cb179
Without the return selector, you keep the code in one spot:
__block int seconds = 0;
NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:1
repeats:YES
usingBlock:^(NSTimer *timer) {
seconds++;
// Update UI
if (seconds>=60*60*2) {
[timer invalidate];
}
}];