UIProgressView not updating for Audio Player? - ios

The ProgressView is not updating while playing the song. By reading from other post i have used an update method to update at each time interval.
- (void)viewDidLoad {
[super viewDidLoad];
NSString *filePath = [[NSBundle mainBundle]pathForResource:#"Fix you_Coldplay" ofType:#"mp3" inDirectory:#"Coldplay"];
NSLog(#"%#",filePath);
NSURL *url = [NSURL fileURLWithPath:filePath];
NSLog(#"%#",url);
musicPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:url error:nil];
[musicPlayer prepareToPlay];
[trackProgress setProgress:0.0];
}
I have a play button which is also being used for calling updateTime: method.
- (IBAction)playButton:(id)sender {
[musicPlayer play];
[NSTimer scheduledTimerWithTimeInterval:0.0 target:self selector:#selector(updateTime:) userInfo:nil repeats:YES];
NSTimeInterval interval = musicPlayer.currentTime/musicPlayer.duration;
NSLog(#"%f",interval);
}
The updateTime: method
-(void)updateTime:(NSTimer *)timer {
trackProgress.progress = (musicPlayer.currentTime/musicPlayer.duration);
}
I am very new to programming and even stack overflow. This is the first question i am posting. Please help me and Thanks in advance.

First of all change timer value to:
//Change Value to 0.5 or 1 sec
[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(updateTime:) userInfo:nil repeats:YES];
Make sure you are setting trackProgress as 0 as initial value.
- (void)viewDidLoad {
[super viewDidLoad];
trackProgress.progress = 0.0;
}
Hope this will resolve your issue.

Make sure to update the progress view on the main thread
-(void)updateTime:(NSTimer *)timer {
dispatch_async(dispatch_get_main_queue(), ^{
self.trackProgress.progress = (musicPlayer.currentTime/musicPlayer.duration);
});
}

This might help. Create a Timer and add it to common runloop as shown below. Also keep a reference to timer so that it can be stopped later.
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateTime:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];

Related

Can I use a NStimer in another NStimer?

I create a NSTimer:
timer = [NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:#selector(sendImage) userInfo:nil repeats:YES];
In the method sendImage, I create another NSTimer timer2. The code following:
- (void)sendImage
{
for(int i = 0;i < 50 ; i++)
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:socket forKey:#"socket"];//parameter which deliver to the method`sendPieceOfImage`
[dict setObject:pData forKey:#"pData"];
timer2 = [NSTimer scheduledTimerWithTimeInterval:0.002f target:self selector:#selector(sendPieceOfImage:) userInfo:dict repeats:NO];
}
}
But it didn't work. I want to know can NSTimer apply mechanically? If it's infeasible, what can I do in the sendImage. I hope every cycle in the for() can run with interval.
The answer to your question is YES. That's possible to schedule one timer in the firing callback of another. Consider this:
dispatch_async(dispatch_get_main_queue(), ^{
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(timer1Event:) userInfo:nil repeats:YES];
});
- (void)timer1Event:(NSTimer*)timer {
NSLog(#"timer1Event");
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(timer2Event:) userInfo:nil repeats:NO];
}
- (void)timer2Event:(NSTimer*)timer {
NSLog(#"timer2Event");
}
Even the problem is not fully described, I'll try to guess that the rootcause is in how very first timer is scheduled.
You need thread with aproprietaly setted up RunLoop. Main thread is suitable.
dispatch_async(dispatch_get_main_queue(), ^{
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:#selector(sendImage) userInfo:nil repeats:YES];
});
If you don't want to load the main thread, consider this:
You need your own thread:
self.workerThread = [[NSThread alloc] initWithTarget:self selector:#selector(startRunLoop) object:nil];
[self.workerThread start];
Which starts the runloop:
- (void)startRunLoop
{
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
do {
#autoreleasepool
{
[runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
}
} while (![NSThread currentThread].isCancelled);
}
Now, for starting the timer on the worker thread you need this:
- (void)startTimer
{
self.timer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:#selector(timerEvent:) userInfo:nil repeats:YES];
}
How to call:
[self performSelector:#selector(startTimer) onThread:self.workerThread withObject:nil waitUntilDone:NO];
Hope it helps.

Managing Timers Passed as Variables

I am trying to perform what I thought was a simple task. I have a handful of repeating timers that need to be started and stopped.
I created methods for starting and stopping the timers, and attempt to pass the timers to the methods as parameters.
The problem is that the timers never seem to be stopped. Any ideas why this might not be working properly? Thank you!
Top of File:
#import "ViewController.h"
NSTimer *launchTimer;
NSTimer *transactionTimer;
Starting Method:
-(void) startingMethod {
NSString *urlString = #"http://google.com";
[[AsynchRequestService sharedInstance]
performAsynchronousURLRequest:urlString completion:^(BOOL success,
NSString *responseBody, NSString *responseStatus) {
if (success) {
[self stopResponseTimer:launchTimer];
}
else {
[self startResponseTimer:launchTimer
method:#selector(startingMethod)
interval:10];
}
}];
}
Method to Start the Timer:
-(void)startResponseTimer:(NSTimer *) timer method:(SEL) method
interval:(int) interval {
[timer invalidate];
timer = nil;
timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self
selector:method userInfo:nil repeats:YES];
}
Method to Stop the Timer:
-(void)stopResponseTimer:(NSTimer *) timer {
NSLog(#"STOP TIMER");
[timer invalidate];
timer = nil;
}
Make startResponseTimer and `stopResponseTimer' take the pointer to the object pointer intead.
-(void)startResponseTimer:(NSTimer **) timer method:(SEL) method
interval:(int) interval {
[*timer invalidate];
*timer = nil;
*timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self
selector:method userInfo:nil repeats:YES];
}
-(void)stopResponseTimer:(NSTimer **) timer {
NSLog(#"STOP TIMER");
[*timer invalidate];
*timer = nil;
}
then invoke it like
[self startResponseTimer:&launchTimer];
[self stopResponseTimer:&launchTimer];
This should make sure that you retain the right NSTimer object.
NOTE:It is always a good idea to check a pointer to a pointer to an object for NULL in a public method

how to make NSTimer consistent on xcode

I'm making a game and would like to use a timer to countdown an event, just like what's seen on Bejeweled. I know that I've to put NSTimer in a NSRunLoop to make it work, since NSTimer is inaccurate. Have tried the following but it still don't work. Please help!
#import ...
NSTimer *_gameTimer;
int secondsLeft;
//some code
//called countdownTimer using [self countdownTimer];
- (void)countdownTimer
{
_gameTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateTime:) userInfo:nil repeats:YES];
NSRunLoop *gameRun = [NSRunLoop currentRunLoop];
[gameRun addTimer:_gameTimer forMode:NSDefaultRunLoopMode];
}
- (void)updateTime:(NSTimer *)timer
{
if (secondsLeft>0 && !_gameOver) {
_timerLabel.text = [NSString stringWithFormat:#"Time left: %ds", secondsLeft];
secondsLeft--;
} else if (secondsLeft==0 && !_gameOver) {
// Invalidate timer
[timer invalidate];
[self timerExpire];
}
}
- (void)timerExpire
{
// Gameover
[self gameOver];
[_gameTimer invalidate];
_gameTimer = nil;
}
NSTimer needs to be a local variable so there can only be one instance of the object running through the loop. Here's the code.
- (void)countdownTimer
{
NSTimer *_gameTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateTime:) userInfo:nil repeats:YES];
NSRunLoop *gameRun = [NSRunLoop currentRunLoop];
[gameRun addTimer:_gameTimer forMode:NSDefaultRunLoopMode];
if (secondsLeft==0 || _gameOver) {
[_gameTimer invalidate];
_gameTimer = nil;
}
}
- (void)updateTime:(NSTimer *)timer
{
if (secondsLeft>0 && !_gameOver) {
_timerLabel.text = [NSString stringWithFormat:#"Time left: %ds", secondsLeft];
secondsLeft--;
} else if (secondsLeft==0 || _gameOver) {
// Invalidate timer
[timer invalidate];
[self timerExpire];
}
- (void)timerExpire
{
// Gameover
[self gameOver];
}

Second NSTimer not working?

I previously had a single NSTimer which is working fine. I have added another NSTimer to my runloop as now I need to call two functions repeatedly after a delay. Both functions have a different delay. My code is given below.
self.now = [NSDate date] ;
self.timer = [[NSTimer alloc] initWithFireDate:self.now
interval:500
target:self
selector:#selector(Func1)
userInfo:nil
repeats:YES] ;
self.runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:self.timer forMode:NSDefaultRunLoopMode];
[self.runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10000]];
//Second timer start here.its not working.The function 'func2' is not getting called
self.now = [NSDate date] ;
self.timer = [[NSTimer alloc] initWithFireDate:self.now
interval:60
target:self
selector:#selector(Func2)
userInfo:nil
repeats:YES] ;
self.runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:self.timer forMode:NSDefaultRunLoopMode];
[self.runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10000]];
The first NSTimer is still working but the second NSTimer doesn't work.
While it's not good that you're overwriting your properties (it doesn't affect the timers' ability to run, individually, but it just means that you're discarding your reference to the first timer when you set the second timer, and thus you lose the ability to invalidate the first timer, should you ever need to do that), the key problem is that you're calling runUntilDate. There's no need to do that and the first runUntilDate is preventing the creation of the second timer from taking place as viewDidLoad will not proceed beyond the first runUntilDate call.
So remove both calls to runUntilDate and both timers will work fine. Use different properties to save the reference to the two timers if you want to keep a reference to both so that you can individually invalidate them if and when you no longer need them. (And it's important that you invalidate them when you no longer need them because they maintain a strong reference to self, which means that you have a "strong reference cycle".)
You could simplify the code sample:
NSDate *date = [NSDate date];
self.timer1 = [[NSTimer alloc] initWithFireDate:date
interval:500
target:self
selector:#selector(method1:)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer1 forMode:NSRunLoopCommonModes];
self.timer2 = [[NSTimer alloc] initWithFireDate:date
interval:60
target:self
selector:#selector(method2:)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer2 forMode:NSRunLoopCommonModes];
Or, unless you really need NSRunLoopCommonModes, you could just do:
self.timer1 = [NSTimer scheduledTimerWithTimeInterval:500
target:self
selector:#selector(method1:)
userInfo:nil
repeats:YES];
self.timer2 = [NSTimer scheduledTimerWithTimeInterval:60
target:self
selector:#selector(method2:)
userInfo:nil
repeats:YES];
And note, I'm using methods with a parameter (hence the colon with the selector):
- (void)method1:(NSTimer *)timer
{
// do whatever
}
- (void)method2:(NSTimer *)timer
{
// do whatever
}
You have to declare a second property of NSTimer. Currently you are overwriting your first timer when doing this
self.timer = [[NSTimer alloc] initWithFireDate:self.now
interval:60
target:self
selector:#selector(Func2)
userInfo:nil
repeats:YES] ;
Declare timer2 in your interface analogues to your timer declaration and the use self.timer2 to store the second timer.
Actually the runUntilDate will cause the app to keep on running the timer and the statements after this will not be executed. Try this:
self.now = [NSDate date] ;
self.timer = [[NSTimer alloc] initWithFireDate:self.now
interval:500
target:self
selector:#selector(Func1)
userInfo:nil
repeats:YES] ;
self.runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:self.timer forMode:NSDefaultRunLoopMode];
//Second timer start here.its not working.The function 'func2' is not getting called
self.now = [NSDate date] ;
self.timer = [[NSTimer alloc] initWithFireDate:self.now
interval:60
target:self
selector:#selector(Func2)
userInfo:nil
repeats:YES] ;
self.runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:self.timer forMode:NSDefaultRunLoopMode];
Even if you use the same timer variable for both timers, it will work. But as pointed out in other answers, you lose the ability to invalidate the first timer. Just comment out the runUntilDate statements in both places.
Declare a second property for your timers is not the solution. You just have to invalidate the old timer and allocate the new one on main thread and it will works! try this:
dispatch_async(dispatch_get_main_queue(), ^{
_timer = [NSTimer scheduledTimerWithTimeInterval:interval
target:self
selector:#selector(function:)
userInfo:nil
repeats:YES];
[_timer fire];
});

iOS can we reuse a NSTimer

I want to use an NSTimer (or if you have a better suggestion) to play a sound when the device is unplugged, or unknown. However if the user plugs the device back in, the sound should immediately stop.
Here's my code but it doesn't seem to behave as I am describing it,
- (void)currentBatteryState
{
UIDevice *device = [UIDevice currentDevice];
switch(device.batteryState) {
case UIDeviceBatteryStateUnknown:
currentBatteryStatusLabel.text = #"Unknown";
if ([batteryControlTimer isValid]) {
[batteryControlTimer invalidate];
batteryControlTimer = [NSTimer scheduledTimerWithTimeInterval:2.5 target:self selector:#selector(playSound) userInfo:nil repeats:YES];
} else {
batteryControlTimer = [NSTimer scheduledTimerWithTimeInterval:2.5 target:self selector:#selector(playSound) userInfo:nil repeats:YES];
}
break;
case UIDeviceBatteryStateUnplugged:
currentBatteryStatusLabel.text = #"Unplugged";
if ([batteryControlTimer isValid]) {
[batteryControlTimer invalidate];
batteryControlTimer = [NSTimer scheduledTimerWithTimeInterval:2.5 target:self selector:#selector(playSound) userInfo:nil repeats:YES];
} else {
batteryControlTimer = [NSTimer scheduledTimerWithTimeInterval:2.5 target:self selector:#selector(playSound) userInfo:nil repeats:YES];
}
break;
case UIDeviceBatteryStateCharging:
currentBatteryStatusLabel.text = #"Charging";
[batteryControlTimer invalidate];
break;
case UIDeviceBatteryStateFull:
currentBatteryStatusLabel.text = #"Full";
[batteryControlTimer invalidate];
break;
}
}
- (void) playSound
{
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef soundFileURLRef;
soundFileURLRef = CFBundleCopyResourceURL(mainBundle, (CFStringRef) #"siren_1", CFSTR ("mp3"), NULL);
UInt32 soundID;
AudioServicesCreateSystemSoundID(soundFileURLRef, &soundID);
AudioServicesPlaySystemSound(soundID);
}
Thank you
If the condition isn't met, don't play any sound, but don't invalidate the timer. That way it will keep firing at the interval even if the condition isn't met once.
So:
- (void)playSound {
if(conditionIsMet) {
//code to play your sound
}
}
Edit:
If you want to make the interval at which the sound can stop because the condition isn't met, then you just need to make the timer's time interval and the duration of the sound smaller.

Resources