The correct way to dismiss a MFMailComposeViewController - ios

I'm at my wit's end. I need to dismiss an MFMailComposeViewController when my app transitions to the background and I can't do it. It ends up creating an awkward application state.
Is there a way to handle this programmatically? Perhaps force the view controller to put the email into the Drafts folder and dismiss without animating?
EDIT:
Calls to - dismissModalViewControllerAnimated: don't work as expected.
The awkward application state I'm talking about is my main view being redrawn over top of the email composer when the application returns from the background. The modal is never dismissed and that email composer is never accessible again.
EDIT:
Code in my initializer:
// For compatibility with iOS versions below 4.0
if (&UIApplicationDidEnterBackgroundNotification != NULL)
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationDidEnterBackgroundNotification:) name:UIApplicationDidEnterBackgroundNotification object:nil];
}
Code in my background-entry handler:
- (void) applicationDidEnterBackgroundNotification:(NSNotification *)note {
// Do some other stuff here
// According to the docs, calling the method like this closes all
// child views presented modally
[self dismissModalViewControllerAnimated:NO];
}

I have reproduced a simple application with the code you have above. The mail composer is dismissed properly when the application enters the background.
I can only assume therefore that the //Do some other stuff here section of your code is doing too much stuff and the OS is shutting you down before you have chance to dismiss the composer.
According to the docs:
You should perform any tasks relating to adjusting your user interface before this method exits but other tasks (such as saving state) should be moved to a concurrent dispatch queue or secondary thread as needed. Because it's likely any background tasks you start in applicationDidEnterBackground: will not run until after that method exits, you should request additional background execution time before starting those tasks. In other words, first call beginBackgroundTaskWithExpirationHandler: and then run the task on a dispatch queue or secondary thread.
Perhaps you should move your other stuff to a different thread or request extra time? If you remove the other stuff, does the composer dismiss correctly?

Related

Using NSNotificationCenter to call method in main app from extensions widget?

I have a widget that I would like to call back to my main app so as to make a call to the server to update data. I looked into delegation, but registering the widget's view controller as a delegate didn't seem very practical. So I moved on to trying to use NSNotificationCenter. I have set it up, but the selector is not being called. In my main iOS app I have this in the viewDidLoad method.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(loadNewData:)
name:kUpdateData
object:nil];
And at the bottom of that file I have this:
/**
* Updates the table when the today widget is called for updated info
**/
- (void)loadNewData:(NSNotification *) notification
{
[self loadTableData];
}
That's in my main app. Now, in my notification center widget/extension, I make this call:
[[NSNotificationCenter defaultCenter]
postNotificationName:kUpdateData
object:nil];
The postNotificationName being passed in, `kUpdateData', is a constant that is resolved to #"updateData". I can see in the debugger that the postNotificationName method is being called, but the main app is not responding to it (regardless of it is in the foreground or the background). What am I doing wrong?
As a side note, the only reason I am doing this is to remove the need for repetitive code and re-implementing things I have already made.
As far as i know extension cant access or call main app methods... what can do is either do the server execution in extension or set a value in shared NSUserDefault so when your app is brought to foreground you can check this value and connect with server accordinly.

Update UIView after Locking and Unlocking phone

My app is running an NSURLSession where it downloads a file. However, when the user locks the phone I cannot update the subviews on the screen. They are frozen. I have a method getting called when the phone locks via the app delegate; however, it will not update any subviews.
I have tried placing the updates on main thread or background and nothing works. I simply want to adjust visibility of certain objects but not longer can do that after screen is locked. I hope that is enough info. Any help would be awesome. Thanks guys!
This is what is getting called via the app delegate when the phone is locked
-(void)pauseDownload{
// I want to update the UI!!!!
[session invalidateAndCancel];
bytesSum = 0;
percent = 0;
[HUD hide:YES];
self.downloadButton.hidden = NO;
HUD.progress = percent;
[HUD setLabelText:[NSString stringWithFormat:#"Loading %.0f%%",(percent*100)]];
}
None of the visibility permissions will work and the objects are un responsive. My guess is that I am losing a pointer to the objects some how when the phone is locked. Can I regain them? Am I way off idk?!
Use this in loadView or viewDidLoad:
[[NSNotificationCenter defaultCenter]addObserver:self
selector:#selector(becomeActive)
name:UIApplicationDidBecomeActiveNotification
object:nil];
-(void) becomeActive
{
NSLog(#"ACTIVE");
//
}
You can update UI in viewWillAppear: life cycle view controller method:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// update UI
}
Also, read apple's doc
Once device is lock there is no certain methodology or no way to create the background process or continue download process.
The only possible way can be used is we while during the downloading process if user lock the device & app goes into background downloaded content should be saved. So once when application is again active the downloading process should start from where it stopped.

NSNotification not working from another class

Part of an app I'm working on involves putting a blank screen over the current content if the user becomes inactive. As such after x seconds a blank page view controller is opened:
(From within ViewController.m and triggered by detecting an NSNotification from ScreenBlank.m)
UIPageViewController *blankPage = [self.storyboard instantiateViewControllerWithIdentifier:#"BlankPageViewController"];
[self presentViewController:blankPage animated:YES completion:nil];
This blank screen is then removed when a user touches the screen (And thus confirms their activity), like so:
(From inside the touchesBegan callback of ViewController.m)
[self dismissViewControllerAnimated:YES completion:nil];
The problem I'm having, is that I now want to trigger removing this screen blanking elsewhere, such as when the user logs out. This is done by removing a card from a plugged in card-reader, and means that it is called from a separate class (And one that is instantiated from within ActionMgr.m).
The class in question (CardWatcher) is created like so:
CardWatcher *newInstance = [[CardWatcher alloc] init];
[newInstance StartCardChecker];
And that instance of CardWatcher issues a notification when the card is removed, like so:
[[NSNotificationCenter defaultCenter] postNotificationName:#"logout" object:nil];
This notification is then listened for in ViewController, and on being triggered, executes EXACTLY the same code as is used to blank the screenearlier:
[self dismissViewControllerAnimated:YES completion:nil];
By using logging, I have determined that the notification is being received fine, and is executing the dismissViewControllerAnimated code, but for some reason, said code is not actually dismissing the view controller.
The only reason I can think of, is that it is (In the long run) called form within an instance of a class, but even then it's being passed via an NSNotification, and so in my eyes its source should be irrelevant?
Any help would be much appreciated!
It sounds like the notification is not generated on the main thread and you are hence trying to dismiss the page from a secondary thread. Make sure that you are posting the notification on the main thread and not from secondary threads. When a notification triggers UI events, such as this one, the code that manipulates the UI is required to be executed on the main thread.
Delivering Notifications To Particular Threads
Regular notification centers deliver notifications on the thread in
which the notification was posted. Distributed notification centers
deliver notifications on the main thread. At times, you may require
notifications to be delivered on a particular thread that is
determined by you instead of the notification center. For example, if
an object running in a background thread is listening for
notifications from the user interface, such as a window closing, you
would like to receive the notifications in the background thread
instead of the main thread. In these cases, you must capture the
notifications as they are delivered on the default thread and redirect
them to the appropriate thread.
Just try the following code and let me know if it worked for you.
dispatch_async(dispatch_get_main_queue(),^{
[[NSNotificationCenter defaultCenter] postNotificationName:#"logout" object:nil];
});

Asynchronous (non-blocking) display of alert in iPhone App

In my iPad App, am connecting to a web service. Whilst connecting to it, am displaying the progress activity indicator and a corresponding message to the user in a label (the label is in a subview and am adding the subview to the current view).
After this line of code (which calls a method to add the subview to the view), am invoking the method to call the web service. However, the web service call is getting executed first, and then only the user-information subview is displayed.
Is there any way to say that I want to 'continue displaying' the alert view even while the execution continues to the next line of code?
// Calling method to add info/alert subview to current view [self displayUserMessage];
// Connect to Web Service [self connectToWebService];
I'm not sure if I totally understand your question. Also it's far more easy to understand if you provide some code after your explanation... Anyway what I understand is that you are connecting to a web service and showing some info while the connection is on going?
Remember that if you don't want to hang your user interface you need to send the webService Connection in another thread, so you can keep the main thread free. You can do so using GCD.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self connectWithWebService];
});
Then depending on the architecture of the web service, you can use a delegate o maybe a completion block to show some messages (info/alert) to the user. In that case remember that anything related to UI should run on the main thread. So as I said before depending on your architecture you should do something like this
dispatch_async(dispatch_get_main_queue(), ^{
// Show UI Changes
});
The UI should update properly while the webService method is running on background.
If you want asynchronous connections its easier to go with NSURLConnection's sendAsynchronousRequest:queue:completionHandler:..
you can display your alert before calling it and dismiss it in the completion handler.

How to perform last actions upon user exiting an iPhone app?

Is there a way to perform some last actions when the user kills the application on iPhone?
In UIApplicationDelegate there is applicationWillTerminate: but as I understand it's not guaranteed to get called when the application terminates. Is there another way?
You can't rely on applicationWillTerminate being called. From the documentation:
For apps that do not support background execution or are linked against iOS 3.x or earlier, this method is always called when the user quits the app. For apps that support background execution, this method is generally not called when the user quits the app because the app simply moves to the background in that case. However, this method may be called in situations where the app is running in the background (not suspended) and the system needs to terminate it for some reason.
The proper place to save any state is when the app enters the background. Once that happens, there is no way to know if the app will return to the foreground or if it gets killed and then started from the beginning.
All methods concerning your app state are in your AppDelegate when you use one of the project templates.
Put the code in the applicationWillResignActive: method. It will get called if your app goes to an inactive state (terminating or no).
- (void)applicationWillResignActive:(UIApplication *)application
{
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
The "correct" place to save state is in both -applicationDidEnterBackground: and -applicationWillTerminate:. Don't worry about double-saving; generally only one of them is called (IME, -applicationWillTerminate: is not called when your app is killed in the background).
Caveat: -applicationDidEnterBackground: is not guaranteed to be called, since it is called after your app enters the background (and thus becomes eligible for killing without notice!). If the device is low on memory when your app is backgrounded, it might be killed. The best way to avoid this is to not use too much memory in the first place.
You could use
-applicationWillResignActive:, but I do not recommend this: apps become inactive quite frequently. An obvious is system dialogs (location/privacy prompts, Wi-Fi, notifications that show as alerts, alarms), TWTweetSheet, and I suspect MFMailComposeViewController, MFMessageComposeViewController, Notification Center, the app-switcher bar (e.g. to change tracks/enable orientation lock).
you can use applicationWillResignActive method in the appdelegate, or you can do the following, if you want to save stuff, but for some reason, you dont want to do it in the app delegate:
- (void) viewDidLoad/init {
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(myApplicationWillResign)
name:UIApplicationWillResignActiveNotification
object:NULL];
}
- (void) myApplicationWillResign {
NSLog(#"About to die, perform last actions");
}

Resources