0

I'm programming a Cocoa application and want the application to work as a kind of Wizard. So in the main window I have a Custom View that interacts with the user and changes from a sign in to a device activation screen as they step through the stages of the wizard. I have currently overridden the WizardViewController's awakeFromNib method:

- (void)awakeFromNib{
   //If no redirect request save, add first view: ID Login
   NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
   NSString *tokenRequest = [defaults objectForKey:@"redirectRequestToken"];
   if (!tokenRequest){
       SignInWithIDViewController *signInViewController = [[SignInWithIDViewController alloc] initWithNibName:@"SignInWithIDViewController" bundle:nil];
       [wizardView addSubview:[signInViewController view]];
   } else {
    NSLog(@"Have already logged in.");
   }
}

As is, initWithNibName in SignInIDViewController gets called twice, once explicitly by me, and again when the view is loaded (presumably through loadView). However, if I simply call init then initWithNib name is only called once, but the wrong xib file is loaded (of the DeviceActivationViewController class). I can't seem to figure out what I'm doing wrong, because the signInViewController should not be init twice, but I need the proper xib file in IB to display.

The only other method I have in this class currently that is not a user interface IBAction is the generated initWithNibName method plus an added NSLog statement.

Katie
  • 253
  • 1
  • 3
  • 13
  • How do you know that initWithNibName is called twice? What result do you see? – rdelmar May 31 '12 at 15:58
  • When you generate a new subclass of NSViewController, initWithNibName is automatically generated with the following code: `code`- (id)initWithNibName:(NSString *)nibNameOrNil bundle: (NSBundle *)nibBundleOrNil{ self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Initialization code here. } return self; } So I added a NSLog to track it. This also affects functionality in other methods--namely, my awakeFromNib gets called twice in two different instances which I know because each instance opens a server on a different port. – Katie May 31 '12 at 16:20
  • Just to clarify the last sentence: my awakeFromNib gets called once from each instance. Two total. – Katie May 31 '12 at 16:26
  • It's hard to tell why this is happening from the information you posted. I made a little test app that alloc initWithNibName's the wizard controller from the app delegate (setting the content view of the main window to the wizard controller's view) and then put the code you posted above in either the initWithNibName or awakeFromNib methods of the wizard controller, and I didn't see the SignInViewController init'd twice. Did you do anything else in the xib file, other than populate your custom views with whatever subviews you needed? – rdelmar May 31 '12 at 20:06
  • I realize it's hard to understand exactly what I'm saying, but with 4+ classes, I didn't want to put all the code. Thank you for looking at this and trying it out, though. Currently my MainMenu.xib has nothing in it and defaults to the WizardViewController that contains the window, the custom view and a next button. (This initWithNibName only gets called once.) The SignInWithIDViewController.xib has a WebView and a button on a custom view. – Katie May 31 '12 at 21:10
  • You didn't add any objects (blue cubes) in IB did you? And, how do you instantiate your WizardViewController – rdelmar May 31 '12 at 21:41
  • The code in my app delegate loads the WizardViewController.xib: `code` if (!self.window) [NSBundle loadNibNamed:@"WizardViewController" owner:self]; [self.window makeKeyAndOrderFront:self]; I did add objects (the cubes) to each xib file and made them subclasses of their controllers and made outlet connections to those objects. Is there anything wrong with that? Typically if I try to connect using the File Owner, I get issues with action connections. – Katie Jun 01 '12 at 13:05

1 Answers1

1

I think that creating the objects in IB (the blue cubes), and instantiating them in code is the problem. If you've created objects for them in IB, then they will be instantiated in awakeFromNib, you shouldn't also call alloc init on them in code -- that will create a new instance.

I don't have a lot of experience with using view controllers in OSX, but it seems that you can't connect IBActions to the view controller (as file's owner). The way I made it work, was to subclass the custom view (that's created for you when you add a view controller), change the class of that view to your new subclass, and put the action methods in that class. It seems like this should be something that would be handled by the view controller, but I think it not working has something to do with the view controller not being in the responder chain in OSX (whereas I think it is in iOS).

After Edit: After a detour into memory management problems, I think I found the best way to do this. You can, and probably should (to comply with Apple's MVC paradigm) put the button methods in the view controller class rather than in the view as I said above. You actually can connect the IBActions to the view controller (as File's Owner), you just need to make sure that the view controller is retained when you instantiate it in code. To do this, you need to make signInViewController a property in whatever class you're instantiating the SignInViewController class in, and use "retain" in the property declaration. Then you don't need to (and shouldn't) create any of the blue cubes in IB.

rdelmar
  • 103,982
  • 12
  • 207
  • 218
  • Thanks! It took me a bit, but I've eliminated the initWithNibName being called twice using your response, but now there's essentially nothing (other than init and dealloc) in the SignInWithIDViewController--it seems to be a big waste of space. Is there any way to eliminate the SignInWithIDViewController and just have the NSView subclass? – Katie Jun 01 '12 at 17:53
  • @Katie, I think you should use the view controller, but put the IBActions in there -- see my edited answer for more details. – rdelmar Jun 03 '12 at 03:19
  • Yup, that seemed to work. I think it was really as simple as making sure the xib file's owner was the corresponding view controller class. Thanks! – Katie Jun 04 '12 at 13:28