App crashes when hitting rewind button multiples times - iphone

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

Related

invalidate and fire Timer from different view controller is not working

myTimer is defined in OneViewController and i m trying to invalidate and fire it again from mainviewcontroller but it is not working. What exactly i m missing here.
here is my code
OneViewController.h
#property (nonatomic, strong) NSTimer *myTimer;
OneViewController.m
#synthesize myTimer;
- (void)viewDidLoad
{
[super viewDidLoad];
[self myTimerMethod];}
- (void)myTimerMethod{
NSLog(#"myTimerMethod is Called");
self.myTimer = [NSTimer scheduledTimerWithTimeInterval:2.4
target:self
selector:#selector(updateView:)
userInfo:nil
repeats:YES];
}
- (void)updateView:(NSTimer *)theTimer
{
if (index < [textArray count])
{
self.textView.text = [self.textArray objectAtIndex:index];
self.imageView.image = [self.imagesArray objectAtIndex:index];
index++;
}else{
index = 0;
}
}
MainViewController.h
#class OneViewController;
#property (strong, nonatomic) OneViewController *oneviewcontroller;
MainViewController.m
#synthesize oneviewcontroller = _oneviewcontroller;
-(void)playpauseAction:(id)sender {
if([audioPlayer isPlaying])
{
[sender setImage:[UIImage imageNamed:#"music.png"] forState:UIControlStateNormal];
[audioPlayer pause];
[self.oneviewcontroller.myTimer invalidate];
}else{
[sender setImage:[UIImage imageNamed:#"audiostop.png"] forState:UIControlStateNormal];
[audioPlayer play];
[self.oneviewcontroller.myTimer fire];
if(isFirstTime == YES)
{
self.timer = [NSTimer scheduledTimerWithTimeInterval:06.0
target:self
selector:#selector(displayviewsAction:)
userInfo:nil
repeats:NO];
isFirstTime = NO; } } }
- (void)displayviewsAction:(id)sender{
OneViewController *oneviewcontroller = [[OneViewController alloc] init];
oneviewcontroller.view.frame = CGRectMake(0, 0, 320, 430);
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:oneviewcontroller.view];
}
Appreciate help.
Thanks.
You create OneViewController as a local variable of the method displayviewsAction. Not as the property you defined previously.
That's why it's not working, the variable gets released once you exit the method.
Change this line:
OneViewController *oneviewcontroller = [[OneViewController alloc] init];
With this one:
self.oneviewcontroller = [[OneViewController alloc] init];

Run Timer for first time and resume timer for second time

Declared variable in m file.
BOOL isFirstTime;
I want that if play button is pressed the very first time then it should toggle to pause button and check the function isFirstTime then it should start the timer and execute the displayviewsAction method and if the pause button is resumed to play back then it should check again the function isFirstTime or second time and if the hit is for the second time then it should resume timer.
-(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];
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(AfterOneSecondsFuction) userInfo:nil repeats:YES];
}
}
-(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)AfterOneSecondsFuction
{
if(!isFirstTime)
{
isFirstTime=YES;
return;
self.timer = [NSTimer scheduledTimerWithTimeInterval:11.0
target:self
selector:#selector(displayviewsAction:)
userInfo:nil
repeats:NO];
} else if (!isFirstTime){
isFirstTime=NO;
return;
[self resumeTimer];
}
}
- (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];
}
-(void)Second
{
Second *secondController = [[Second alloc] init];
secondController.view.frame = CGRectMake(0, 0, 320, 480);
CATransition *transitionAnimation = [CATransition animation];
[transitionAnimation setDuration:1];
[transitionAnimation setType:kCATransitionReveal];
[transitionAnimation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
[self.view.layer addAnimation:transitionAnimation forKey:kCATransitionReveal];
[self.view addSubview:secondController.view];
[self.view addSubview:toolbar];
[secondController release];
self.timer = [NSTimer scheduledTimerWithTimeInterval:27 target:self selector:#selector(Third) userInfo:nil repeats:NO];
}
Looks like when play is pressed first it is only toggling to pause button not checking the function isFirstTime or not and not starting the timer and executing displayviewsaction.
If anyone can tell why it is not happening and what i m doing wrong.
Thanks for help.
BOOL isFirstTime
//Somewhere where the object is set up, isFirstTime should be set to YES
- (void)playpauseAction:(id)sender {
//When the button is pressed
if(isFirstTime) {
//Load the sound file
//Start playing the sound
//Start view controller timers
//Change the button image to paused
isFirstTime = FALSE;
}
else {
//The button has already been pressed
if ([audioPlayer isPlaying]) {
//If the sound is playing
//Stop playing the sound
//Stop view controller timers
//Change the button image to playing
}
else {
//Resume playing the sound
//Start view controller timers
//Change the button image to paused
}
}
}

[__NSDate timeIntervalSinceNow]: message sent to deallocated instance

App crashed with the message when hitting rewind twice just to test app on iPhone device [__NSDate timeIntervalSinceNow]: message sent to deallocated instance
-(void)pauseTimer{
pauseStart = [[NSDate dateWithTimeIntervalSinceNow:0] retain];
previousFireDate = [[timer fireDate] retain];
[timer setFireDate:[NSDate distantFuture]];
}
-(void)dealloc
{
[audioPlayer release];
[pauseStart release];
[previousFireDate release];
[super dealloc];
}
-(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)rewind:(id)sender{
[timer invalidate];
audioPlayer.currentTime = 0;
MainViewController *viewController = [[[MainViewController alloc] init]autorelease];
viewController.view.frame = CGRectMake(0, 0, 320, 480);
[self.view addSubview:viewController.view];
[self.view addSubview:toolbar];
[self playpauseAction:_playButton];
}
when hitting rewind button again and purposely to test app on iphone device app crashes and gives message [__NSDate timeIntervalSinceNow]: message sent to deallocated instance
Any ideas what is wrong.
Appreciate help.
Thanks.
Removed
[pauseStart release];
[previousFireDate release];
from the dealloc method.
That solved my problem.

Pause the Views from Updating

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.

How to resume updating views when resumed AVAudioplayer

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.