1

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;
}
Kenneth P. Hough
  • 577
  • 2
  • 8
  • 25
  • I think that's because you are using a window sheet. – El Tomato Dec 31 '17 at 13:51
  • @ElTomato I think you're right. I found `[NSApp runModalSessionForWindow:(NSWindow *)window]` which looks promising. But I need to figure out how to get the save to happen while the modal occurs. Will do more research in a bit. https://developer.apple.com/documentation/appkit/nsapplication/1428590-runmodalsession?language=objc – Kenneth P. Hough Dec 31 '17 at 18:44
  • Update: So I tried `runModalSessionForWindow` and followed Apple's documentations but now have two problems: (1) the same issue where the progress bar is greyed out, and (2) I can't seem to get the modal to disappear after saving...it lingers despite calling `[NSApp stopModal]` and breaking from loop `if ([NSApp runModalSession:session] != NSModalResponseContinue])`. I feel like I'm missing something straight forward. – Kenneth P. Hough Dec 31 '17 at 19:01
  • 1
    Try `beginSheet`, `beginModalSessionForWindow`, `runModalSession`, save, `endModalSession`, `endSheet`. – Willeke Jan 02 '18 at 13:56
  • @Willeke, thanks for the suggestion but I still get a greyed out progress bar. I'm starting to wonder if I'm invoking the code to start a sheet in the wrong place... I've noticed if I run a sheet from the VC then I get a properly animated progress window. It's only when I call it from my Document class. – Kenneth P. Hough Jan 04 '18 at 06:49

1 Answers1

2

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;
}
Kenneth P. Hough
  • 577
  • 2
  • 8
  • 25
  • Thanks! I had turned off the title bar in IB so my sheet would appear like a sheet, but then I couldn't figure out why I wasn't able to interact with it. I could still click on the parent window behind the sheet (an NSDocument) but nothing on the sheet was working. – Fls'Zen Jan 26 '20 at 16:40