I've seen many conflicting answers that seem to be situation dependent, but I haven't figured out one that works for me. I'm building a tabbed application in Storyboard, and I want to save the state of a view controller inside one tab that lives inside a navigation controller. It has dynamically created objects (labels and pictures) that I want to preserve when the app is shut down.
Thus far, I have the following in AppDelegate.m:
-(BOOL)application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder {
return YES; }
-(BOOL)application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder {
return YES; }
and in my viewController.m file:
-(void)encodeRestorableStateWithCoder:(NSCoder *)coder {
[coder encodeObject:self.view forKey:#"view"];
[super encodeRestorableStateWithCoder:coder]; }
-(void)decodeRestorableStateWithCoder:(NSCoder *)coder {
self.view = [coder decodeObjectForKey:#"view"];
[super decodeRestorableStateWithCoder:coder]; }
I've also assigned RestorationID's to UITabBarController, the navigation controller, and the view controller in question.
Right now, if I minimize and restore, data is preserved, but if I stop and restart using xcode, it's lost. What am I missing?
Try to run an app, push the Home button twice to go to the app switcher, swipe your app away to close it and run it by clicking the icon on home screen (not from Xcode!). Is now everything ok?
I suppose when you run your app from Xcode it not just relaunches it, but reinstalls it, so that is not exactly what you need.
Related
This question already has answers here:
Prevent iOS from taking screen capture of app before going into background
(10 answers)
Closed 3 years ago.
I am new to IOS development
I want to remove my application from recent apps which is developed in Objective C.
I tried UIApplicationExitsOnSuspend in
info.plist
, but no luck still application is showing in info.plist.
Can anyone help me on this.
Thanks in Advance !!!
You could use concept of the cover window.
When app will resign active state you show your cover, and system will take snapshot of that cover instead of last visible UIViewController.
When app will become active you hide and deallocate your cover window.
Here is example
#import "AppDelegate.h"
#interface AppDelegate ()
#property (nonatomic) UIWindow *coverWindow;
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
self.coverWindow = UIWindow.new;
self.coverWindow.rootViewController = UIViewController.new;
[self.coverWindow makeKeyAndVisible];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[self.coverWindow removeFromSuperview];
self.coverWindow = nil;
}
#end
UIApplicationExitsOnSuspend is deprecated. You shouldn't use it any more. There has been reports of apple rejecting apps with that key. As per apple:
Deprecated
The system now automatically suspends apps leaving the foreground when
they don’t require background execution. For more information, see
About the Background Execution Sequence.
So for now, you are stuck with letting apple handle the background state of apps. Forcefully trying to exit the app by any manner would lead to a rejection from App Store.
UPDATE
I just noticed your comment saying what you actually want. To prevent the Background Snapshot, you can add a custom view to the window. This is similar to the answer posted by Mark Agranal below, but the thing is you don't need to add a new Window or new ViewController. You can simply add a custom view to the window and remove the view when the app reenters active state. In your AppDelegate:
// The view to use as a mask
#property (nonatomic, weak) UIView* coverView;
// Add the view to window
-(void)applicationWillResignActive:(UIApplication *)application
{
coverView = [[UIView alloc]initWithFrame:[self.window frame]];
[self.window addSubview:coverView];
}
// Remove the view to window
- (void)applicationDidBecomeActive:(UIApplication *)application
{
if(coverView != nil) {
[coverView removeFromSuperview];
coverView = nil;
}
}
Note that you can add any view to the window using the above method. The system will take screenshot of the added view and hence the sensitive user data will be protected.
I have implemented spotlight search in my application, Everything is working fine, But in some situation I want to require to know that application launch from spotlight search or not ? please help me how can I know this.
AppDelegate.m
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray *restorableObjects))restorationHandler{
self.isSportlight = YES;
self.strSportlightUnitname = [userActivity.userInfo valueForKey:#"kCSSearchableItemActivityIdentifier"];
return YES;
}
rootViewcontroller.m
- (void)viewDidLoad
{
if(!appDel.isSportlight){
[self OnLaunchSettings];
}else{
[self setupSportLightEvent];
}
}
Aspected result is appDel.isSportlight = TRUE But appDel.isSportlight always got FALSE because "continueUserActivity:(NSUserActivity *)userActivity restorationHandler" method call after "ViewDidLoad"
The root view controller shouldn't be checking in with the app delegate, that relationship is the wrong way round. The app delegate should be telling the root view controller (or possibly posting a global notification if other controllers need to know about it) so that it can respond at any time, not just when it's initially setting things up. You need to write your code so that you can switch between different states. That would usually be done by pushing or adding/removing child view controllers so you compartmentalise the functionality of each mode.
I have a tabbed application with 2 storyboards. In the 2nd storyboard I have a restoration-ID.
I implemented the following in the AppDelegate:
-(BOOL)application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder
{
return YES;
}
-(BOOL)application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder
{
return YES;
}
and this in my SecondViewController:
-(void)encodeRestorableStateWithCoder:(NSCoder *)coder
{
[coder encodeObject:self.myTextView.text forKey:#"unsavedText"];
[super encodeRestorableStateWithCoder:coder];
}
-(void)decodeRestorableStateWithCoder:(NSCoder *)coder
{
self.myTextView.text=[coder decodeObjectForKey:#"unsavedText"];
[super decodeRestorableStateWithCoder:coder];
}
But when i go to Home with iOS simulator , stop the running application and restart, the TextView doesn't restore text, my application starts in a first tab.
You may have missed assigning a restoration ID elsewhere in your view hierarchy.
Did you also change your AppDelegate from -application:didFinishLaunchingWithOptions: to application:willFinishLaunchingWithOptions:?
There are some useful state restoration tools available at the Downloads for Apple Developers page.
Search for restoration to find a debug profile which logs your app's state restoration to the console, as well as the restorationArchiveTool to dump the state restoration data in human-readable format.
I am facing the issue in preserving the state of my application.
My application is based on storyboard, this is how it goes.
First I have a navigation controller, then a login screen. When user logs in, I push it to a controller, which have table view on left side, with UITabbar Items, this is mainly a custom tab bar. On clicking every button, I add that container view to the the self. The application state preserves till here, But whenever I push further, it doesn't preserve and restore the state. On the custom tab bar, I have even encode and decoded the child view controller, and add accordingly when state restores. But when I navigate further it doesn't. Further I saw a strange behavior, If I initially hit the first tab item and move further in the navigation, it preserve and restore the state further.
One more thing, when I run the application on the simulator by hitting stop and play button, it shows the preserve view for few seconds and come back to the custom tab bar.
Any help would be appreciated.
My Tabbar code is
// encodeRestorableStateWithCoder is called when the app is suspended to the background
- (void)encodeRestorableStateWithCoder:(NSCoder *)coder
{
NSLog(#"ParentViewController: encodeRestorableStateWithCoder");
[coder encodeObject:theNavigationController forKey:#"RootNav"];
[coder encodeObject:ssTodayNaVController forKey:#"TodaysCallNavEncode"];
// remember our children view controllers
[coder encodeObject:self.ssDasboardVC forKey:#"DashboardChild"];
[coder encodeObject:self.ssMessageVC forKey:#"MessageChild"];
[coder encodeObject:self.ssSearchCustomerVC forKey:SEARCH_CUSTOMER_SCENE];
[coder encodeObject:self.ssTodaysCallVC forKey:TODAYS_SCENE];
// remember the segmented control state
[coder encodeInteger:self.tabBar.mSelectedIndex forKey:#"selectedIndex"];
[super encodeRestorableStateWithCoder:coder];
}
// decodeRestorableStateWithCoder is called when the app is re-launched
- (void)decodeRestorableStateWithCoder:(NSCoder *)coder
{
NSLog(#"ParentViewController: decodeRestorableStateWithCoder");
theNavigationController=[coder decodeObjectForKey:#"RootNav"];
ssTodayNaVController=[coder decodeObjectForKey:#"TodaysCallNavEncode"];
self.tabBar.mSelectedIndex = [coder decodeIntegerForKey:#"selectedIndex"];
[[NSNotificationCenter defaultCenter]postNotificationName:TABBUTTON_NOTIFICATION object:[NSNumber numberWithInteger:self.tabBar.mSelectedIndex]];
[self.tabBar reloadData];
[super decodeRestorableStateWithCoder:coder];
}
And if I print the saved controllers with this method, it prints but doesn't save
- (UIViewController *)application:(UIApplication *)application
viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents
coder:(NSCoder *)coder {
// return various navigation controllers here.
// actual view controlers will each be returned in their own classes
SCLogNotice(#"Saved Controllers are %#",identifierComponents);
return nil;
}
Cheers
I am new to Objective-C and I'm making my first app, a single-view app. When I run my program ,the fullscreen ad that is supposed to appear (I'm using RevMob) appears. However, when I exit the ad I get a black screen with a blue status bar at the top.
I have tried many things, such as setting my main view controller as initial view controller, restarting my computer, changing/removing debugger, resetting the iOS simulator, etc.
My Xcode version is 4.6 and my OS is mac OSX 10.8.4
I don't want to delete Xcode and I also don't want to remove ads because that is my only source of income.
Here is my code:
Appdelegate.m
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions
{
[RevMobAds startSessionWithAppID:#"myappid"];
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
[self.window makeKeyAndVisible];
return YES;
}
- (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.
}
- (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.
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
RevMobFullscreen *ad = [[RevMobAds session] fullscreen];
[ad loadWithSuccessHandler:^(RevMobFullscreen *fs) {
[fs showAd];
NSLog(#"Ad loaded");
} andLoadFailHandler:^(RevMobFullscreen *fs, NSError *error) {
NSLog(#"Ad error: %#",error);
} onClickHandler:^{
NSLog(#"Ad clicked");
} onCloseHandler:^{
NSLog(#"Ad closed");
}];
}
- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
- (void)dealloc
{
[_window release];
[super dealloc];
}
#end
Appdelegate.h:
#import <UIKit/UIKit.h>
#import "ViewController.h"
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic)UIWindow *window;
#end
There is nothing wrong with the ad code. What seems to be (not) happening is that your app has no content. I suspect that you haven't set up the rootViewController (initial view controller) for the app correctly.
This is what you need to do:
Get rid of all of the app ad code and just make an app that works. All it has to do is correctly show an initial view controller. You could just make a new project in XCode using the Single View Application template.
Add your app code as per this example. You will invoke the code from your initial view controller via a button.
Once that is working, you can add the code into - (void)applicationDidBecomeActive:(UIApplication *)application as per your question. When you dismiss the advert, you should see your initial view controller.