Wrong handling when resuming from MFMailComposeViewController - ios

My application is a quizz game. The user has a limited time to answer the question.
A timer is used for that. When the time runs out, a simple sound is triggered.
NSTimer *m_timer;
In function viewDidAppear:
m_timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(decrementSpin) userInfo:nil repeats:YES];
In my fisrt version, I encountered the following situation:
If during a question, an incoming call interrupts the game, the timer was still counting during the call.
I fixed this problem by adding in function viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appDidEnterInBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
- (void)appDidEnterInBackground:(NSNotification *)notification {
[[SoundManager sharedManager]stopMusic:NO];
[m_timer invalidate];
m_timer = nil;
}
- (void)appWillEnterForeground:(NSNotification *)notification {
if(m_timer) {
[m_timer invalidate];
m_timer = nil;
}
//NSLog(#"%d", self.TimerbackgroundView.percent);
m_timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(decrementSpin) userInfo:nil repeats:YES];
}
The function decrementSpin updates the clock image and plays the sound if the player has run out of time.
Everything works well.
Since my last version, I added a feature. The user can report a question (for incorrect content) by pressing a button.
When a button is pressed it opens the mail app with a prefilled content.
MFMailComposeViewController*mailComposerVC = [[MFMailComposeViewController alloc] init];
if ([MFMailComposeViewController canSendMail]) {
mailComposerVC.mailComposeDelegate = self;
[mailComposerVC setToRecipients:#emailAddress];
[mailComposerVC setSubject:emailSubject];
[mailComposerVC setMessageBody:emailBody isHTML:NO];
[self presentViewController:mailComposerVC animated:YES completion:^{
}];
}
else{
NSLog(#"Unable to send message");
}
- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error{
[controller dismissViewControllerAnimated:YES completion:^{
}];
}
It seems that my application doesn't handle correctly when the user sends the mail and returns to the Question page.
The timer doesn't pause (like the incoming call case), and in addition, when the Question page re-appears the clock-image shows the initial image (exactly like when the page appears for the first time)
This bug causes the sound (the one triggered when the user runs out of time) to be played during the next Page.
The only thing that comes in my mind that the events notified by UIApplicationDidEnterBackgroundNotification and UIApplicationWillEnterForegroundNotification are not covered in the case of MFMailComposeViewController.
Any idea ?

You are correct. The app is still running the in the foreground.

So a possible solution would be to execute the same code of appDidEnterInBackground when opening the Mail.app, and to do the same as appWillEnterForeground in the body of
- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error{
[controller dismissViewControllerAnimated:YES completion:^{
}];
}
What do you think ?

Related

NSTimer and closing app while in background

I've encountered problem with closing the app while in background.
When working in the background, tapping 2x and swiping the app to close it, the app doesn't call the applicationWillTerminate:(UIApplication *)application, but goes to #autoreleasespool, which is definitely crash.
I suppose it is connected with working NSTimer, because without initializing it the app closes properly from background.
I wanted to disable the NSTimer in the appDelegate with no success, since NSTimer can be disabled only from the same thread.
I am starting NSTtimer in init of my class:
[NSTimer scheduledTimerWithTimeInterval:5.0f target:self selector:#selector(checkLastSeenTimeofPeripheral) userInfo:nil repeats:YES];
I wanted to stop it while going to background using the answer given here, but still it doesn't seem to stop the timer and the app crashes on termination.
EDIT:
Initializing in myClass
- (id)init {
self = [super init];
if(self){
//check the connection timer
[self startCheckLastSeenTimeOfPeripheralTimer];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(stopCheckLastSeenTimeOfPeripheralTimer) name:UIApplicationDidEnterBackgroundNotification object:[UIApplication sharedApplication]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(startCheckLastSeenTimeOfPeripheralTimer) name:UIApplicationWillEnterForegroundNotification object:[UIApplication sharedApplication]];
methods to start/stop timer
-(void)startCheckLastSeenTimeOfPeripheralTimer {
_checkLastSeenTimeOfPeripheralTimer = [NSTimer scheduledTimerWithTimeInterval:5.0f
target:self
selector:#selector(checkLastSeenTimeofPeripheral)
userInfo:nil
repeats:YES];
NSLog(#"checkLastSeenTimeofPeripheralTimer started");
}
-(void)stopCheckLastSeenTimeOfPeripheralTimer {
if (_checkLastSeenTimeOfPeripheralTimer) {
[_checkLastSeenTimeOfPeripheralTimer invalidate];
_checkLastSeenTimeOfPeripheralTimer = nil;
NSLog(#"checkLastSeenTimeofPeripheralTimer stopped");
}
else {
NSLog(#"checkLastSeenTimeofPeripheralTimer not initialized - can't stop");
}
}
According to documentation appWillTerminate is not being called when closing suspended App: Suspended apps receive no notification when they are terminated; the system kills the process and reclaims the corresponding memory.
Apps get suspended by the system while in background without informing about it.

Handle NSNotification while scrolling a inner UITableView

I need some UIViewControllers that receive a NSNotification from the app delegate. It's like a timer, but every UIViewController handle your way. My problem is: when I interact with user interface, the UIViewController doesn't receive the notification, causing problems.
Here is my code in AppDelegate:
-(void)updateCounter:(NSTimer *)theTimer{
[[NSNotificationCenter defaultCenter] postNotificationName:TimeTickNotification object:nil];
}
//*called by some trigger in the app
-(void) startTimer{
timer = [NSTimer
scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(updateCounter:)
userInfo:nil
repeats:YES];
}
I am handling the notifications in each UIViewController like this:
-(void) updateGlobalTime:(NSNotification *) notification{
totalTime = [NSNumber numberWithFloat:([ficha.tempoTotal floatValue] + STEP)];
}
-(void) viewWillAppear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(updateGlobalTime:)
name:TimeTickNotification
object:nil];
}
What should I do to interact with UI and update it at same time? Maybe the NSNotification is not being thrown while user interacts with UI.
You need to make sure you're updating any UI on the main thread. If you want to update the UI to have the new totalTime in a label or something, make sure the setText: function is running on the main thread. You can accomplish that with GCD, like this:
-(void) updateGlobalTime:(NSNotification *) notification{
totalTime = [NSNumber numberWithFloat:([ficha.tempoTotal floatValue] + STEP)];
// Update label on main thread
dispatch_async(dispatch_get_main_queue(), ^{
[label setText:totalTime];
});
}
The solution was to use NSRunLoop, as following:
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
timer = [NSTimer
scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(updateCounter:)
userInfo:nil
repeats:YES];
[runloop addTimer:timer forMode:NSRunLoopCommonModes];
[runloop addTimer:timer forMode:UITrackingRunLoopMode];

Are performSelector afterDelay calls reset when applicationDidEnterBackground?

I totally understand if they are, but what I'm looking for is a timer that pauses when the application enters the background and unpauses after the user returns to the app. I do not need a background task; I just want to make sure that after approximately x minutes within the app a certain action occurs whether that is today or tomorrow.
Thanks!
Brett
Backgrounding the app (assuming you have no background task) doesn't "pause" the timer. It's still counting down in theory so if the app is reopened, it will fire if enough time has passed. This goes for NSTimer's as well. (Let me know if you want more details as to why and I'll edit the answer).
Consider using the following code:
#implementation MyCustomClass {
int elapsedTime;
NSTimer *timer;
}
- (id) init {
if ( ( self = [super init] ) ) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationEnteredBackground)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationEnteredForeground)
name:UIApplicationDidBecomeActiveNotification
object:nil];
}
return self;
}
- (void) applicationEnteredForeground {
timer = [NSTimer timerWithTimeInterval:1
target:self
selector:#selector(timerTicked)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
- (void) applicationEnteredBackground {
[timer invalidate];
}
- (void) timerTicked {
elapsedTime += 1;
// If enough time passed, do something
}

How to update UISlider from another singleton class?

I want to update an UISlider according to a notification generated by another singleton class.
sondDuration=audioPlayer.currentItem.asset.duration;
songDurationinSeconds=CMTimeGetSeconds(sondDuration);
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateTime:) userInfo:nil repeats:YES];
This is my notofication generation.
According to this how I can update my UISlider in another ViewController Please help me.
You can use Delegate Method For that
Like the given Below:
http://www.roostersoftstudios.com/2011/04/12/simple-delegate-tutorial-for-ios-development/
I think you have to add a NSNotification in your viewcontroller which updates the UISlider
In your viewcontroller
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveSliderUpdate:)
name:#"UpdateSlider"
object:nil];
- (void) receiveSliderUpdate:(NSNotification *) notification
{
// [notification name] should always be #"UpdateSlider"
// unless you use this method for observation of other notifications
// as well.
if ([[notification name] isEqualToString:#"UpdateSlider"])
// do something with your slider
}
In your controller add the code to notify your view controller
[[NSNotificationCenter defaultCenter]
postNotificationName:#"UpdateSlider"
object:self];

MPMoviePlayerViewController stops after a few seconds

I have an app in which I am streaming a live TV channel in one of tabs. I am using MPMoviePlayerViewController. I did declare my MPMoviePlayerViewController in my header file and synthesize it in my implementation file.
Here's my viewDidAppear:
- (void)viewDidAppear:(BOOL)animated
{
NSURL *movieURL = [[NSURL alloc]initWithString:#"http://mysuperURL"];
moviePlayerController = [[MPMoviePlayerViewController alloc] initWithContentURL:movieURL];
[self checkIt];
}
And my checkIt function
- (void) checkIt {
if ([[moviePlayerController moviePlayer] loadState] == MPMovieLoadStateUnknown) { // before you wreck yourself
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(checkIt) userInfo:nil repeats:NO];
} else {
[moviePlayerController setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self presentModalViewController:moviePlayerController animated:YES];
}
}
However the video freezes after two seconds and the app stops responding.
You should use the MPMoviePlayerNotifications instead of manually polling the current state.
For example - somewhere in you initializing code:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(MPMoviePlayerLoadStateDidChange:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:nil];
Now implement a notification handler:
- (void)MPMoviePlayerLoadStateDidChange:(NSNotification *)notification
{
NSLog(#"loadstate change: %Xh", movieController_.loadState);
}
And somewhere within your deinitializing code:
[[NSNotificationCenter defaultCenter] removeObserver:self
name:MPMoviePlayerLoadStateDidChangeNotification
object:nil];
Also note that MPMoviePlayerController.loadState is a bitmap -> you need to mask out the value you want to check for.
For Example:
if ((movieController_.loadState & MPMovieLoadStatePlayable) == MPMovieLoadStatePlayable)
{
NSLog(#"yay, it became playable");
}
Asfar as my knowledge concern usage of timer it is freezing and it takes time streaming also.

Resources