Life as Clay

Cocoa Programming for Mac OS X: Chapter 12 Challenge

with 10 comments


Pesky!

This is another challenge that is easy but can trip you up when you test it… The challenge reads:

Create a nib file with a custom About panel. Add an outlet to AppController to point to the new window. Also add a showAboutPanel: method. Load the nib by using NSBundle, and make AppController File’s Owner.

The easy part of this is that you don’t have to create a subclass like you did with PreferenceController. Read the bits about NSBundle and you’ll see that all you have to do is to modify the File’s Owner (AppController) and create the nib/xib file. That’s simple enough.

I initially didn’t understand the purpose of creating an IBOutlet for the About panel. After testing the application, I realized that the implementation of showAboutPanel: allowed for multiple panels to be opened.

Pesky! Read the rest of the post to see the code and instructions.

To do this properly:

  • Create the About.xib file in the same manner as done with the preferences file during the chapter. Make sure that you set the class of File’s Owner to be AppController. You don’t need any of the color wells or check boxes. You do, however, need to change one of the check boxes. Select the Panel object in Interface Builder and then UNcheck the “Release When Closed” check box in the Behavior section of the Attributes inspector. If you do not uncheck this, then the IBOutlet that you declare in AppController.h is released and you can only open the About panel one time without the program crashing.
  • Follow the directions and declare IBOutlet NSPanel aboutPanel; in AppController.h. This is part of what I didn’t understand at first, since the bit in the book about NSBundle points out that you can load the nib file without NSWindowController.
  • Declare -(IBAction)showAboutPanel:(id)sender; in AppController.h. Your AppController.h file should look like this:
#import <Cocoa/Cocoa.h>
@class PreferenceController;

@interface AppController : NSObject {
	PreferenceController *preferenceController;
	IBOutlet NSPanel *aboutPanel;
}

- (IBAction)showPreferencePanel:(id)sender;
- (IBAction)showAboutPanel:(id)sender;

@end
  • Open MainMenu.xib in Interface Builder and connect the About menu item to App Controller. Control-click on AppController and drag from the received actions for showAboutPanel: to the About menu item and release. Follow the same procedure that you did for the preference panel earlier in the chapter. The method show below is, incidentally, the same as control dragging from the About RaiseMan menu item and releasing it over App Controller, then selecting showAboutPanel: from the Received Actions popup.

  • Return to About.xib in Interface Builder and control-click on File’s Owner. You need to tell File’s Owner (AppController) what your IBOutlet called aboutPanel points to, so drag from the little circle to the Panel icon in the same window, like this:

  • Ok, now you need to write the -(IBAction)showAboutPanel: method in AppController.m. However, keep in mind that you want to check to see whether the aboutPanel already is open. Create a simple if statement. This was the pesky part and is one of the reasons for creating the IBOutlet. The logic is: if the aboutPanel does not exist, create it and bring it to the front. If it does exist, bring it to the front. The code for AppController.m looks like this:
#import "AppController.h"
#import "PreferenceController.h"

@implementation AppController

- (IBAction)showPreferencePanel:(id)sender
{
	// Is preferenceController nil?
	if (!preferenceController) {
		preferenceController = [[PreferenceController alloc] init];
	}
	NSLog(@"showing %@", preferenceController);
	[preferenceController showWindow:self];
}

- (IBAction)showAboutPanel:(id)sender
{
	if (!aboutPanel) {
		BOOL successful = [NSBundle loadNibNamed:@"About" owner:self];

		// This makes the about panel the key window - gives it focus
		[aboutPanel makeKeyAndOrderFront:nil];

		NSLog(@"About panel loaded with success status %d.", successful);
		return;
	}

	// The About panel is open, so just give it focus.
	[aboutPanel makeKeyAndOrderFront:nil];
	NSLog(@"About panel given focus.");
}
@end

Now, assuming that you followed the steps and connected everything in Interface Builder, you should be set to test the application. Make sure that you can’t open multiple about panels.

On a larger scale, think about this from the point of view of using NSBundle to open the About.xib file. The text just above the Challenge in the book says that you could do it like this: BOOL successful = [NSBundle loadNibNamed:@"About" owner:someObject]; (In this instance, since this code exists within AppController and AppController is the File’s Owner, then someObject can be replaced with self.) This code is a ‘dumb’ opening of the nib file. It doesn’t know if the About panel already is open.

To prevent it from doing the same thing repeatedly, you have to insert the if statement to check whether the window already is open. Hence, the declaration of the IBOutlet NSPanel aboutPanel; It allows you to check for the existence of the About window.

Advertisements

Written by Clay

January 5, 2010 at 22:12

Posted in Cocoa, Code, Objective-C

Tagged with , , ,

10 Responses

Subscribe to comments with RSS.

  1. “UNcheck the “Release When Closed” check box in the Behavior section of the Attributes inspector.” – Thx dude, I was staring at the code and reiterating your description of what to do several times until i noticed this was the cause of my crashes when reopening the About window!

    Sven

    September 30, 2010 at 16:36

  2. thanks…

    igooodman

    January 17, 2011 at 02:34

  3. I managed to complete this challenge by making the AppController to be the subclass of NSWindowController, and call showWindow of NSWindowController (due to the crash when calling NSPanel’s makeKeyAndFront)

    Your solution is the true work!
    Thanks!

    ZZig

    May 23, 2011 at 08:23

  4. I spent a long time looking for this on the web. Thanks for the tutorial!

    I offer up my initial search keywords: OSX NSPanel Popup Tutorial

    Nate

    September 27, 2011 at 12:09

  5. I think you mean ‘yo mama’… ;-) Awesome walkthrough by the way

    adamjansch

    January 6, 2012 at 18:06

  6. Everything works for me, except…

    – When the panel is launched, it’s on top of the app’s window, but it doesn’t have focus…

    – The panel doesn’t limit itself to one instance … if you click the “About” menu item again, it opens another, and another, and another…

    – And, most annoyingly, every time you open the panel, it reloads the WebView I have in the app’s main window!

    Any thoughts, or updates? This looks like it was done in Xcode pre-version 4, so all the IB stuff looks different, and I never used any of the earlier, non-integrated versions of Xcode/IB, so it’s a little confusing

    Thanks for any help!

    Matt

    April 18, 2012 at 14:56

    • Well, it sure helps to not skip steps! The first two problem have been alleviated; the last one, however, is still a problem. It only happens the first time you open the panel (after closing it and opening it again, the WebView is not reloaded), which is that my WebView reloads. Any ideas?

      Matt

      April 18, 2012 at 15:11

      • It’s been a long time since I’ve looked at that (and unfortunately, I lost my Xcode projects in a hard drive crash). I suspect something is happening during the init method for the panel that isn’t being called again. If the panel is retained by another object after its creation, then the init method only would be called once. You might browse over to the Big Nerd Ranch forum where people are actively looking at those problems. :)

        It could also be that the web view reloads when it is sent the message to make it key.

        Clay

        April 18, 2012 at 15:16

      • Never mind on the third one, too … you can just delete these comments. I added an if statement to my awakeFromNib: method that causes it to only execute if aboutPanel is not open, and because it executes immediately upon launch, it’s not an issue. Thanks for the great tutorial!

        Matt

        April 18, 2012 at 15:17

      • Glad you got it working!

        Clay

        April 18, 2012 at 15:19


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: