Search code examples
objective-ccocoansdocument

Displaying a progress indicator sheet on NSDocument shows up greyed out (though still serves purpose)


First Try - beginSheet: completionHandler: Method

I have a document-based application that does moderately intense saving (approximately 10-15 seconds, but definitely noticeable). In order for the end-user to not think that the app is hung up, I've added a progress indicator that is displayed as a sheet over the document. I'm able to display my progress indicator as a sheet while the document is being saved, and the sheet properly disappears when the save is over. However, the indicator is greyed out. I know this is more of an aesthetic problem but would appreciate pointers on how to remedy this.

Below is a screenshot of the progress indicator. Instead of the blue and animated bar, it is greyed out and still.

enter image description here

I've listed the relevant code below.

Code to display progress indicator:

- (void) showProgressIndicatorSheet
{
    NSStoryboard *storyboard = [NSStoryboard storyboardWithName:@"Main" bundle:nil];
    modalProgressWindowController = [storyboard instantiateControllerWithIdentifier:@"modalProgressWindowController"];

    NSArray *windowControllers = self.windowControllers;
    if ([windowControllers count] > 0) {
        NSWindowController *controller = windowControllers[0];
        [controller.window beginSheet:modalProgressWindowController.window completionHandler:nil];
    }
}

Code to hide sheet:

- (void) hideProgressIndicatorSheet
{
    if (modalProgressWindowController) {
        NSArray *windowControllers = self.windowControllers;
        if ([windowControllers count] > 0) {
            NSWindowController *controller = windowControllers[0];
            [controller.window endSheet:modalProgressWindowController.window];
        }
    }
}

Code that display the indicator then hides it while saving:

- (BOOL)writeToURL:(NSURL *)url ofType:(NSString *)typeName error:(NSError * _Nullable __autoreleasing *)outError {
    [self showProgressIndicatorSheet];

    /* code to save data to file */

    [self hideProgressIndicatorSheet];
}

Second Try - beginModalSessionForWindow: Method

As was alluded to by a comment that maybe using a window sheet is the issue here. I did some searching around and found beginModalSessionForWindow and it's documentation. Looked promising so I tried using it but have the same problem where the progress bar is greyed out. I also have a new problem where I cannot stop the modal despite calling [NSApp stopModal].

Code to display progress indicator:

- (void) showProgressIndicatorSheet
{
    NSStoryboard *storyboard = [NSStoryboard storyboardWithName:@"Main" bundle:nil];
    modalProgressWindowController = [storyboard instantiateControllerWithIdentifier:@"modalProgressWindowController"];

    session = [NSApp beginModalSessionForWindow:modalProgressWindowController.window];
}

Code to dismiss modal:

- (void) hideProgressIndicatorSheet
{
    [NSApp endModalSession:session];
}

Code that display the indicator then dismisses it while saving:

- (BOOL)writeToURL:(NSURL *)url ofType:(NSString *)typeName error:(NSError * _Nullable __autoreleasing *)outError {
    [self showProgressIndicatorSheet];

    BOOL response = NO;
    BOOL beganSave = NO;

    while ([NSApp runModalSession:session] == NSModalResponseContinue) {
        if (!beganSave) {
            beganSave = YES;
            response = [self saveToDBForURL:url];
        }
    }

    [self hideProgressIndicatorSheet];

    return response;
}

stopModal inside of saveToDBForURL:

- (BOOL)saveTODBForURL: (NSURL *) url {

    /* save method */

    // stop modal after saving is done
    [NSApp stopModal];

    // return whether save was success or not...
    return response;
}

Solution

  • Solved! Willeke is correct about invoking all the methods in the comment. But that alone was not enough...I was still getting the greyed out progress bar. I stumbled on the following post on SO "Document sheet not responding to keyboard events". Turns out, you must have "title bar" enabled in IB!

    With the title bar enabled in IB, here is the updated code that works (hopefully no one else will need to run into this weirdness!):

    Code to display sheet

    - (void) showProgressIndicatorSheet
    {
        NSArray *windowControllers = self.windowControllers;
        if ([windowControllers count] > 0) {
            NSWindowController *controller = windowControllers[0];
            ((BWProgressIndicatorSheetViewController *)modalProgressWindowController.window.contentViewController).progressLabel.stringValue = @"Saving workspace...";
            [controller.window beginSheet:modalProgressWindowController.window completionHandler:nil];
            session = [NSApp beginModalSessionForWindow:modalProgressWindowController.window];
        }
    }
    

    Code to dismiss sheet

    - (void) hideProgressIndicatorSheet
    {
        [NSApp endModalSession:session];
        if (modalProgressWindowController) {
            NSArray *windowControllers = self.windowControllers;
            if ([windowControllers count] > 0) {
                NSWindowController *controller = windowControllers[0];
                [controller.window endSheet:modalProgressWindowController.window];
            }
        }
    }
    

    Code that display the indicator then dismisses it while saving

    - (BOOL)writeToURL:(NSURL *)url ofType:(NSString *)typeName error:(NSError * _Nullable __autoreleasing *)outError {
    
        [self showProgressIndicatorSheet];
    
        BOOL response = NO;
        BOOL beganSave = NO;
    
        while ([NSApp runModalSession:session] == NSModalResponseContinue) {
            if (!beganSave) {
                beganSave = YES;
                response = [self saveToDBForURL:url];
            }
        }
    
        [self hideProgressIndicatorSheet];
    
        return response;
    }
    

    And lastly...stopModal inside of saveToDBForURL:

    - (BOOL) saveToDBForURL: (NSURL *) url
    {
        // do saving here
    
        [NSApp stopModal];
    
        return response;
    }