Why sometimes initWithNibName not called? - objective-c

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
Everything works fine. The code above is standard written by compiler. Nothing strange. I put breakpoints there in my viewControllers just to see how things go.
Sometimes the function is called. Sometimes it's not.
'
In all cases things work fine. But what would be the circumstances where that function is not called?
Yes, I am using XIB for all those viewControllers.
I notice that viewControllers directly under the navigationViewController is the one where initWithNibName doesn't break. I wonder why.

When your NSViewController is defined in a xib file (usually as an IBOutlet), initWithNibName:bundle: is not called, rather initWithCoder: gets called. This is the case when you use Interface Builder to set your NSViewController as part of UITabBarController(for UIViewController) or UINavigationController, and almost always when using Storyboards.
Or you can try this one also:
myViewController *objMyViewController = [[MyViewController alloc] initWithNibName:#"WebViewController_iPhone" bundle:nil];
objMyViewController.managedObjectContext = self.managedObjectContext;
navController = [[UINavigationController alloc] initWithRootViewController:objMyViewController];
[self.window setRootViewController:navController];
[window makeKeyAndVisible];

Related

Which method called when appdelegate.m transfers control to viewController

I learn the ObjC program flow, so far I understand that chain begins in main.m->UIApplicationMain->AppDelegate->ViewController
The point I don't understand is to which method inside the ViewController the AppDelegate class transfers the focus...
I feel it is critical to understand this topic, so would be thankful for any clarifications.
I have this code of Appdelegate.m -
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
MasterViewController *masterViewController = [[MasterViewController alloc] init];
self.navigationController = [[UINavigationController alloc] initWithRootViewController: masterViewController];
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
return YES;
and inside the ViewController there are these methods -
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:YES animated: NO];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear: animated];
}
and others methods...
My questions are
To what method AppDelegate transfers control in MasterViewController. And does the control come back after MasterViewController "finished" its job or it is just looped?
How MasterViewController gets the xib name for initialisation (is it the same name as m file? i.e. what does it mean - nibNameOrNil bundle:nibBundleOrNil)
I see navigation controller involvement, however I don't understand how it is connected to viewcontroller....
If you catch my misunderstanding points - please be patient to explain... I feel that after this point I will be able to start...
(1) You're thinking about the logical flow in a procedural sort of way. The main run loop dispatches events down the responder chain. Your MasterViewController doesn't really 'finish'.
(2) The designated initializer for UIViewController is initWithNibName:bundle:. Your use of init here will not touch any associated nib file. If you wish to initialize MasterViewController from the nib, then you must use the designated initializer, e.g.
MasterViewController *masterViewController = [[MasterViewController alloc] initWithNibName:#"your-nib-name-goes-here" bundle:nil];
nibNameOrNil and nibBundleOrNil are just parameter names for the method. It's a tip to you that they may be nil. Take a look at the documentation for how the method behaves when those params are nil.
(3) UINavigationController is a subclass of UIViewController that presents content hierarchically. In this case, the application's window's root view controller is a navigation controller. In turn, that navigation controller's root view controller is your MasterViewController instance.
The documentation for UINavigationController describes it well.

Calling method in AppController from a ViewController

I'm trying to build a Cocoa app for OSX Lion.
I have this line in my AppController code:
self.viewController = [[LoginViewController alloc] initWithNibName:#"LoginViewController" bundle:nil appController:self];
[_view addSubview:[_viewController view]];
[[_viewController view] setFrame:[_view bounds]];
LoginViewController looks like this:
#implementation LoginViewController
#synthesize appController = _appController;
- (id) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil appController:(AppController *)appController {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
[self setAppController:appController];
NSLog(#"Appcontroller init: %#", _appController);
}
return self;
}
- (IBAction)login:(id)sender {
NSLog(#"Appcontroller login: %#", _appController);
}
The login method is connected to a button click.
Log:
2012-05-23 12:45:49.574 QBLoader[3241:503] Appcontroller init: <AppController: 0x7fe2ab210440>
2012-05-23 12:45:52.085 QBLoader[3241:503] Appcontroller login: (null)
Why is the second log line null?
Since you indicated there is more than one instance of LoginViewController, I would check your xibs to see if you have created an object of that type anywhere. In particular I would start with the xib where you wire up the -login: action. If you only have one instance of AppController, a common method is to create the object in your MainMenu.xib and attach it to an outlet in your app's delegate. Then you can use something like [[NSApp delegate] appController] to access it.

Access Root UINavigationController from Pushed UIViewController

I've run into a problem with my Project.
Every time I try to access my UINavigationController from a Pushed UIViewController, I get "(null)" as a return.
So this is my project is structured:
1. AppDelegate.m initalises UINavigation Controller
2. UINavigation Controller starts with Welcome Page
3. Welcome Page Pushes New UIViewController on Button Press
4. UIViewController returns (null) when self.navigationController called
OK, so here's some code, in my Welcome Page I'm calling this:
MyClass *theChatRoom = [[MyClass alloc] initWithNibName:#"ChatRoom" bundle:nil];
[theChatRoom setTitle:[unitCode text]];
[self.navigationController pushViewController:theChatRoom animated:YES];
Which works fine, it pushes the new View Controller.
But then in MyClass.m :
#implementation MyClass
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
NSLog([NSString stringWithFormat:#"%#",self.navigationController]);
}
return self;
}
This returns (null)
What am I doing wrong?
According the documentation, the navigationController of UIViewController:
This property is nil if the view controller is not embedded inside a
navigation controller.
That's said, if you access the navigationController before embedding into the UINavigationController(not push yet), it will return nil. So you can access it in the viewDidLoad but no in - (id)initWithNibName:bundle:.

In which method should I set the delegate of a UITextField?

Is it good practice to be setting the delegate of a UITextField in viewDidLoad or in an init method?
I tried setting the delegate as self in an init method, but it wasn't calling the corresponding delegate methods, when I moved the code into viewDidLoad, it registered as setting self as the delegate?
It seems that I should be able to set it in either method, if someone can help clear this up for me it would be much appreciated.
-(id) init {
self = [super init];
if (self)
textField.delegate = self; //this text field is an IBOutlet
//some other code here as well
return self;
}
OR
-(void)viewDidLoad {
[super viewDidLoad];
textField.delegate = self;
}
If your text field is an IBOutlet then until viewDidLoad method is called your text field will be nil (hence you set delegate to nil object). When viewDidLoad gets called it literally means that view was loaded and all IBOutlets and IBActions were connected and are at your disposal.
Assuming that your class is a UIViewController and is loaded from a NIB (since you have an IBOutlet), the proper init method to override is initWithCoder:(NSCoder*)decoder. What's happening right now:
iOS loads your NIB file and creates your UIViewController
The UIViewController is created by calling its initWithCoder:(NSCoder*)decoder method.
The first thing that initWithCoder does is to call init and therefore your code, before it has even decoded the NIB.
Because it hasn't decoded the NIB yet, your textField IBOutlet hasn't been set yet (if you debug it you should be able to see that its value is nil inside your init). And therefore setting the delegate doesn't do anything.
The easiest way to proceed is indeed to set your delegate in the viewDidLoad method; it can't be done in init. However it can be done by overriding initWithCoder:
- (id)initWithCoder:(NSCoder*)decoder {
self = [super initWithCoder:decoder];
if (self)
textField.delegate = self;
return self;
}
This time you wait until UIViewController's implementation of initWithCoder has finished decoding the NIB and so all your outlets have been set.

Bringing up new view controllers - releasing query

I've written some code where I bring up a new view (from my main view controller); then it calls the main controller when it is closed, like so -
-(void)showMyNewView {
MyNewViewController *myNewViewController = [[MyNewViewController alloc] initWithNibName:#"MyNewViewController" delegate:self];
[self.view addSubview:myNewViewController.view];
}
and then when the new one closes, it calls -
-(void)myNewViewControllerDidFinish:(MyNewViewController *)myNewViewController {
[myNewViewController.view removeFromSuperview];
[myNewViewController release];
}
Now this works fine, and there are no leaks, but the compiler moans with warnings about "Potential leak of an object allocated on line x and stored into myNewViewController".
I've been looking at Apple's presentModalViewController:animated: code, which also doesn't release the new modal view controller in the method which creates it, it seems to release it with a dismissModalViewControllerAnimated: call when the delegate's viewControllerDidFinish: method is called. Is there something I'm missing here? Using the presentModalViewController code doesn't generate any warnings. Many thanks for any help.
I think I've figured it out now, and I've written a small bit of code which gives me my own version of "presentModalViewController:animated:" with all the control I want. I'd be grateful to hear what more seasoned coders make of this (it's probably really straight forward but I've not been doing this for very long...), and if there are any problems with the code, etc -
Interface:
#import <UIKit/UIKit.h>
enum {
MyViewLoaderTransitionTypeNone = 0,
MyViewLoaderTransitionTypeSomeEffect,
MyViewLoaderTransitionTypeSomeOtherEffect
};
typedef NSInteger MyViewLoaderTransitionType;
#interface MyViewLoader : UIViewController {
UIViewController *myLoadedViewController;
}
#property (nonatomic, retain) UIViewController *myLoadedViewController;
-(void)loadView:(UIViewController *)theViewController withTransition:(MyViewLoaderTransitionType)theTransition;
-(void)dismissViewWithTransition:(MyViewLoaderTransitionType)theTransition;
#end
Implementation:
#import "MyViewLoader.h"
#implementation MyViewLoader
#synthesize myLoadedViewController;
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
;
}
return self;
}
-(void)dealloc {
[myLoadedViewController release];
[super dealloc];
}
-(void)loadView:(UIViewController *)theViewController withTransition:(MyViewLoaderTransitionType)theTransition {
[self setLoadedViewController:theViewController];
UIView *theLoadedView = theViewController.view;
[self.view addSubview:theLoadedView];
// do all sorts of transition stuff here
[theViewController viewWillAppear:NO];
}
-(void)dismissViewWithTransition:(MyViewLoaderTransitionType)theTransition {
UIView *theLoadedView = self.loadedViewController.view;
// do all sorts of transition stuff here
[theLoadedView removeFromSuperview];
self.loadedViewController = nil
}
I just use MyViewLoader as the superclass of any view controllers where I need it.
Thanks for any comments / help!
The usual thing to do here is, when you add a subview to a view, release the subview directly after. The parent view becomes responsible for the subview. When removeFromSuperview is called later, that decrements the retain count and the subview is automatically released.