4

My objective: Display a custom sheet with a determinate NSProgressIndicator while the app works through a lengthy loop. I want the sheet to be application-modal, not document-modal. The user cannot dismiss the modal sheet. They must wait for the app to finish processing the loop.

Problem: I can't get the custom sheet to attach to the window. It appears as a separate window lacking the window title bar (as a sheet should). Additionally, the sheet is not released (does not close) when the loop finishes.

I have 2 separate nib files for the sheet and main application window, and also 2 controller classes for each window.

Here's the pertinent information: Controller implementation for the custom sheet:

@implementation ProgressSheetController //subclass of NSPanel

-(void)showProgressSheet:(NSWindow *)window
{
    //progressPanel is an IBOutlet to the NSPanel
    if(!progressPanel)
        [NSBundle loadNibNamed:@"ProgressPanel" owner:self];

    [NSApp beginSheet: progressPanel
       modalForWindow: window
        modalDelegate: nil
       didEndSelector: nil
          contextInfo: nil];

    //modalSession is an instance variable
    modalSession = [NSApp beginModalSessionForWindow:progressPanel];

    [NSApp runModalSession:modalSession];
}

-(void)removeProgressSheet
{
    [NSApp endModalSession:modalSession];
    [NSApp endSheet:progressPanel];
    [progressPanel orderOut:nil];
}

//some other methods 
@end

Implementation for the main application window. testFiles method is an IBAction connected to a button.

@implementation MainWindowViewController //subclass of NSObject

-(IBAction)testFiles:(id)sender;
{
    //filesToTest is a mutable array instance variable
    int count = [filesToTest count];

    float progressIncrement = 100.0 / count;

    ProgressSheetController *modalProgressSheet = [[ProgressSheetController alloc] init];
    [modalProgressSheet showProgressSheet:[NSApp mainWindow]];

    int i,
    for(i=0; i<count; i++)
    {
        //do some stuff with the object at index i in the filesToTest array

        //this method I didn't show in ProgressSheetController.m but I think it's self explanatory
        [modalProgressSheet incrementProgressBarBy:progressIncrement];
    }
    //tear down the custom progress sheet
    [modalProgressSheet removeProgressSheet];
    [modalProgressSheet release];
}
@end

One thought: Am I subclassing correctly? Should I use NSWindowController instead? Thank you in advance for your help!

Anoop Vaidya
  • 46,283
  • 15
  • 111
  • 140
David Nix
  • 3,324
  • 3
  • 32
  • 51

2 Answers2

15

Found this gem doing some googling. The behavior for my NSPanel in Interface Builder was set to "Visible at Launch" which is the default for new windows when using IB. What happened was as soon as the nib was loaded, the window was made visible BEFORE [NSApp beginSheet:...]. Therefore, unchecking the "Visible at Launch" option solved my problem and now a sheet appears connected to the window like I want.

David Nix
  • 3,324
  • 3
  • 32
  • 51
1
@implementation ProgressSheetController //subclass of NSPanel

-(void)showProgressSheet:(NSWindow *)window
{
    //progressPanel is an IBOutlet to the NSPanel
    if(!progressPanel)
        [NSBundle loadNibNamed:@"ProgressPanel" owner:self];

So your NSPanel owns another NSPanel? If the second one is the progress panel, what does the first one show?

Should I [subclass] NSWindowController instead?

Sounds like it.

modalSession = [NSApp beginModalSessionForWindow:progressPanel];

Why?

[modalProgressSheet showProgressSheet:[NSApp mainWindow]];

Have you verified that there is a main window? mainWindow does not return the window you care most about; it returns the active window (which is probably, but not necessarily, also key).

If mainWindow is returning nil, that could be the cause of your problem.

int i,
for(i=0; i<count; i++)
{
    //do some stuff with the object at index i in the filesToTest array

First, int is the wrong type. You should use NSUInteger for NSArray indexes.

Second, unless you need the index for something else, you should use fast enumeration.

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
  • Peter, thanks for the fast enumeration tip. I replaced the "for" statement with "for (NSURL *fileURL in filesToTest)" because that's what the array contains. "[NSApp mainWindow]" is not nil. I sprinkled the code with NSLogs to find out. It's passing the correct window. I'm not sure if I'm using modal sessions correctly. "beginModalSessionforWindow:" returns NSModalSession. I made it an ivar so that my "removeProgressSheet:" method can end the modal session. But, even if I remove the modal session code, I still have the same problem. Hmmm... – David Nix Nov 02 '10 at 16:27