PDA

View Full Version : Hillegass chapter 12, Challenge "About panel" help needed




petron
Jul 15, 2009, 02:09 PM
Hi,
I just went through the Challenge in chapter 15.

When ready with the "cahllenge" I do see the About panel only for a second or so. then it disapear.

I enable it in the following way in the AppController.m:

- (IBAction)showAboutPanel:(id)sender {
NSLog(@"Entered showAboutPanel");

BOOL success = [NSBundle loadNibNamed:@"About" owner:self];

if (success)
NSLog(@"Success");
else
NSLog(@"Not Success");
}



I do not know what I need to do with the outlet...

IBOutlet NSPanel *aboutPanel;


I will try to go through it again, but I will appreciate some help.

BR
/petron



petron
Jul 15, 2009, 02:51 PM
Now it works...

I have created new NSWindowController for About Palne and modified the AppController.m in the following way:

- (IBAction)showAboutPanel:(id)sender {
NSLog(@"Entered showAboutPanel");

BOOL success = [NSBundle loadNibNamed:@"About" owner:self];
if (success) {
NSLog(@"Success");
aboutController = [[AboutController alloc] initWithWindowNibName: @"About"];
[aboutController showWindow:self];
} else {
NSLog(@"Not Success");
}
}


/petron

thingsis
Jul 16, 2009, 01:31 AM
Hi,

actually this should be all you need:

-(IBAction)showAboutPanel:(id)sender {
[NSBundle loadNibNamed:@"AboutPanel" owner:self];
[aboutPanel setTitle:@"Outlet geht!"];
}

Of course you don't even need to set the title. I just used it to check whether my outlet is working fine. It seems pretty similar to what you are doing. It also works when I assign the result of the NSBundle... to a BOOL var - which makes it even closer to your example.

thingsis

petron
Jul 16, 2009, 01:51 AM
Hello Tingsis

It seems that I did something very wrong.

I do not have any outlet at all, since I did not know how to connect the Outlet "aboutPanel" and to whom to connect.

Is it declared in the AppController.h like this ?

IBOutlet NSPanel *aboutPanel;

I did create a NSwindowController and it works but your solution seems to be more sexy... How did you solved the Outlet connection ?

/petron

thingsis
Jul 16, 2009, 08:57 AM
Hi,

you are almost right ;)

IBOutlet NSWindow *aboutPanel;

It might also work using NSPanel. I do not have the object graph in my head. Also, in InterfaceBuilder you set File's Owner to AppController and connect the aboutPanel Outlet of File's Owner to the actual Panel. Then it should work.

Thingsis

thingsis
Jul 16, 2009, 08:59 AM
Hi again,

I just thought about it again...
Maybe the problem is not the actual outlet but that you forgot to set a class for File's Owner. Just an idea. Anyway, my post above is still valid I guess.

Thingsis

SRossi
Jul 16, 2009, 09:11 AM
Hi,

you are almost right ;)

IBOutlet NSWindow *aboutPanel;

It might also work using NSPanel. I do not have the object graph in my head. Also, in InterfaceBuilder you set File's Owner to AppController and connect the aboutPanel Outlet of File's Owner to the actual Panel. Then it should work.

Thingsis

When I did that challenge I didn't have any Outlets I did exactly the same as what happened during the chapter my AppController.h file is:

@class PrefrenceController;
@class AboutController;

@interface AppController : NSObject {
PrefrenceController *prefrenceController;
AboutController *aboutController;
}
- (IBAction)showPrefrencePanel:(id)sender;
- (IBAction)showAboutPanel:(id)sender;

@end

Then I implemented the AboutController file's which opened the nib.

Hope this helps.

Stephen

Edit: Also you make the File Owner the class of your AboutController. Not the AppController.

Edit again: About adding the outlet you would point it to NSPanel not NSWindow. Should work after that.

petron
Jul 16, 2009, 09:14 PM
Hi,
I wake up early and needed to try....

First answer to Stephen..
Well, both you and I went through the same path with very small differences. It of course works but it is not as sexy as the solution that Thingis made and what the Hillegass had in mind. The Hillegass in his "Challenge" description specified the use of an Outlet. Any way thanks for comments.

Secondly answer to Thingis..
Thanks for the tips regarding the "you set File's Owner to AppController"
After I done this the possibility to connect the Outlet became obvious and possible.

Here is the actual method, it looks even better if one does not need to change the title of the panel window. It even could be one_liners method if I will drop the NSLog lines.


- (IBAction)showAboutPanel : (id)sender {
NSLog(@"Entered showAboutPanel");

BOOL success = [NSBundle loadNibNamed:@"About" owner:self];
if (success) {
NSLog(@"Loaded nib");
} else {
NSLog(@"Not able to load nib");
}
}


The last comment is that I think that you should use the NSPanel and not NSWindow. Both solutions works but the NSPanel is more correct class.

Any way thanks to all for the help and cooperation. The most important thing is that I learned something new and maybe I understand the subject a bit better.

/petron

beebauman
Aug 16, 2009, 07:35 PM
I believe that the objective of the challenge was to avoid using an NSWindowController (Hillegass wants you to understand what the controller is doing for you behind the scenes, and how to do it yourself if you don't use one). The purpose of setting the outlet is to enable the AppController to know about and message the about panel. Here is what you do when the user selects the "About" option from the menu:

1) Load the NIB (but only if it hasn't already been loaded)
2) Tell the about panel not to release itself when the window is closed
3) Display the about panel again if it has been closed

Relevant code:

AppController.h:

@interface AppController : NSObject {
IBOutlet NSPanel *aboutPanel;
BOOL aboutNibWasLoaded;
}

- (IBAction)showAboutPanel:(id)sender;

AppController.m:

- (IBAction)showAboutPanel:(id)sender
{
if ( aboutNibWasLoaded == 0 ) {
aboutNibWasLoaded = [NSBundle loadNibNamed:@"About" owner:self];
[aboutPanel setReleasedWhenClosed:NO];
}

if ( ! [aboutPanel isVisible] )
[aboutPanel makeKeyAndOrderFront:self];
}

mdeh
Aug 16, 2009, 10:39 PM
I believe that the objective of the challenge was to avoid using an NSWindowController (Hillegass wants you to understand what the controller is doing for you behind the scenes, and how to do it yourself if you don't use one).

Yes...makes perfect sense now. I too wondered what the purpose of the outlet was.

Here's my code....taking most from yours.

-(id) init
{

self = [ super init];
nibIsLoaded = NO;
return self;

}




-(IBAction) showPanel: (id) sender
{
if ( nibIsLoaded== NO){
{
nibIsLoaded = [NSBundle loadNibNamed: @"About" owner: self];
[aboutWindow setReleasedWhenClosed:NO];
}
if ( !nibIsLoaded)
NSLog(@"Could not load custom About window");

}

else
if ( nibIsLoaded)

{
NSLog(@"Custom \"About window\" already open");
[aboutWindow makeKeyAndOrderFront:self ];
}
}

-(void) dealloc
{
if (nibIsLoaded == YES)
[aboutWindow release];
[super dealloc];
}

Pommade
Oct 4, 2009, 12:14 PM
First of All, in the Interface Builder :

- uncheck the 'Release When Close' in the Behavior group of Window menu of the Panel Attributes, in the inspector window.

- then in the 'File's Owner' identity, just add 'aboutPanel' in the class outlet table view (click plus button); let the type set to id.

Save your nib file then leave the IB.

Now, let's examin the code. In bold are the piece i added for this challenge.

AppController.h

#import <Cocoa/Cocoa.h>
@class PreferenceController;

@interface AppController : NSObject {
PreferenceController *preferenceController;
IBOutlet NSPanel *aboutPanel;
}
- (IBAction)showPreferencePanel:(id)sender;
- (IBAction)showAboutPanel:(id)sender;
@end


Now let's see the showAboutPanel method :

in AppController.m
#import "AppController.h"
#import "PreferenceController.h"


@implementation AppController

- (IBAction)showPreferencePanel:(id)sender
{...}

- (IBAction)showAboutPanel:(id)sender
{

// if my nib wasn't loaded, my aboutPanel is nil, then that means i
// have to load the nib
if (!aboutPanel) {
BOOL succes = [NSBundle loadNibNamed:@"About" owner:self];

// if it succed, Appcontroller becomes the file's owner and
// aboutPanel is linked (set in the IB previously in the file's
// owner attributes). Otherwise, i get a messsage of failure in
// the console
if (!succes) {
NSLog(@"Failure loading the about panel nib file");
}
} else {
// the other case is that the panel is existing, so we just
// bring it to the front.
[aboutPanel makeKeyAndOrderFront:self];
}
}

@end


If any question...

Darkroom
Oct 20, 2009, 07:46 AM
- (IBAction)showAboutPanelid)sender
{

// if my nib wasn't loaded, my aboutPanel is nil, then that means i
// have to load the nib
if (!aboutPanel) {
BOOL succes = [NSBundle loadNibNamed:@"About" owner:self];

// if it succed, Appcontroller becomes the file's owner and
// aboutPanel is linked (set in the IB previously in the file's
// owner attributes). Otherwise, i get a messsage of failure in
// the console
if (!succes) {
NSLog(@"Failure loading the about panel nib file");
}
} else {
// the other case is that the panel is existing, so we just
// bring it to the front.
[aboutPanel makeKeyAndOrderFront:self];
}
}



if you've unchecked "Release When Closed" in IB, the above code will not display your custom panel again if it is closed after being once opened. it's ideal to only load the XIB once while simply showing it if has been loaded.


- (IBAction)showAboutPanel:(id)sender
{
if (!aboutPanel)
{
NSLog(@"load then display");
[NSBundle loadNibNamed:@"About" owner:self];
[aboutPanel makeKeyAndOrderFront:self];
}
else
{
NSLog(@"only display - has already been loaded");
[aboutPanel makeKeyAndOrderFront:self];
}
}

Pommade
Oct 21, 2009, 03:58 AM
Hi.

I think there's a misunderstanding, Darkroom. When i write :
First of All, in the Interface Builder :

- uncheck the 'Release When Close' in the Behavior group of Window menu of the Panel Attributes, in the inspector window.


it means that i don't want that the panel release itself when it recieving a close message( typically posted when hitting the little red close button on the top left of the panel).

What does it imply? It implies that when the panel is 'closed', its behavior is not anymore : "Free the memory zone it was using";
but now its : "Only hide its view".
It implies too that when the panel is closed, its outlet (translate by the pointer pointing on this object) still pointing on a still used memory zone, and not anymore on a freed memory zony which could contain anything else.


So then let's resume :
- the about panel i 've made only hide itself when the close button is hit.

Next, i did this following step :- then in the 'File's Owner' identity, just add 'aboutPanel' in the class outlet table view (click plus button); let the type set to id.


I hope that you understood that my intention was to tell my panel that its futur owner (wathever it will be) will have a outlet pointing on itself. In this case, the file's owner is going to be AppController, so i only have to declare the outlet in its header (which is one Aaron Hillegass directive for this challenge).


Now let 's run on paper this little piece of code.

i launch the Raisman app, and my controller class is initialized; during this initialisation, the outlet pointing on my about panel point on nil.
It happens what happens, and i click on "show about panel" in my menu. It calls the showPanel:sender method (if i've correctly linked my Menu Item Target).
Let's see what happens:
First of all i test my AppController outlet aboutPanel :if (!aboutPanel). It s the first time i launch this method, so my aboutPanel still pointing on nil, which means (!aboutPanel) = YES, so i go to the statement.
In this statement, i load the nib, and i link to it AppController. BOOL succes = [NSBundle loadNibNamed:@"About" owner:self];

If the loading is a succes, my About Panel is unarchived, shown and the file's owner link its outlet aboutPanel to the About Panel. So then the outlet aboutPanel of AppController points now on the About Panel.

If the nib can't be load if print a message of failure in the console.
if (!succes) {
NSLog(@"Failure loading the about panel nib file");
}

That 's what happens when the showAboutPanel:sender is first called.

At this point, now my About Panel is in on front and the outlet aboutPanel points on it and it's not nil.

Then I decide to close the About Panel. After hit the close buton, the panel is no more visible, but it still residing in memory, according the behavior we impose to it. So then my outlet still pointing on a active memory zone well defined.

Further in time, i decide to it again the menu item "Show about Panel"

Let's see what happens in showAboutPanel:sender method:
I test again my outlet ,if(!aboutPanel)but this time aboutPanel is no more nil, it's value is the address of the About Panel(which still existing in memory), so (!aboutPanel) = NO; so then i go to the else statement and i unhide my About Panel, and put it in front :
[aboutPanel makeKeyAndOrderFront:self];

I close again my About Panel, it will not release my Panel, and my outlet will point again on the well defined piece of memory occupied by the About Panel. And So on, And so on.

Let me correct your piece of code, will you.
In your if statement,
if (!aboutPanel)
{
NSLog(@"load then display");
[NSBundle loadNibNamed:@"About" owner:self];
[aboutPanel makeKeyAndOrderFront:self];
}
(...)

Calling [aboutPanel makeKeyAndOrderFront:self]; is redundant, because when you load your nib, instances are created and when our about panel is created, its default behavior is to be on front of our app (it's acting like a basic panel).

Anyway, i hardly recommand you to try my piece of code. It works on my mini-mac mac os x 10.5.8.
Your piece of code is valid, but i say that it is not well optimized according me.

Sincerely, me.

hubrisForAll
Jan 13, 2011, 02:51 PM
Is the point of using BOOL success = .....

only so that you can test if you successfully loaded the nib file?

As is, my code:


if (!aboutPanel) {
[NSBundle loadNibNamed:@"About" owner:self];
}
else {
[aboutPanel makeKeyAndOrderFront:self];
}


With File's Owner as my AppController (not a window Controller), and aboutPanel an outlet to my panel titled "About."

Also - according to Apple Documentation, an NSPanel's default behavior is to NOT release when sent the message [panel close] - this is however not consistent with the "Release on close" checkbox being on by default in Interface Builder. Odd?