1
votes

I'd like to learn to which class may a NSObject belong (other than NSAlert, NSOpenPanel and NSSavePanel) in order to be responsive to the above call (as in the question title). Here's the scenario of what I need to do. A menu-selected action which has to be modal for self.window requires additional user input in form of a dialog more elaborate than "OK/Cancel" (with the actual work-code omitted from example, for simplicity):

- (IBAction)myActionDialog:(id)sender
{
NSPanel *panel = self.myActionPanel;
[panel beginSheetModalForWindow:self.window completionHandler:^(NSInteger result) {        
    ;        
    if (result == 0){
        ;
    } 
}];
}

However, the compiler warns:

'NSPanel may not respond to beginSheetModalForWindow:completionHandler'

which in fact proves to be true, yet I don't know how to get around this problem and code the desired behavior. I've been looking for proper documentation, but found nothing, so far. Any experienced advice is very welcome. Thanks in advance!

UPDATE:

This question respects the:

[[NSApplication sharedApplication] beginSheet: modalForWindow: modalDelegate: didEndSelector:
                                  contextInfo: ]

method, which works with almost any subclass of NSPanel, AFAICT. The only problem is that Apple, while announcing it getting deprecated, hasn't yet properly documented a substitution method which would replace it universally, AFAIK.

2
See answer here, include link to example on github stackoverflow.com/questions/27541684/… - Daniel Farrell
Thanks @boyfarrell, this is a valuable link, and the example is also clearly written, I've already figured it out myself meanwhile, for the 10.10 SDK. The "dirtier" part of the problem is securing compatibility, by calling Gestalt(gestaltSystemVersionMinor, &versionMinor); in - (void)applicationDidFinishLaunching:(NSNotification *)aNotification, then branching the code through if(versionMinor > ...){...}else{...}. By the way, as of which version minor did the old API get retired for NSPanel (NSWindow) in general? I have 10.6 - 10.10, except for 10.9. Thanks in advance. - user3078414
I recommend that you not bother having two code branches. Twice the code is twice the opportunity for bugs. It also makes it harder to test. If you can't drop support for 10.8 and earlier, just keep using the older API as your only code path. When your drop support for 10.8 and earlier, switch to the new API and remove the old API from your code. If you want to switch, use a test like [NSWindow instancesRespondToSelector:@selector(beginSheet:completionHandler:)] instead of the gestalt check. Other better approaches include comparing NSAppKitVersionNumber; see AppKit release notes. - Ken Thomases
Thanks for your concern @Ken Thomases. I'm well aware of the risks being mentioned. I've worked out a simple "branching pattern" which so far gives no bugs: sheets now open properly in 10.7.-10.10 and completion operations perform as desired. I don't want to force-persuade people into upgrading to newer OSes for my own being lazy in solving compatibility properly, or post separate OS-dependent releases for as long as I can manage solving it programmatically. Yet, I didn't quite get: which was the last OSX allowing for old "modal delegate"-based sheet code: 10.8 or 10.9? - user3078414
There is no last OS X allowing it. It's still allowed. "Deprecated" does not mean "disallowed". That was my point. The old methods, like -[NSApplication beginSheet:modalForWindow:modalDelegate:didEndSelector:contextInfo:], were officially deprecated in the 10.10 SDK. Just look up the method in the documentation and it tells you. - Ken Thomases

2 Answers

2
votes

Apple have documented the replacement for -[NSApplication beginSheet:...]. It's new methods on NSWindow:

-beginSheet:completionHandler:
-beginCriticalSheet:completionHandler:
-endSheet:
-endSheet:returnCode:

In your case, you'd do:

[self.window beginSheet:panel completionHandler:^(NSInteger result) {
    if (result == 0){
        ;
    }
}];
2
votes

This is my answer to the question, following individual research on the subject. I find a major issue in carefully implementing custom sheets being a rather sudden and silent transition between deprecation and disfunction of the old modal delegate based code, which must have happened sometime during 10.8. It causes a critical compatibility problem requiring a simple code branching and refraining from over-implementing new API enums, unless really needed for a reason. I understand such a concept can be disagreed with, but here's an imlementation. I'm using the Gestalt() command because it works with all systems between 10.7 and 10.10. Being aware there are newer, better and more sophisticated methods for determining OS runtime, here's the link with more relevant info: https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKit/

I'm posting this for simplicity, having the header declaration and interface builder details omitted. Hope it's clear, though:

SInt32 versionMinor;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
versionMinor = 0;
Gestalt(gestaltSystemVersionMinor, &versionMinor);
NSLog(@"Version Minor: %d\n", (int)versionMinor);
}

The sheet posting method:

- (IBAction)postSheet:(id)sender
{
if(versionMinor > 8){
    [self.window beginSheet:self.sheet completionHandler:^(NSInteger result) {
        if (result == 1) [self doSomething];
        if (result == 0) [self doNothing];
    }];
}else [[NSApplication sharedApplication] beginSheet:self.sheet
                                   modalForWindow:self.window
                                    modalDelegate:self
                                   didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:)
                                      contextInfo:nil
     ];
}

OK button pressed IBAction:

- (IBAction)ok:(id)sender
{
[self.sheet orderOut:self];
if(versionMinor > 8){NSLog(@">OK Button pressed\n");
    [self.window endSheet:self.sheet returnCode:1];
}else{ NSLog(@"<OK Button pressed\n");
    [NSApp endSheet:self.sheet returnCode: 1];
    }
}

Likewise, for the Cancel button pressed IBAction:

- (IBAction)cancel:(id)sender
{
[self.sheet orderOut:self];
if(versionMinor > 8){ NSLog(@">Cancel Button pressed\n");
    [self.window endSheet:self.sheet returnCode:0];
}else{  NSLog(@"<Cancel Button pressed\n");
    [NSApp endSheet:self.sheet returnCode: 0];
}
}

Finally, the "legacy" didEndSelector (for pre-10.8. runtime)

- (void)sheetDidEnd : (NSPanel*)panel returnCode:(NSInteger)returnCode contextInfo:(void*)contextInfo
{
if (returnCode == 1) [self doSomething ];
if (returnCode == 0) [self doNothing ];
}

This works for 10.7 through 10.10, built with either 10.8 or 10.10 SDKs. In the academic and scientific communities OSX 10.7 and 10.8 are still being used quite a bit. Hope someone can find this helpful. Thanks for the contributions and comments.