Why applicationDidFinishLaunching is called after viewDidLoad? [duplicate] - swift

I've been playing with the iPad's SplitView template in Xcode. Here are two of the many important methods that are auto-generated for you by the Split View-based Application template...
AppNameAppDelegate.m
#pragma mark -
#pragma mark Application lifecycle
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after app launch
rootViewController.managedObjectContext = self.managedObjectContext;
// Add the split view controller's view to the window and display.
[window addSubview:splitViewController.view];
[window makeKeyAndVisible];
return YES;
}
RootViewController.m
#pragma mark -
#pragma mark View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
self.clearsSelectionOnViewWillAppear = NO;
self.contentSizeForViewInPopover = CGSizeMake(320.0, 600.0);
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
When you build and run the project before making any changes at all, the application:didFinishLaunchingWithOptions method is called before the RootViewController:viewDidLoad method is called. I'm new to iPhone development, but I'm assuming this is the correct and typical sequence. So here are the changes I made...
Once I confirmed everything was working without any modifications, I changed the RootViewController code and set it as a subclass of UIViewController (instead of UITableViewController by default) and made the respective adjustments in Interface Builder. I built and ran, everything was still working fine.
Then, I added a UIView (with nothing in it) to the RootView in IB and when I built and ran it, suddenly the RootViewController:viewDidLoad is being called before the application:didFinishLaunchingWithOptions method.
I need to get it back to the way it was working before because, as you can see in the code, the viewDidLoad method depends on didFinishLauchingWithOptions method to execute so it can set the rootViewController's managedObjectContext that it uses to perform the fetch request.
Any ideas what caused this?
Any ideas how I can fix this?
Thanks so much in advance for your help! I'm gonna keep researching and playing with the code.

In the template app -applicationDidFinishLaunching adds RootViewController's view to the window, causing the view to load, so obviously -viewDidLoad will follow - applicationDidFinishLaunching.
ViewDidLoad is (indirectly) called from applicationDidFinishLaunching.
If, as you say, viewDidLoad is being called before applicationDidFinishLaunching it is because you have done something to cause the view to load before applicationDidFinishLaunching is called.
Did you add a breakpoint in -viewDidLoad and look at the stacktrace to see what was responsible for calling it?

Where are you initializing RootViewController? Typically, you do so in applicationDidFinishLaunching (at least on the iPhone). If you are initializing it in your app delegate's init method, that could cause the root view controller's viewDidLoad method to be invoked before applicationDidFinishLaunching.

This is propabaly caused by the fact that in MainWindow.xib, your application delegate object is not connected to File's Owner (UIApplication). You can open the MainWindow.xib and right click on your App Delegate to see if it has a connection in Referencing Outlet to File's Owner. If not, set it to. And this will fix your problem.

Related

viewDidAppear isn't getting called after running from the background

Method viewDidAppear is not getting called after going from the background back into my iPhone Application.
Does anyone have a solution for this?
I need to call a method every time my MainViewController is shown (even after returning from the background) that will change a label to the new date.
viewDidAppear doesn't seem to be working properly.
Actually it is working properly.
Instead, consider installing a notification handler for UIApplicationDidBecomeActiveNotification or UIApplicationWillEnterForegroundNotification, whichever may be more appropriate for your situation. From there you could do an update of your GUI.
You will have the UIApplicationWillEnterForegroundNotification method in App delegate.Now you need to create the object of the class you want and call the viewwillAppear
this method will be called when the app is in background.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
}
now for the view will appear code like this
- (void)applicationWillEnterForeground:(UIApplication *)application
{
ViewController *vc = [ViewController alloc]init];//you can use your viewcontroller here
[vc viewDidAppear:YES];// this will call the method.
}
Try using viewWillAppear if you dont mind doing this process before the is actually shown.
Else you can use the method.
- (void)applicationWillEnterForeground:(UIApplication *)application{}

How often is viewDidLoad called?

When the user switches to another program and then back again., the original program's view will be replaced by a new view from another program. So when the user switches back to the original program, would viewDidLoad be called the second time ?
Am asking this because if this is the case, then the initialization code placed inside viewDidLoad would be executed every time the user switches the screen back and forth. And this could result in reseting views and loosing unfinished works of the user ...
Don't do view controller initialisation in viewDidLoad. This is a common mistake.
For stuff that should only happen once when the view controller is loaded, do it in the controller's init method, like this:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)bundleOrNil
{
if ((self = [super initWithNibName:nibNameOrNil bundle:bundleOrNil]))
{
//do your initialisation here
}
return self;
}
The initWithNibName:bundle: method is called before the view is loaded from the nib, and is only called once in the lifespan of the view controller.
The controller's view can be loaded and unloaded multiple times during the lifespan of the controller and viewDidLoad will be called every time. It may be unloaded whenever it's not on screen, usually if memory is low.
If you do set stuff up in viewDidLoad (e.g. adding subviews programmatically) you should always unset them again in viewDidUnload.
Think of viewDidLoad and viewDidUnload as being like the init/dealloc for the view property of the view controller. For stuff that relates to the views, create and release it in those methods. For stuff that relates to the controller itself, create and release it in initWithNibName and dealloc.
UPDATE: On iOS 6 and later, viewDidUnload is never called any more (unless the view is explicitly set to nil in the code), and so viewDidLoad will typically only be called once in the life of a view controller. This makes the advice above less critical, but it's still best practice, and still necessary if you need to support iOS 5 and earlier.
UPDATE 2: If you are loading your view controller from a Storyboard (which is now the recommended practice) instead of creating it programmatically then initWithNibName:bundle: will not be called. Use initWithCoder: or awakeFromNib to initialize your controller instead.
#Nick Lockwood provides excellent information, but there are a few more things to remember.
First, initWithNibName:bundle: is not called if the view controller is instantiated from a nib file or storyboard. In that case, initWithCoder: and awakeFromNib are called instead. This situation used to be somewhat uncommon on iOS, but with the addition of storyboards it is now much more common for view controllers to bypass initWithNibName:bundle:.
I recommend putting non-UI initialization code in a separate method (I call mine setup) and call it from both initWithNibName:bundle: and awakeFromNib. But I only do this if it's important that that initialization only run once. Otherwise I put it in viewWillAppear: to be as lazy-load as possible.
Second, you should not do anything that references self.view in init... or awakeFromNib. You should never reference self.view until viewDidLoad is called (otherwise you will force the nib file to be loaded sooner than it is needed). UI-related things should go in viewDidLoad if they're related to setting up the views, or viewWillAppear: if they're related to configuring the views (i.e. loading them with data).
So the way I usually set these things up:
#implementation
- (void)setup {
// Non-UI initialization goes here. It will only ever be called once.
}
- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)bundle {
if ((self = [super initWithNibName:nibName bundle:bundle])) {
[self setup];
}
return self;
}
- (void)awakeFromNib {
[self setup];
}
- (void)viewDidLoad {
// Any UI-related configuration goes here. It may be called multiple times,
// but each time it is called, `self.view` will be freshly loaded from the nib
// file.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Set all IBOutlets to `nil` here.
// Drop any lazy-load data that you didn't drop in viewWillDisappear:
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Most data loading should go here to make sure the view matches the model
// every time it's put on the screen. This is also a good place to observe
// notifications and KVO, and to setup timers.
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// Unregister from notifications and KVO here (balancing viewWillAppear:).
// Stop timers.
// This is a good place to tidy things up, free memory, save things to
// the model, etc.
}
- (void)dealloc {
// standard release stuff if non-ARC
[[NSNotificationCenter defaultCenter] removeObvserver:self]; // If you observed anything
// Stop timers.
// Don't unregister KVO here. Observe and remove KVO in viewWill(Dis)appear.
}
#end
-viewDidLoad will be called once whenever the view controller needs to load its view hierarchy. Obviously, that'll happen the first time that the controller accesses its view. If the view controller later unloads its view, then -viewDidLoad will be called again the next time the view is loaded. A view controller won't unload its view just because the view is hidden, but it might do so if memory starts to run low.
A view controller should know the state of its views and be able to set them up as necessary in its -viewDidLoad method. Views shouldn't be used to store state -- nothing should be irrevocably lost just because the view is unloaded.
So when the user switches back to the original program, would
viewDidLoad be called the second time ?
(Above is from the op)
In those cases there are two methods to be called:
- (void)applicationWillEnterForeground:(UIApplication *)application;
reopening a backgrounded app (from task manager or from springboard again)
unlocking device which is locked when the app is active.
- (void)applicationDidBecomeActive:(UIApplication *)application
after phone calls
notification center dismissal
task manager dismissal (double tap home button & double tap again)
From the docs:
This method is called after the view controller has loaded its associated views into memory.
So, it is called whenever the view controller has its views loaded into memory. This could be the first time the view is loaded and never again, or every time the view is made visible if your view unloads constantly (viewDidUnload due to memory constraints, etc.)

How to dealloc uiviewcontroller/unload views on logout action

I think this is a pretty common usecase as I have seen it in several apps. But after spending couple of days, I am still struggling with this. I have a structure like the following:
UITabBarController
-- UINavigationController1
---- UITableViewController1
-- UINavigationController2
---- UITableViewController2
Now I have a logout button on UITableViewController2. When I click on that logout button I want all and any viewcontroller to be deallocated, all view unloaded. Basically start fresh like launching the app. I basically want the viewDidLoad on each of those UITableViewController called again.
I tried the following method to be called in my appdelegate when the logout action on UITableViewController2 is taken.
-(void) logout {
for (UINavigationController* ctrl in self.tabBarController.viewControllers) {
[ctrl popToRootViewControllerAnimated:NO];
ctrl.visibleViewController.view = nil;
}
[self.tabBarController.view removeFromSuperview];
[self.window addSubview:self.tabBarController.view];
}
But alas, it does not seem to work?
Any ideas how such a thing is accomplished? Also I see different behaviors in iOS4 vs iOS5 with the visibleViewController. I am not using any modal viewcontroller here. Any gotchas?
Update: I am not using ARC
thanks
mbh
Your for-loop will release and thus dealloc any view controllers that you have pushed onto the respective UINavigationController roots (depending on how many tabs you have), i.e. as these will not have a superview when you pop back to the root of each navigation controller, these are dealloc-ed automatically. These are your UITableViewControllers taken care of.
As for the respective UINavigationControllers, you would need your tabbar-controller to release the old instance. IMHO, this should be done for you when you release the UITabBarController.
This then leaves the UITabBarController itself. I don't think it can be done tbh. Your code will only remove the view, but not dealloc the tabbar controller itself. And as Krishna K points out, you need at least one view controller to reload all others.
Putting the code into the appdelegate makes sense, but you need to ensure that your logout() will not cause a retain on the UITableViewController2 as well as the UITabbarController as it's called from UITableViewController2 somewhere.
One idea to explore, does your AppDelegate hold an instance to the TabBar-Controller which you could release and create a new instance after removing the view from self.window?
// manually create UITabBarController - AppDelegate holds instance
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
mytabcontroller=[[UITabBarController alloc] initWithNibName:#"foo" bundle:nil];
}
- (void) logout {
[self.tabBarController.view removeFromSuperview];
[mytabcontroller release];
mytabcontroller=[[UITabBarController alloc] initWithNibName:#"foo" bundle:nil];
[self.window addSubview:self.tabBarController.view];
}
But as I said, there might be caveats with memory management at this point.
You need to release your view controllers. When their release method is called, that method should include statements to release all of its object's resources (and also dealloc its superclass).
Your rootViewController for both Navigation controllers are their respective TableView controllers. So I don't think popToRootViewController would do anything.
You probably need to reset the data and refresh the views instead of deallocating the views.

What is the difference between view 'did load' method and 'didFinishLaunching' application

In the iPhone SDK, can anyone explain the difference between application DidFinishLaunching in delegate and ViewDidLoad method in ViewControler?
applicationDidFinishLaunching is called by the App Delgate when your application has finished launching. This method is useful for doing setup as soon as possible. Examples of this could include setting up GameCenter, and doing some first launch checking.
viewDidLoad is called by a UIViewController after the view is loaded, usually from the nib. However, in some cases, you may want to do setup before the view is loaded. In that case, use
viewWillLoad is called just before the view is loaded, usually from the nib. For the most part, it will not make much of a difference wether you use viewDidLoad or viewWillLoad. However, some setup may have to be done after the view is loaded and other setup you may want to do before the screen displays anything.
applicationDidFinishLaunching is for initial appwide setup, viewWillLoad is for setup before the view is displayed, and viewDidLoad is for setup right after the view is loaded.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions gets called when your app is finished launching; and viewDidLoad: gets called when an UIView controlled by UIViewController is loaded.
viewDidLoad is the method that is called once the view has been loaded. It is a place where you can insert code that does initial setup of the view once it has been loaded.
The applicationDidFinishLaunching: method of the NSApplication delegate will be called when the app has finished loading.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
Method available only in application AppDelegate it calles only ones at the time of app is loaded you can do all the stuff related to your application prelaunch here.
-(void)viewDidLoad: called whenever a view is loaded.
it also call ones when the view is loaded
but it's has own copy for every viewController you can do any stuf related to that controller inside it.

Does UIWindow randomly remove its subview?

The reason I ask is because I was doing the following in AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Add the view controller's view to the window and display.
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
PageViewController *pageViewController = [[PageViewController alloc] init];
[window addSubview:pageViewController.view];
[pageViewController release];
[window makeKeyAndVisible];
return YES;
}
But, when I tried to scroll the pageView, whose controller implements the UIScrollViewDelegate protocol, I got an error like:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSURL scrollViewDidScroll:]: unrecognized selector sent to instance 0x5f42a80
When I took out [pageViewController release];, this error went away. This is weird to me because window should retain ... OH GOD!!! duh... it retains the pageViewController's view, not the pageViewController.
I get it now why it's wrong to release pageViewController. Silly me... I think it's time for a break.
It looks like you figured this part out, but the answer to your question is no, UIWindow does not randomly remove its subview AFAIK. In this case, it's retaining pageViewController.view not pageViewController. So, you shouldn't release pageViewController nor should you release pageViewController.view because pageViewController.view will automatically get released when pageViewController is released. I don't see where you are releasing pageViewController. I recommend making it an ivar of the AppDelegate and then releasing it in the AppDelegate's dealloc method, as you've probably done with window. The dealloc method never gets called, so pageViewController (and window) will never explicitly get released anyway, but making pageViewController an ivar is better style IMHO. Either way, I'll bet they get cleaned up when the application terminates.