I m struggling with this issue for so long but not able to find any solution for this
The issue i have is when i pause the audioplayer i want views to be paused from updating so i used timer invalidate to pause the views from loading but when i resume play only Audioplayer is resumed not the views cause i invalidated the timer.
Now i how i can resume updating views from that point where view was paused. By starting the timer again i can start updating views again but how program will know that at what view it was paused and it should resume updating views from that point. There are multiple views involved displayed code for only two.
-(void)playpauseAction:(id)sender
{
if
([audioPlayer isPlaying]){
[sender setImage:[UIImage imageNamed:#"play.png"] forState:UIControlStateSelected];
[audioPlayer pause];
[timer invalidate];
} else {
[sender setImage:[UIImage imageNamed:#"pause.png"] forState:UIControlStateNormal];
[audioPlayer play];
self.timer = [NSTimer scheduledTimerWithTimeInterval:11 target:self selector:#selector(displayviewsAction:) userInfo:nil repeats:NO];
}
}
- (void)displayviewsAction:(id)sender
FirstViewController *viewController = [[FirstViewController alloc] init];
viewController.view.frame = CGRectMake(0, 0, 320, 480);
[self.view addSubview:viewController.view];
[self.view addSubview:toolbar];
[viewController release];
self.timer = [NSTimer scheduledTimerWithTimeInterval:23 target:self selector:#selector(secondViewController) userInfo:nil repeats:NO];
-(void)secondViewController {
SecondViewController *secondController = [[SecondViewController alloc] init];
secondController.view.frame = CGRectMake(0, 0, 320, 480);
[self.view addSubview:secondController.view];
[self.view addSubview:toolbar];
[secondController release];
self.timer = [NSTimer scheduledTimerWithTimeInterval:27 target:self selector:#selector(ThirdviewController) userInfo:nil repeats:NO];
}
Thanks and appreciate for your answers and helping me out with this issue.
Use a background thread to basically bind the view to the player, something like this:
UIImageView* imageView; //the view that gets updated
int current_selected_image_index = 0;
float time_for_next_image = 0.0;
AVAudioPlayer* audioPlayer;
NSArray* image_times; //an array of float values representing each
//time when the view should update
NSArray* image_names; //an array of the image file names
-(void)update_view
{
UIImage* next_image_to_play = [image_names objectAtIndex:current_selected_image_index];
imageView.image = next_image_to_play;
}
-(void)bind_view_to_audioplayer
{
while(audioPlayer.isPlaying)
{
float currentPlayingTime = (float)audioPlayer.currentTime;
if(currentPlayingTime >= time_for_next_image)
{
current_selected_image_index++;
[self performSelectorOnMainThread:#selector(update_view) withObject:nil waitUntilDone:NO];
time_for_next_image = [image_times objectAtIndex:[current_selected_image_index+1)];
}
[NSThread sleep:0.2];
}
}
-(void)init_audio
{
current_selected_image_index = 0;
time_for_next_image = [image_times objectAtIndex:1];
}
-(void)play_audio
{
[audioPlayer play];
[self performSelectorInBackground:#selector(bind_view_to_audioplayer) withObject:nil];
}
-(void)pause_audio
{
[audioPlayer pause];
//that's all, the background thread exits because audioPlayer.isPlaying == NO
//the value of current_selected_image_index stays where it is, so [self play_audio] picks up
//where it left off.
}
also, add self as observer for the audioPlayerDidFinishPlaying:successfully: notification to reset when the audioplayer finishes playing.
Related
I am using AVAudioplayer to play audio files, play/pause button to toggle between play and pause and NSTimer with audio player to synchronize view controllers one after another. I have a total of 10 or 11 view controllers to load one after the other.
When play button is pressed the very first time it starts NSTimer, starts playing an audio file and based on NSTimer, view controllers will load one after the other. When pause button is pressed again to resume it resumes everything from that point audio file, NSTimer and view controllers from the same point where it was paused which is good. However, at the same time it starts loading the view controllers again one after the other from the beginning based on NSTimer which is not good.
It is reloading view controllers with this statement
self.timer = [NSTimer scheduledTimerWithTimeInterval:11.0
target:self
selector:#selector(displayviewsAction:)
userInfo:nil
repeats:NO];
So my issue is that I need to stop the loading of view controllers again from the beginning when pause button is pressed again to resume playback.
-(void)playpauseAction:(id)sender
{
if ([audioPlayer isPlaying]) {
[sender setImage:[UIImage imageNamed:#"Play Icon.png"] forState:UIControlStateSelected];
[audioPlayer pause];
[self pauseTimer];
} else {
[sender setImage:[UIImage imageNamed:#"pause.png"] forState:UIControlStateNormal];
[audioPlayer play];
[self resumeTimer];
self.timer = [NSTimer scheduledTimerWithTimeInterval:11.0
target:self
selector:#selector(displayviewsAction:)
userInfo:nil
repeats:NO];
}
}
-(void)pauseTimer{
pauseStart = [[NSDate dateWithTimeIntervalSinceNow:0] retain];
previousFireDate = [[timer fireDate] retain];
[timer setFireDate:[NSDate distantFuture]];
}
-(void)resumeTimer{
float pauseTime = -1*[pauseStart timeIntervalSinceNow];
[timer setFireDate:[previousFireDate initWithTimeInterval:pauseTime sinceDate:previousFireDate]];
[pauseStart release];
[previousFireDate release];
}
- (void)displayviewsAction:(id)sender
{
First *firstController = [[First alloc] init];
firstController.view.frame = CGRectMake(0, 0, 320, 480);
CATransition *transitionAnimation = [CATransition animation];
[transitionAnimation setDuration:1];
[transitionAnimation setType:kCATransitionFade];
[transitionAnimation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
[self.view.layer addAnimation:transitionAnimation forKey:kCATransitionFade];
[self.view addSubview:firstController.view];
[self.view addSubview:toolbar];
[firstController release];
self.timer = [NSTimer scheduledTimerWithTimeInterval:23 target:self selector:#selector(Second) userInfo:nil repeats:NO];
}
Can anyone give an idea on how to stop the loading of view controllers again from the beginning when pause button is pressed again to resume playback.
I gather you're trying to display FirstViewController after 11 seconds have elapsed in the audio track.
You can set you timer up to poll (every .5 - 1.0 seconds) and if the audioPlayer's elapsed time is greater than 11.0, perform your action.
-(void)playpauseAction:(id)sender
{
if ([audioPlayer isPlaying]){
[sender setImage:[UIImage imageNamed:#"Play Icon.png"] forState:UIControlStateSelected];
[audioPlayer pause];
[self.timer invalidate]; // stop the polling timer
} else {
[sender setImage:[UIImage imageNamed:#"pause.png"] forState:UIControlStateNormal];
[audioPlayer play];
// start/resume the timer
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 /* polling interval */
target:self
selector:#selector(tryToDisplayViews:)
userInfo:nil
repeats:YES]; /* REPEAT = YES */
}
}
// the new polled method
-(void)tryToDisplayViews:(id)sender {
// need last current time as a new variable to ensure that we only add the view once
// and not every time the method fires with currentTime > 11.0
if( audioPlayer.currentTime > 11.0 && lastCurrentTime < 11.0) {
[self displayviewsAction:nil];
}
// you can add the second view here in a similar fashion
// if(audioPlayer.currentTime > (23.0+11.0) && lastCurrentTime < (23.0+11.0) ) {}
lastCurrentTime = audioPlayer.currentTime;
}
This also get's rid of your resumeTimer/pauseTimer actions, you don't need to count the time paused.
Application crashes and message is program received signal "EXC_BAD_ACCESS" when hitting purposely on rewind button multiple times just to test application on iPhone device.
-(void)rewind:(id)sender{
[timer invalidate];
audioPlayer.currentTime = 0;
MainViewController *viewController = [[MainViewController alloc] init];
viewController.view.frame = CGRectMake(0, 0, 320, 480);
[self.view addSubview:viewController.view];
[self.view addSubview:toolbar];
[viewController release];
[self playpauseAction:_playButton];
}
-(void)playpauseAction:(id)sender {
if([audioPlayer isPlaying])
{
[sender setImage:[UIImage imageNamed:#"Play Icon.png"] forState:UIControlStateSelected];
[audioPlayer pause];
[self pauseTimer];
[self pauseLayer:self.view.layer];
}else{
[sender setImage:[UIImage imageNamed:#"pause.png"] forState:UIControlStateNormal];
[audioPlayer play];
[self resumeTimer];
[self resumeLayer:self.view.layer];
if(isFirstTime == YES)
{
self.timer = [NSTimer scheduledTimerWithTimeInterval:11.0
target:self
selector:#selector(displayviewsAction:)
userInfo:nil
repeats:NO];
isFirstTime = NO;
}
}
}
-(void)pauseTimer{
pauseStart = [[NSDate dateWithTimeIntervalSinceNow:0] retain];
previousFireDate = [[timer fireDate] retain];
[timer setFireDate:[NSDate distantFuture]];
}
-(void)resumeTimer{
float pauseTime = -1*[pauseStart timeIntervalSinceNow];
[timer setFireDate:[previousFireDate initWithTimeInterval:pauseTime sinceDate:previousFireDate]];
}
-(void)resumeLayer:(CALayer*)layer
{
CFTimeInterval pausedTime = [layer timeOffset];
layer.speed = 1.0;
layer.timeOffset = 0.0;
layer.beginTime = 0.0;
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
layer.beginTime = timeSincePause;
}
-(void)pauseLayer:(CALayer*)layer
{
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.speed = 0.0;
layer.timeOffset = pausedTime;
}
i don't know why it is crashing may be because of the resume timer or may be because of the view controller i released.
Why do you alloc and init your MainViewController inside of your rewind function? that seems odd to me, normally you only need to initialize your viewcontroller once, not every time a method is called. And similarly, you release it at the end of the method, which is also strange. Generally errors that take multiple clicks to be caused are caused by memory management problems. I'm guessing that something is being removed from memory (possibly your view controller) and then you are trying to access it, giving you a bad access error.
To debug this, use NSZombies, which allows you to see what was removed from memory. A tutorial of how to use it can be found here
In playpauseAction Method execution it will play audio file as well as load multiple viewcontrollers
-(void)playpauseAction:(id)sender
{
if
([audioPlayer isPlaying]){
[sender setImage:[UIImage imageNamed:#"play.png"] forState:UIControlStateSelected];
[audioPlayer pause];
} else {
[sender setImage:[UIImage imageNamed:#"pause.png"] forState:UIControlStateNormal];
[audioPlayer play];
self.timer = [NSTimer scheduledTimerWithTimeInterval:11 target:self selector:#selector(displayviewsAction:) userInfo:nil repeats:NO];
}
}
While loading these view controllers just want to check in every viewcontroller that if audioplayer is paused then invalidate timer if not then continue loading of viewcontroller with NSTimer
- (void)displayviewsAction:(id)sender
{
FirstViewController *viewController = [[FirstViewController alloc] init];
viewController.view.frame = CGRectMake(0, 0, 320, 480);
[self.view addSubview:viewController.view];
[self.view addSubview:toolbar];
if
([audioPlayer pause]){
[timer invalidate];
} else {
self.timer = [NSTimer scheduledTimerWithTimeInterval:23 target:self selector:#selector(secondViewController) userInfo:nil repeats:NO];
}
[viewController release];
}
My problem is when i add if statement in displayviewAction method it gives me red warning message that statement requires expression of scalar type (void invalid).
But if statement in the playpauseAction works fine.
Why the if statement of displayviewAction is giving red warning message.
AVAudioPlayer's pause is defined as:
-(void)pause
so writing:
if([audioPlayer pause]) {...}
corresponds to write:
if(void) {...}
and void is not a scalar type, exactly what the error message tells you.
Probably your intention was to check if the audio player was paused, that is it is not playing :
if([audioPlayer isPlaying]==NO) {...}
Want to pause the multiple views from updating when Pause button is pressed
In h file
#property BOOL appIsPaused;
In m file
#synthesize appIsPaused;
-(void)playpauseAction:(id)sender
{
if
([audioPlayer isPlaying]){
[sender setImage:[UIImage imageNamed:#"play.png"] forState:UIControlStateSelected];
[audioPlayer pause];
appIsPaused = YES;
} else {
[sender setImage:[UIImage imageNamed:#"pause.png"] forState:UIControlStateNormal];
[audioPlayer play];
appIsPaused = NO;
[self performSelector:#selector(displayviewsAction:) withObject:nil afterDelay:11.0];
}
}
- (void)displayviewsAction:(id)sender
{
FirstViewController *viewController = [[FirstViewController alloc] init];
viewController.view.frame = CGRectMake(0, 0, 320, 480);
[self.view addSubview:viewController.view];
[self.view addSubview:toolbar];
[self performSelector:#selector(secondViewController) withObject:nil afterDelay:23];
[viewController release];
}
-(void)secondViewController {
SecondViewController *secondController = [[SecondViewController alloc] init];
secondController.view.frame = CGRectMake(0, 0, 320, 480);
[self.view addSubview:secondController.view];
[self.view addSubview:toolbar];
[self performSelector:#selector(ThirdviewController) withObject:nil afterDelay:27];
[secondController release];
}
and it goes on like this for multiple views.
Any ideas how to pause views from updating whenever pause button is pressed.
Instead of using performSelector after delay, you should consider using a NSTimer.
Like this:
Declare a NSTimer *timer ivar.
Declare a NSUInteger viewControl;
Set viewControl to 0;
On the play part of the method add this line:
timer = [NSTimer scheduledTimerWithTimeInterval:11 target:self selector:#selector(tick) userInfo:nil repeats:YES];
-(void)tick
{
switch(viewControl)
{
case 0:
[self performSelector:#selector(firstViewController) withObject:nil];
break;
case 1:
[self performSelector:#selector(secondViewController) withObject:nil];
break;
case 2:
[self performSelector:#selector(thirdViewController) withObject:nil];
break;
.
.
.
default:
break;
}
viewControl++;
if(viewControl > MAX_VIEWS)
{
viewControl = 0;
}
}
And add this line on pause action:
[timer invalidate]
It is also cleaner and let you have more control over your code.
Hope it helps.
Time is invalidated to pause the views from updating because pause button was hit to pause the audio file.
-(void)playpauseAction:(id)sender
{
if
([audioPlayer isPlaying]){
[sender setImage:[UIImage imageNamed:#"play.png"] forState:UIControlStateSelected];
[audioPlayer pause];
[timer invalidate];
} else {
[sender setImage:[UIImage imageNamed:#"pause.png"] forState:UIControlStateNormal];
[audioPlayer play];
self.timer = [NSTimer scheduledTimerWithTimeInterval:11 target:self selector:#selector(displayviewsAction:) userInfo:nil repeats:NO];
}
}
- (void)displayviewsAction:(id)sender
{
FirstViewController *viewController = [[FirstViewController alloc] init];
viewController.view.frame = CGRectMake(0, 0, 320, 480);
[self.view addSubview:viewController.view];
[self.view addSubview:toolbar];
self.timer = [NSTimer scheduledTimerWithTimeInterval:23 target:self selector:#selector(secondViewController) userInfo:nil repeats:NO];
[viewController release];
}
-(void)secondViewController {
SecondViewController *secondController = [[SecondViewController alloc] init];
secondController.view.frame = CGRectMake(0, 0, 320, 480);
[self.view addSubview:secondController.view];
[self.view addSubview:toolbar];
self.timer = [NSTimer scheduledTimerWithTimeInterval:27 target:self selector:#selector(ThirdviewController) userInfo:nil repeats:NO];
[secondController release];
}
It goes like this for multiple views. As per the application logic user can hit pause on any view to pause the audio file and timer invalidate statement will also pause views from updating. When user will hit pause again to resume audio file it should start updating views from that point onwards. So problem i m having is how to restart the timer again so that it keep on updating views onwards no matter at which view user pauses.
Anyideas how i can do that.
The timers are not supposed to be reused, simply create another one.
-(void)countDown{
counter--;
if (counter == 0){
[self.timer invalidate];
[self secondViewController];
}
}
Launch your timer:
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(countDown) userInfo:nil repeats:YES];
And relaunch your timer:
counter = 23;
[self.timer fire];
you should adjust this answer for your purposes, because as you can see, the timer is scheduled only for launching -(void)secondViewController method