I want my app to be able to count the number of taps every second. I assume this would have something to do with touchesBegan:..., but that doesn't work on a button, does it? Anyway, how would I measure the number of taps per second?
I think I could do it manually using a counter which resets every second but I was wondering if there is a better way.
Would it add the values to an array? And if so, would I be able to calculate an average excluding 0's?
My current code.
-(void) timer:(NSTimer *)averageTimer {
if(tapCountInLastSecond != 0) {
secondsElapsed++;
averageTapsPerSecond += tapCountInLastSecond / secondsElapsed;
tapCountInLastSecond = 0;
NSLog(#"Average: %f", averageTapsPerSecond);
}
}
in your viewController put those counters
int tapCountInPastSecond = 0;
float averageTapsPerSecond = 0;
int secondsElapsed = 0;
Then add this method that invokes when you the screen or tap a button
- (void)incrementTapCount
{
tapCountInPastSecond++;
}
Create a timer that will fire every second, doing it's calculations, then resets the tap count
- (void)timerActions
{
secondsElapsed++;
averageTapsPerSecond = (averageTapsPerSecond*(secondsElapsed-1) +tapCountInPastSecond) / secondsElapsed;
tapCountInpastSecond = 0;
}
Now you can init your timer like that:
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(timerActions) userInfo:Nil repeats:YES];
Then at any point you'll be able to get the average Taps/Second by reading the value averageTapsPerSecond
Hope this make sense to you
Related
Problem:
If you take a look at my current code, you'll see that it works fine if targetSeconds is higher than ~2-3 seconds.
However, it will not work if targetSeconds is 0.005 seconds because there's no way it can finish 100 method calls in 0.005 seconds. Therefore, does anyone have any suggestions to what I can do to improve it? I'd rather not include third party GitHub repositories.
Current code:
// Target seconds means the seconds that it'll take to become 100.0f.
- (void)startAnimatingWithTargetSeconds:(NSTimeInterval)targetSeconds{
// Try to set timeInterval to 0.005f and you'll see that it won't finish in 0.005f
[NSTimer scheduledTimerWithTimeInterval:targetSeconds / 100.0f target:self selector:#selector(animateWithTimer:) userInfo:nil repeats:YES];
}
- (void)animateWithTimer:(NSTimer *)timer {
BOOL isFinished = self.currentProgress >= 100;
if (isFinished) {
// Invalidate timer
if (timer.isValid) {
[timer invalidate];
}
// Reset currentProgress
self.currentProgress = 0.0f;
}else{
if (timer.isValid) {
self.currentProgress += 1;
}
}
}
// Overriden setter
- (void)setCurrentProgress:(CGFloat)currentProgress {
if (_currentProgress == currentProgress) {
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
_currentProgress = currentProgress;
[self setNeedsDisplay];
});
}
And then in drawRect, I have an UIBezierPath that basically draws the circle depending on self.currentProgress.
Something like this: CGFloat endAngle = (self.currentProgress / 100.0f) * 2 * M_PI + startAngle;
Question:
Is there any formula or anything that'll help me in my case? Because if I were to set self.currentProgress to self.currentProgress += 5; instead of 1, it'll animate a lot faster, which is precisely what I'm looking for.
First of all, when would you want to redraw every 0.005 seconds? That's 200 FPS, way more than you need.
Don't reinvent the wheel – leverage Core Animation! Core Animation already knows how to call your state change function at the proper rate, and how to redraw views as necessary, assuming you tell it what to do. The gist of this strategy is as follows:
Add a dynamic property to your layer that represents the completeness of your pie slice.
Tell Core Animation that this property can be animated by either overriding actionForKey:, or setting the animation into the actions dictionary (or even more options, detailed here).
Tell Core Animation that changes to this property require redraws of the layer using needsDisplayForKey:.
Implement the display method to redraw the pie based on the presentation layer's value of your dynamic property.
Done! Now you can animate the dynamic property from any value to any other, just as you would opacity, position, etc. Core Animation takes care of the timing and the callbacks, and you get a buttery smooth 60 FPS.
For some examples, see the following resources, listed in order of decreasing usefulness (in my opinion):
Animating Pie Slices using a custom CALayer – this is basically what you want to do
Animating Custom Layer Properties – better written but a bit less applicable
Apple's Core Animation Guide – esoteric, but worth a read if you want to master the strange beast that is Core Animation
Good luck!
I prefer use something like this, because timer (and usleep) on small intervals works very inaccurately:
-(void)startAnimatingWithTargetSeconds:(NSTimeInterval)targetSeconds
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
float fps = 60;
float currentState = 0;
float frameStateChange = fps/targetSeconds;
NSDate *nextFrameDate = [NSDate date];
while (currentState < 1) {
currentState += frameStateChange;
self.currentProgress = roundf(currentState * 100.);
nextFrameDate = [nextFrameDate dateByAddingTimeInterval:1./fps];
while ([nextFrameDate timeIntervalSinceNow] > 0) {
usleep((useconds_t)(100000/fps));
}
}
self.currentProgress = 0;
});
}
Ok so recently I made a project which had like a bit of a gravity timer,
int speed = 5;
NSTimer *gravitytimer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(gravity) userInfo:nil repeats:YES];
-(void)gravity {
image.center = CGPointMake(image.center.x, image.center.y - speed)
}
The problem is that the speed keeps multiplying or keeps adding and the image goes faster and faster. I don't know what the problem is. I am a newbie so sorry if this is a bad question. Thanks to all the people who take the time to answer.
When you create a timer, while it will try to call it every 0.01 seconds, you actually have no assurances that it will be called precisely at that rate (i.e. if it takes too long, it may skip one or two; if the main thread is blocked doing something else (and it's quite busy when the app first starts), you may skip quite a few). You can request to update image.center 100 times per second, but you have no guarantee that this is how often it actually will end up getting called.
If you wanted to use this technique to animate, you do so in a manner that isolates the speed of the animation from the frequency of the timer's selector is actually called. So, you might capture the start time, e.g.
CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
CGPoint startPoint = image.center;
And then in your timer routine, you'd capture the elapsed time, multiply your speed times the elapsed time, and go from there:
-(void)gravity {
CFAbsoluteTime elapsed = CFAbsoluteTimeGetCurrent() - self.start;
CGFloat speed = 100.0; // e.g. 100 points per second
CGPoint center = startPoint;
center.y += (elapsed * speed);
image.center = center;
}
As an aside, I assume this is just an exercise in timers. If this were a real animation, there are far more effective ways to do such animation (e.g. block-based animateWithDuration, UIKit Dynamics, and if you really wanted to do something timer-based, you'd often use CADisplayLink rather than a NSTimer). Also, BTW, the frame rate of the display is 60fps, so there's no point in trying to call it more frequently than that.
I'm trying to increment a percentage view using the following code:
// Progress
[UIView animateWithDuration:5.0 animations:^{
for (float percent = 0.0; percent <= 0.86; percent+=0.01) {
NSLog(#"%f", percent);
self.circularProgress2.progress = percent;
}
}];
It doesn't animate, it goes straight to the final value. I know I can use NSTimer:
[NSTimer scheduledTimerWithTimeInterval:0.0001 target:self selector:#selector(onLableAnimateTick:) userInfo:nil repeats:YES];
And then change the value from inside the called method. But I have several bars on the same view, so I guess using the first option would be better for me.
If not possible, how would I duplicate the NSTimer behavior without having to create properties and methods for every progress bar?
All the code inside the animation-block will be executed which is your case is a for-loop. Consider the following case:
[UIView animateWithDuration:5.0 animations:^{
CGRect f = aView.frame;
f.origin.x = 20;
f.origin.x = 25;
f.origin.x = 40;
aView.frame = f;
}];
This will animate the view from it's current position to x = 40. Not animate to 20, then to 25, then to 40. The result of the block is that aViews frames origin.x should be 40, no matter what happened before that.
I'd say use a timer if you want to auto-increment the progress with a given interval. You can still animate the change of course (for example by overriding the setProgress-method on your Progress-view class and call an animation from there), but don't use an animation block to increment the model value (progress).
Below is the back button, but the user should lose 1 point only once from pressing the button any other time he presses the button it won't deduct another point?
- (IBAction)btnback:(id)sender {
NSString *backspace = _textbox.text;
int lengthofstring = backspace.length;
if(lengthofstring > 0)
backspace = [backspace substringToIndex:lengthofstring -1];
_textbox.text = backspace;
if (lengthofstring < 2)
[_textXclear setHidden:YES];
score = score -1;
and it should only deduct 1 point after the first letter is typed!
How is this done right?
Use a bool.
Delcare a BOOL ivar like so BOOL minusPoint.
In your viewDidLoad initialize it: minusPoint = YES.
Then in your IBAction do:
if(minusPoint) {
//Minus a point
minusPoint = NO;
}
then set minusPoint to yes whenever you want to subtract a point again.
I have an animation of a drinking glass filling up from empty to full, using CAKeyframeAnimation. The animation works perfectly fine.
I need to update a UILabel with the percentage corresponding to the fullness of the glass, i.e. it will read "0%" when the animation begins, and "100%" when it ends.
The keyframe animation runs for 5 seconds. I don't see a good way of updating this label. I can think of two options:
starting another thread and running a polling-type loop that updates the text at the cost of processing power.
break down the animation into 100 parts and use CABasicAnimation to update the text and then proceed with the next bit of glass animation.
Is there a better way to do this?
Thanks in advance
You can use either a NSTImer or dispatch_after() blocks to update the label at some scheduled interval:
// assume an ivar "NSTimer *myTimer" and "int count", initially 0
// fires 20 times, so update is 0.05
myTimer = NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:#selector(updateLabel:) userInfo:nil repeats:YES
- (void)updateLabel:(NSTimer *)timer
{
++count;
if(count >= 20) {
[timer invalidate];
}
CGFloat percent += 0.05;
myLabel.text = [NSString stringWithFormat:...
}