Invalidate Timers when user press button home, doesn't work - objective-c

I have this code in app delegate that invokes an existing method in my view that invalidate timers and pause the music of my application:
Appdelegate.m
- (void)applicationWillResignActive:(UIApplication *)application{
GameViewController *gamer = [[GameViewController alloc] init];
[gamer EnterBackground];
}
GameViewController.m
-(void)EnterBackground{
NSLog(#"App Enter in background mode!");
[t1 invalidate];
[t2 invalidate];
[t3 invalidate];
[_audioPlayer stop];
}
I can not understand why the simulator when I press the lock button or home, the NSLog works, but timers and the audio still continue to run, why this is happening? there is a possible solution?

Related

Closing OSX window on load

I have come up with a taskbar program that is able to shut and close a window. Key code being:
-(void) openWindow{
NSLog(#"Opening Window");
//put infront of all other apps
[NSApp activateIgnoringOtherApps:YES];
//show window
[[_myView window] orderFront:self];
}
-(void) closeWindow{
NSLog(#"Closing Window");
//hide window
[[_myView window] orderOut:self];
}
This is working perfectly.
The only issue is, I now want the program to start off with the window closed but when I set this:
- (void)viewDidLoad {
[super viewDidLoad];
[self closeWindow];
}
Nothing happens and the window stays open?? And before you ask - Yes 'Visible At Launch' is switched off! Haha
If I add a 0.001 second delay in the view did load it works!!
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.001 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self closeWindow];
});
but then there is a horrible flicker...
If there is a viewWillAppear method, try to put your [self closeWindow] in.
Or maybe you try to close the window before she is visible so the code to show window is executed after your call to closeWindow.
Put some breakpoints if needed to see the order of the methods's execution.

How to resume a movie that's being played with presentMoviePlayerViewControllerAnimated

I'm uisng this code to display a movie:
MPMoviePlayerViewController *mp = [[MPMoviePlayerViewController alloc]
initWithContentURL:movieURL];
mp.moviePlayer.movieSourceType = MPMovieSourceTypeUnknown;
[self presentMoviePlayerViewControllerAnimated:mp]; [mp.moviePlayer play];
The code is working fine. However when the application goes to the background while playing a movie, when the app comes back in the foreground the movieplayer is not displayed. (I see the view of the controller that called presentMoviePlayerViewControllerAnimated:mp
Is it possible when entering the foregound to resume the movie that was playing before the app went to the background?
Have you set the UIBackgroundmode to audio and also there has been problem with playing the video after app enters foreground .Refer this Tutorial on MPMoviePlayerViewController Also you can try using MPMoviePlayerViewController which has options for implementing various notifications .
you can implement notification techniques to handle it. Add a notification in the class where movie player is playing and associate with it a selector. When app goes to background then in the delegate method
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
UIApplication *app = [UIApplication sharedApplication];
UIBackgroundTaskIdentifier bgTask = 0;
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
}];
}
write this code.Actually when app goes background it pauses the MPMoviePlayerController so when it is coming to foreground you post the notification which call the method in class where movie controller is implemented and play it again in this method.
-(void)playIntroAnimationAgain
{
[[NSNotificationCenter defaultCenter]removeObserver:self name:NOTIFICATION_PlayAgain_Player object:nil];
[self.moviePlayerController play];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playIntroAnimationAgain)name:NOTIFICATION_PlayAgain_Player object:nil];
}
It solved my problem.

Resuming execution of code after interruption while app is in background

I have spent days researching on SO and other websites for the answer to this but without any luck.
Essentially, the challenge I've set for myself is to create an alarm clock app for iOS that will sound no matter where the user may be (foreground or background). This I have already accomplished by using an AVAudioPlayer instance and starting to play an empty sound file when the user sets the alarm in order for the app to keep running in the background. When it is time for the alarm to go off (ie when the NSTimer is fired) a second player, which has already been initiated and prepared to play, starts playing the ringtone to which the user wakes up.
Also, I have managed to handle interruptions by a phone call, system timer or alarm clock by implementing the AVAudioSessionDelegate methods beginInterruption and endInterruptionWithFlags. It works both in background and foreground modes, but the strangest thing happens:
When the interruption ends, the AVAudioPlayer resumes playing BUT I cannot execute any other code in my app unless I bring the app to the foreground again.
To get to the bottom of this, I have experimented with a much simpler project which I am posting below.
What this app does is, as soon as you enter the app, an instance of the AVAudioPlayer class starts looping a certain sound. Then when you bring it to the background, the player continues to loop the sound. When an interruption occurs I pause the player and when it ends I use a dispatch to wait a couple of seconds before it calls two methods, ie (void)playPlayer, a method that contains the code to resume playing the file and (void)tester, a method that contains a timer, which is set to stop the player 5 seconds after the interruption (or 7 seconds to be exact) has ended. Both the methods get called as indicated by the NSLogs I have put in both of them, but the timer never gets fired and the player continues to play indefinitely.
Here is the code for the .h file:
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>
#interface InterruptionTest3ViewController : UIViewController <AVAudioSessionDelegate, AVAudioPlayerDelegate>
{
AVAudioSession *mySession;
AVAudioPlayer *myPlayer;
}
-(void) playPlayer;
-(void) pausePlayer;
-(void) tester;
#end
Here is the code for the .m file:
#import "InterruptionTest3ViewController.h"
#interface InterruptionTest3ViewController ()
#end
#implementation InterruptionTest3ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
mySession = [AVAudioSession sharedInstance];
NSError *setActiveError = nil;
[mySession setActive:YES withFlags:AVAudioSessionSetActiveFlags_NotifyOthersOnDeactivation error:&setActiveError];
if (setActiveError) {
NSLog(#"Session failed to activate within viewDidLoad");
}
else {
NSLog(#"Session was activated within viewDidLoad");
}
NSError *setCategoryError = nil;
[mySession setCategory:AVAudioSessionCategoryPlayback error:&setCategoryError];
if (setCategoryError) {
NSLog(#"Category failed to be set");
}
else {
NSLog(#"Category has been set");
}
[mySession setDelegate:self];
NSString *path = [[NSBundle mainBundle] pathForResource:#"headspin" ofType:#"wav"];
NSError *initMyPlayerError = nil;
myPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&initMyPlayerError];
if (initMyPlayerError) {
NSLog(#"myPlayer failed to initiate");
}
else {
NSLog(#"myPlayer has been initiated");
}
[myPlayer prepareToPlay];
[self playPlayer];
OSStatus propertySetError = 0;
UInt32 allowMixing = true;
propertySetError = AudioSessionSetProperty (
kAudioSessionProperty_OverrideCategoryMixWithOthers,
sizeof (allowMixing),
&allowMixing
);
[myPlayer setNumberOfLoops:-1];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
}
-(void) beginInterruption
{
[myPlayer pause];
}
-(void) endInterruptionWithFlags:(NSUInteger)flags
{
if (flags) {
if (AVAudioSessionInterruptionFlags_ShouldResume)
{
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC),dispatch_get_main_queue(), ^{
[self playPlayer];
[self tester];
});
}
}
}
}
-(void) tester
{
[NSTimer timerWithTimeInterval:5.0 target:self selector:#selector(pausePlayer) userInfo:nil repeats:NO];
NSLog(#"tester method has been called");
}
-(void) playPlayer
{
[NSTimer timerWithTimeInterval:5.0 target:myPlayer selector:#selector(stop) userInfo:nil repeats:NO];
[myPlayer play];
NSLog(#"playPlayer method has been called");
}
-(void) pausePlayer
{
[myPlayer pause];
}
//viewDidUnload etc not listed.
So, this is it folks. Again, why is the timer not being fired after an interruption while the app is in the background? Do I need to set something in the applicationDidEnterBackground method?
Thank you very much in advance!
please use "[NSTimer scheduledTimerWithTimeInterval:5.0 target:myPlayer selector:#selector(stop) userInfo:nil repeats:NO]";
Not to avoid the question, but the play-an-empty-sound-in-the-background bit is a hack. The ability to play a sound in the background is provided so that you can play music or other sound for the user's benefit, not to keep a background process alive.
Consider using local notifications instead if you want to play a sound or otherwise alert the user at a particular time.
why is the timer not being fired after an interruption while the app is in the background?
If I remember correctly, timers are either suspended or cancelled when your app enters the background. The documentation explicitly says that you should "stop timers and other periodic tasks" when your app is interrupted, i.e. when it's sent to the background.

Pause NSTimer to go about screen

i have a slideshow that works with NSTimer function calls every 3 seconds and i have a About button when user touch about that goes to another screen:
-(IBAction)toAbout:(id)sender
{
about *ab=[[about alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:ab animated:YES];
[ab release];
}
and returns from about screen with this function
-(IBAction)aboutButton:(id)sender
{
[self.parentViewController dismissModalViewControllerAnimated:YES];
}
why I can pause the NSTimer when I'm go to about screen?
and why I can creat the object that works in slideshow screen and works in about screen too?
There is no method for pausing your timer and it doesn't make sense to call [timer setFireData:[NSDate distantFuture]]. Instead, invalidate the timer and create a new one once entering the first view.
Invalidate by saying [timer invalidate]; and set the timer to nil timer = nil;.
To pause the timer you may simply call
[timer setFireDate:[NSDate distantFuture]];
This does not invalidate the timer but simply avoid that the timer keeps firing.

Cocoa AppKit - Dismissing a modal window (i.e. popup or contextual menu) and pressing the button currently hovered above

Basically I want to create the effect of that provided in the system's menu bar. A user presses on one of the menu headings, and as he moves across the different headings, the menus open up automatically.
The snag is that if I open a pop-up menu for a button, the user has to click again to dismiss it. The entire runloop is on hold as I believe the pop-up menu is modal. How do I go about being able to send a [somePopUpMenu cancelTracking] when the user moves to the next button?
Here's code I'm currently trying out in my NSWindow subclass. The point is that once the mouse exits a button, the mouse is automatically clicked (leftdown/leftup) on the next button, the timer is invalidated and the popup menu is cancelled.
Assuming a popup menu is open and the mouse exists, the leftdown/leftup events are fired (I know this works as I log them in NSLog's for mouseDown and mouseUp), the timer is invalidated, but the pop up menu is still showing, and the other button "clicked" on by the fake events doesn't show anything. Also, the whole thing gets into a loop and there's frantic mouseDown/mouseUp being sent for some reason.
Worth noting the the popup menu is created in another object, though the hookToMenu has a proper reference to it (I confirmed via debug/stepping through).
Dunno if I'm borking the event tracking timer or doing it the wrong way. I did try it via a window controller as well but got the same results.
Any help on this would be appreciated.
-(void)stopTimer
{
[timer invalidate];
[hookToMenu cancelTracking]; //hookToMenu is NSMenu*
}
-startFireDateTimer
{
NSDate *fireDate = [NSDate dateWithTimeIntervalSinceNow:0];
timer = [[NSTimer alloc] initWithFireDate:nil interval:0 target:self selector:#selector(targetMethod) userInfo:nil repeats:YES];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer forMode:NSEventTrackingRunLoopMode];
[timer release];
}
-(void)targetMethod
{
NSEvent *newEvent;
newEvent=[NSApp nextEventMatchingMask:NSMouseExitedMask untilDate:[NSDate distantFuture] inMode:NSEventTrackingRunLoopMode dequeue:NO];
if ([newEvent type]==NSMouseExited)
{
NSPoint mouseLoc;
mouseLoc=[newEvent locationInWindow];
int type=NSLeftMouseDown;
int type2=NSLeftMouseUp;
int windowNumber=[self windowNumber];
id fakeMouseUpEvent=[NSEvent mouseEventWithType:type
location:mouseLoc
modifierFlags:nil
timestamp:[NSDate timeIntervalSinceReferenceDate]
windowNumber:windowNumber
context:nil
eventNumber:nil
clickCount:1
pressure:1];
id fakeMouseUpEvent2=[NSEvent mouseEventWithType:type2
location:mouseLoc
modifierFlags:nil
timestamp:[NSDate timeIntervalSinceReferenceDate]
windowNumber:windowNumber
context:nil
eventNumber:nil
clickCount:1
pressure:1];
[NSApp sendEvent:fakeMouseUpEvent];
[NSApp sendEvent:fakeMouseUpEvent2];
[self stopTimer];
}
}
-(void)mouseDown:(NSEvent *)theEvent
{
[self startFireDateTimer];
NSLog(#"WINDOW: mouse down in window!");
[super mouseDown:theEvent];
}
-(void)mouseUp:(NSEvent *)theEvent
{
NSLog(#"WINDOW: mouse up in window!");
[super mouseUp:theEvent];
}
How do I go about being able to send a [somePopUpMenu cancelTracking] when the user moves to the next button?
Do exactly that. NSMenu responds to a cancelTracking message since Mac OS X 10.5.