I want to animate my UILabel such that it looks like its counting upward. For arguments sake lets just say I want it to go up by 1 every second.
None of the ways below worked properly.
A simple for loop (example here) doesn't work because its way too fast.
for(int i =0;i<1000;i++)
{
lblNum.text = [NSString stringWithFormat:#"%d",i];
}
Adding in sleep(1) doesn't work because the executions are asynchronous (I think thats why at least)
I also tried:
for(int i=0;i<1000;i++)
{
[self performSelector:#selector(updateLbl:)
withObject:[NSNumber numberWithInt:i ] afterDelay:1];
}
-(void)updateLbl:(NSNumber *)num
{
lblNum.text = [NSString stringWithFormat:#"%#",num];
}
As well as:
for(int i=0;i<1000;i++)
{
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// Do something...
sleep(1);
dispatch_async(dispatch_get_main_queue(), ^{
lblNum.text = [NSString stringWithFormat:#"%d",i];
});
});
}
NSTimer *timer = [NSTimer scheduleTimerWithTimeInterval:1.0 target:self selector:#selector(increment:) userInfo:label repeats:YES];
...
- (void)increment:(NSTimer *)timer {
UILabel *label = (UILabel *)timer.userInfo;
NSInteger i = label.text.integerValue;
i++;
label.text = [NSString stringWithFormat:#"%d", i];
if(someCondition){
[timer invalidate]//stops calling this method
}
}
I think a good way to do this is to use an NSTimer.
How do I use NSTimer?
The implementation is simple, just set repeat to YES and make the timer fire every second. You could have a variable keep track of count and increase it each time.
A good rule of thumb for programming: Never use sleep!!
Use NSTimer & NSRunLoop to perform animation in your code
timer_=[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(labelAnimation:) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:timer_ forMode:NSDefaultRunLoopMode];
- (void)increment:(NSTimer *)timer
{
if(isAnimationComplete)
{
[timer_ invalidate]//stops calling this method
}
else
{
//perform your action
}
}
Related
I want to show user that the value initially is 0 than it increase to 1 than 2 than 3 sequence repeat after some seconds
Worked done by me
I write this code in viewdidload but it print 99 in label directly but i want to show user changing values from 0 to 99.
for(int i=0;i<100;i++){
_totalEffort.text=[NSString stringWithFormat:#"%d",i];
}
I think this will help you:
#interface ViewController (){
int i;
NSTimer *myTimer;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
myTimer = [NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(setData)
userInfo:nil
repeats:YES];
i = 0;
}
-(void)setData{
_totalEffort.text=[NSString stringWithFormat:#"%d",i];
i = i + 1;
if(i == 100){
[myTimer invalidate];
myTimer = nil;
}
}
Based on your requirement you need to use NSTimer for update your label progress
Step 1: Create 2 variable in your #interface class
#interface BUViewController: UIViewController {
NSTimer *timerCount;
int progress;
}
Step 2: Start Timer in your viewDidLoad() with progress value 0.
progress = 0;
timerCount = [NSTimer timerWithTimeInterval:0.5 target:self selector:#selector(updateLabel) userInfo:nil repeats:YES];
Step 3: create method with timer update label text an check progress value in it and stop timer while its reach 100.
-(void)updateLable{
if (progress<100) {
lblProgress.text = [NSString stringWithFormat:#"%d",progress];
progress++;
}else{
[timerCount invalidate];
timerCount = nil;
}
}
Hope this will help you to achieve your expected output.
For this you can use NSTimer you need to update UILabel value in its selector method. You can use this code.
-(void)viewDidLoad{
[super viewDidLoad];
self.time = 0;
_labelTimer.text =#"0";
[self startTimer];
}
- (void)startTimer {
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timerAction:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
}
- (void)timerAction:(NSTimer *)timer {
self.time++;
if (self.time == 100)
{
[self stopTimer];
}
_labelTimer.text = [NSString stringWithFormat:#"%d",self.time];
}
-(void)stopTimer{
[self.timer invalidate];
self.timer = nil;
}
Here is code for your problem .hope it helps you.
- (void)viewDidLoad
{
[super viewDidLoad];
counter = 0;
t = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(updateLabel) userInfo:nil repeats:YES];
}
-(void)updateLabel
{
for (int i = counter; i<counter+1; i++)
{
_totalEffort.text=[NSString stringWithFormat:#"%d",i];
}
counter++;
if (counter ==101)
{
[t invalidate];
}
}
You are blocking run loop with your cycle. You just need resume it in each iteration.
for(int i=0;i<100;i++){
_totalEffort.text=#(i).stringValue;
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
}
Ok so basically, I'm trying to build some code that allows an NSNumber to reduce by 1 every minute,
This is what I have so far.
- (void)viewDidLoad {
[super viewDidLoad];
NSNumber *numVar = [NSNumber numberWithUnsignedChar:99];
[self num];
}
-(void)num{
usleep(60000000);
NSNumber *numVar = [NSNumber numberWithUnsignedChar:numVar-1];
[self num];
}
First off, it won't reduce by one every minute and secondly is it wrong to have the initial numVar in viewDidLoad?
We tried this and it worked fine for what we needed :
You need to re-arrange this in your code though
- (void)viewDidLoad{
NSTimer *timer;
int minutes = 0;
//Creating a label that shows the timer ; i assume you have one that is already linked
_labelTimer.text = [NSString stringWithFormat:#"This is %i minute", minutes];
//Creating the timer itself with the NSTimer object
timer = [NSTimer scheduledTimerWithTimeInterval: 60.0 target: self selector:#selector(decreaseTimeCount) userInfo:nil repeats:YES];
//Note that 60 is the number of seconds before it calls the selector. You can use 1 if you want it to change your timer/clock every second
}
- (void)decreaseTimeCount{
//The method changes the value of my variable and updates the label. If i'm at the 10th minute, i could do something, like perform a segue.
minute+=1;
_labTimer.text = [NSString stringWithFormat:#"This is %i minutes", minutes];
if (minutes == 10){
//do stuff
}
}
You sleep inside the main thread, blocking your UI. You need to use an NSTimer or similar timing mechanism to run the function periodically without blocking.
#implementation YourViewController
{
NSNumber *numVar;
}
- (void)viewDidLoad
{
[super viewDidLoad];
numVar = [NSNumber numberWithUnsignedChar:99];
[NSTimer scheduledTimerWithTimeInterval:60 target:self selector:#selector(timerFireMethod:) userInfo:nil repeats:YES];
}
- (void)timerFireMethod:(NSTimer *)timer
{
numVar = [NSNumber numberWithInt:[numVar intValue] - 1];
}
Whenever I need something fast I do it like this
- (void)viewDidLoad
{
[super viewDidLoad];
[self performSelector:#selector(num) withObject:nil afterDelay:60];
}
- (void)num
{
NSNumber *numVar = [NSNumber numberWithUnsignedChar:numVar-1];
[self performSelector:#selector(num) withObject:nil afterDelay:60];
}
And you can decide in the num function wheather you want to do it again after a minute or not.
In my viewController I inserted a timer for a calculation of numbers taken directly from my database of Parse.com.
The timer is working correctly, were inserted in viewDidAppear and in ViewDidDisappear in such a way as to lock the sequence when changing view controller.
The problem I have is that when I change the view controller with the Push the timer does not stop you give an example:
I open the application and the calculation is done correctly with the animation of NSTimer.
Change View, and then I go back with the back button to view where the timer at this point I see that the numbers contiunano to increase every time I change view and go back ...
Can you explain where I'm wrong?
-(void)viewDidAppear:(BOOL)animated {
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
[self.navigationController.navigationBar setBackgroundImage:[UIImage new]forBarMetrics:UIBarMetricsDefault];
self.navigationController.navigationBar.shadowImage = [UIImage new];
[self.navigationController.navigationBar setBackgroundColor:[UIColor clearColor]];
self.title = NSLocalizedString(#"", nil);
[self animatelayertopoint:0];
[self AnimationMenuView:600];
[self QueryForViewPrincipal];
[self QueryForCollectionView];
[self ShowBadgeNumber];
[self Timer];
[collectionView reloadData];
}
-(void)Timer{
timer = [NSTimer scheduledTimerWithTimeInterval:0.012 target:self selector:#selector(CalcoloMediadiLaurea) userInfo:nil repeats:YES];
}
-(void) CalcoloMediadiLaurea{
PFUser *CurrentUser = [PFUser currentUser];
NSNumber *NumeroUndici = [NSNumber numberWithInt:11];
NSNumber *NumeroTre = [NSNumber numberWithInt:3];
NSNumber *ValoreMediaPonderata = [CurrentUser objectForKey:FF_USER_MEDIA_PONDERATA];
int FFValoreTre = [NumeroTre intValue];
int FFValoreUndici = [NumeroUndici intValue];
double MediaPonderata = [ValoreMediaPonderata doubleValue];
double RisultatoMoltiplicazioneValoriPonderataUndici;
RisultatoMoltiplicazioneValoriPonderataUndici = MediaPonderata * FFValoreUndici;
double RisultatoMediaLaurea;
RisultatoMediaLaurea = RisultatoMoltiplicazioneValoriPonderataUndici / FFValoreTre;
CalcoloMediaLaureaLabel.text = [NSString stringWithFormat:#"%.1f", FFTickValoreMediaLaurea ];
FFTickValoreMediaLaurea++;
if(FFTickValoreMediaLaurea > RisultatoMediaLaurea){
[timer invalidate];
timer = nil;
}
}
-(void)viewDidDisappear:(BOOL)animated {
[self animatelayertopoint:0];
[self AnimationMenuView:600];
[self CalcoloMediadiLaurea];
}
The timer get rescheduled again on view did appear without getting invalidated to the previous one.You should make instance of timer in .h and then hold the reference of timer in it .And on view did appear when you are scheduling the timer just invalidate it and then schedule it again.
if([self.timer isValid])
{
[self.timer invalidate];
self.timer=nil;
self.timer=[NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(CalcoloMediadiLaurea:) userInfo:nil repeats:YES];
}
This worked for me which is along the lines of the above answer but a little more explicitly stated.
- (void)viewWillAppear:(BOOL)animated
{
NSTimeInterval secondsBetween = [endDate timeIntervalSinceDate:currentTime];
secondsLeft = secondsBetween;
[self countdownTimer];
}
- (void)viewDidDisappear:(BOOL)animated
{
[self.timer invalidate];
self.timer = nil;
}
//Initiates timer
- (void)countdownTimer
{
[self.timer invalidate];
self.timer = nil;
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateCounter:) userInfo:nil repeats:YES];
}
It's because you are creating new timers without invalidating old ones. The timers don't get invalidated automatically, you have to do it yourself. So if U go on second view and return before your invalidate condition is true you will create new timer and have two of them :)... And so on.
So here's my code :
- (IBAction)run:(id)sender {
animationPointer = 0;
self.animationArray = [[NSMutableArray alloc] init];
UIView *allViews = [[UIView alloc]
initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
for (int i = 0; i < [self.paths count]; i++) {
[allViews addSubview:[self createAnimation:[self.paths objectAtIndex:i]]];
[self.animationArray addObject:allViews];
}
timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(animateTurn)
userInfo:nil repeats:YES];
}
- (void)animateTurn {
UIView *tempView = [self.animationArray objectAtIndex:animationPointer];
[self.view addSubview:tempView];
for (UIImageView *view in tempView.subviews) {
[[tempView.subviews objectAtIndex:animationPointer] startAnimating];
}
animationPointer++;
if (animationPointer >= [self.animationArray count]) {
[timer invalidate];
}
}
createAnimation returns, as you would expect, a fully animated UIImageView. The plan is to have several on each UIView and then animate them all on a UIView then the next after half a second and it repeats. There's only one on each UIView for now.
However, when calling animateTurn on the NSTimer, the animations don't show up.
If I call the method via the normal [self animateTurn] then they appear but nothing with NSTimer nor the performSelector function.
Any help with this (or even suggestions for a better method)?
EDIT:
I should mention the function is actually being triggered and the code is being run including the startAnimating (checked with BOOL/NSLog). There is simply nothing being shown.
This is Your Solution:-
This is what I used and now the selector is Working. :-)
if (![NSThread isMainThread])
{ [self performSelectorOnMainThread:#selector(selectorToRunInMainThread) withObject:nil waitUntilDone:NO];
}
-(void)selectorToRunInMainThread
{
NSString *str = #"Timer event has fired";
NSTimer *atimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateModel:) userInfo:str repeats:YES];
}
Include this line after your timer:-
[timer fire];
I have this app which has a timer in it and I want to be able to display a button at random points in that timer. Sort Of like a game setting where you have to get the button to get more time before it disappears. I already have some code , but the problem is that the timer that i already have is being messed up ( i can tell because it is linked to a label. It messed up because it goes down by 2 instead of 1 and this only happens when i have the code to make the button appear and disappear with the timer.
Here is the code to my timer that is being messed up:
-(IBAction)Ready:(id)sender {
[self performSelector:#selector(TimerDelay) withObject:nil afterDelay:1.0];
[self performSelector:#selector(randomTimer) withObject:nil afterDelay:0.5];
}
-(void)TimerDelay {
MainInt = 36;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(countDownDuration)
userInfo:nil
repeats:YES];
}
Here is the code to the button:
-(IBAction)Ready:(id)sender { //the same ready action as above
[self performSelector:#selector(TimerDelay) withObject:nil afterDelay:1.0];
[self performSelector:#selector(randomTimer) withObject:nil afterDelay:0.5];
}
-(void)TimerDelay {
MainInt = 36;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(countDownDuration)
userInfo:nil
repeats:YES];
}
- (void)randomTimer {
NSInteger randomTime = arc4random_uniform(0); //Some number between 0 and 9
timer = [NSTimer scheduledTimerWithTimeInterval:randomTime
target:self
selector:#selector(showButton)
userInfo:nil
repeats:NO];
}
- (void)showButton {
if(self.plus5.hidden == YES) {
self.plus5.hidden = NO;
}
else {
self.plus5.hidden = YES;
}
[self TimerDelay]; //Call random timer function again to run this method at a future random time
}
You are calling "randomTimer" and "TimerDelay" (this should be "timerDelay") one after the other and scheduling and REscheduling "timer".
I think you should reconsider your architecture, here...
EDIT: Use just one timer and let it manage your button.
- (void)somewhereInYourCode
{
shouldShowButton = YES;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(timeFired)
userInfo:nil
repeats:YES];
}
- (void)timeFired
{
// Here your countdown and...
if (shouldShowButton) {
NSInteger randomTimeInt = arc4random_uniform(9); // as suggested by Bergasms
float randomTimeFloat = randomTimeInt;
[self performSelector:#selector(showButton) withObject:nil afterDelay:randomTimeFloat];
shouldShowButton = NO;
}
}
This way your timer won't (probably) mess up.
This:
arc4random_uniform() will return a uniformly distributed random number
less than upper_bound.
is one of your problems.
NSInteger randomTime = arc4random_uniform(0); //Some number between 0 and 9
needs to be
NSInteger randomTime = arc4random_uniform(9); //Some number between 0 and 9
I cannot say for the rest of your code. But the line you have will always return 0, which means the random timer will always execute instantly.