Alert Sheets hard wired in Interface Builder

  • Instead of calling NSAlertSheet with sender, defaultButton etc., is there a
    way to call a window sheet whose GUI is hard wired in Interface Builder.  I
    know that in Applescript Studio, all you have to do is provide a name for
    the sheet you wish to call and it appears.  Can the same implementation
    scheme exist in Cocoa/XCODE?

    Thanks in advance,

    John Love
  • On May 27, 2008, at 4:28 PM, John Love wrote:

    > Instead of calling NSAlertSheet with sender, defaultButton etc., is
    > there a
    > way to call a window sheet whose GUI is hard wired in Interface
    > Builder.

    Sure - pretty much any window you can build in IB, can be used as a
    sheet.

    Have a look at:

        <http://developer.apple.com/documentation/Cocoa/Conceptual/
    Sheets/Tasks/UsingCustomSheets.html
    >

    sherm--
  • On Tue, May 27, 2008 at 4:28 PM, John Love <jolove+<cocoa...> wrote:
    > Instead of calling NSAlertSheet with sender, defaultButton etc., is there a
    > way to call a window sheet whose GUI is hard wired in Interface Builder.  I
    > know that in Applescript Studio, all you have to do is provide a name for
    > the sheet you wish to call and it appears.  Can the same implementation
    > scheme exist in Cocoa/XCODE?

    What is this Ecks See Oh Dee Eee you refer to?  Is it related to Xcode?  :P

    You're looking for +[NSApplication
    beginSheet:modalForWindow:modalDelegate:didEndSelector:contextInfo:],
    which is appropriately described in the "Custom Sheets" section of the
    "Sheet Programming Topics for Cocoa" guide:
    http://developer.apple.com/documentation/Cocoa/Conceptual/Sheets/Tasks/Usin
    gCustomSheets.html#//apple_ref/doc/uid/20001290


    --Kyle Sluder
  • On 27 May '08, at 1:40 PM, Kyle Sluder wrote:

    > You're looking for +[NSApplication
    > beginSheet:modalForWindow:modalDelegate:didEndSelector:contextInfo:],

    I'm not surprised the OP didn't find this … it's my candidate for Most
    Misplaced Cocoa Method. I've always wondered why this isn't an
    instance method on NSWindow, to be sent either to the sheet window
    - (void)
    beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo:
    or to the host window
    - (void) beginSheet:modalDelegate:didEndSelector:contextInfo:

    This is an instance [sic] of an OOP design error wherein a particular
    class method that takes an object as a parameter should be changed
    into an instance method on that object.

    —Jens
  • I agree with your assessment. What business has NSApplication
    providing this method? it doesn't make sense.

    Easy to fix with a category though.

    G.

    On 28 May 2008, at 2:06 pm, Jens Alfke wrote:

    >
    > On 27 May '08, at 1:40 PM, Kyle Sluder wrote:
    >
    >> You're looking for +[NSApplication
    >> beginSheet:modalForWindow:modalDelegate:didEndSelector:contextInfo:],
    >
    > I'm not surprised the OP didn't find this … it's my candidate for
    > Most Misplaced Cocoa Method. I've always wondered why this isn't an
    > instance method on NSWindow, to be sent either to the sheet window
    > - (void)
    > beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo:
    > or to the host window
    > - (void) beginSheet:modalDelegate:didEndSelector:contextInfo:
    >
    > This is an instance [sic] of an OOP design error wherein a
    > particular class method that takes an object as a parameter should
    > be changed into an instance method on that object.
    >
    > —Jens
  • > I agree with your assessment. What business has NSApplication
    > providing this method? it doesn't make sense.

    Probably because it involves the run loop and event dispatch, since the
    running a window as a sheet must ensure that the window to which the sheet
    is attached does not receive events, yet a click in that parent window must
    bring the pair forward... Further, I'll bet it's also there because the
    method to run a window application-modally was there first. So if you think
    about what the method must *really* do, it no longer appears to be so bound
    to the window, because it requires doing things that are clearly not the
    responsibility of the window. Yes, the method could be in NSWindow and call
    on NSApplication to get that done, but, should a window really be able to
    have such a drastic effect on the events going to other windows???

    But I had the same difficulty as everybody else locating it the first time.
    And then again, later, when I had not used it for a while ;-)

    --
    Scott Ribe
    <scott_ribe...>
    http://www.killerbytes.com/
    (303) 722-0567 voice
  • On Wed, May 28, 2008 at 11:29 PM, Scott Ribe <scott_ribe...> wrote:
    > Probably because it involves the run loop and event dispatch, since the
    > running a window as a sheet must ensure that the window to which the sheet
    > is attached does not receive events, yet a click in that parent window must
    > bring the pair forward...

    Hrm.  I thought that at first but then I came to the conclusion that
    this argument doesn't really hold water.  Plenty of operations on
    view-level code are going to affect the run loop mode (-[NSResponder
    presentError:], for example), and event dispatch is only relevant to
    the NSWindow to which the sheet is attached anyway.

    I can live with "it was put in the wrong place", and Jens makes a
    point that this seems like a mechanical transformation is possible,
    which is usually further evidence that something's wrong.
    -[SomeSingleton makeThisObject:doThis:] instead of -[ThisObjectClass
    doThis:] seems to actually fit the definition of the anti-pattern.  Oh
    well, it's not *broken*, and that's the important part.

    --Kyle Sluder
  • On 28 May '08, at 8:29 PM, Scott Ribe wrote:

    > Yes, the method could be in NSWindow and call
    > on NSApplication to get that done, but, should a window really be
    > able to
    > have such a drastic effect on the events going to other windows???

    It doesn't. A sheet doesn't change anything in the app's overall
    state, since it's asynchronous. The event loop runs normally and
    dispatches events normally. The most significant change is that the
    host window ignores most events (and just beeps) while it has a sheet
    open, but the app itself shouldn't care about that.

    In any case, regardless of where the implementation of the
    functionality lives, the API should be designed in a way that makes
    sense conceptually to clients. Even if it was NSApplication or
    NSRunLoop that had to do most of the work of opening a sheet, it would
    be a simple matter for a public NSWindow method to call a private
    implementation method elsewhere. There are a number of other cases of
    that. (For example, -[NSDictionary initWithContentsOfFile:] is
    probably just a wrapper around NSPropertyListSerialization.)

    —Jens
  • Here's a trivial category to address this. Whether it's really worth
    using is hard to say, but for what it's worth:

    @interface NSWindow (SheetAdditions)

    - (void)    beginSheet:(NSWindow*) sheet modalDelegate:(id) modalDelegate
    didEndSelector:(SEL) didEndSelector contextInfo:(void*) contextInfo;
    - (void)    beginSheetModalForWindow:(NSWindow*) docWindow modalDelegate:
    (id) modalDelegate didEndSelector:(SEL) didEndSelector contextInfo:
    (void*) contextInfo;

    @end

    @implementation NSWindow (SheetAdditions)

    - (void)    beginSheet:(NSWindow*) sheet modalDelegate:(id) modalDelegate
    didEndSelector:(SEL) didEndSelector contextInfo:(void*) contextInfo
    {
    [NSApp beginSheet:sheet modalForWindow:self
    modalDelegate:modalDelegate didEndSelector:didEndSelector
    contextInfo:contextInfo];
    }

    - (void)    beginSheetModalForWindow:(NSWindow*) docWindow modalDelegate:
    (id) modalDelegate didEndSelector:(SEL) didEndSelector contextInfo:
    (void*) contextInfo;
    {
    [NSApp beginSheet:self modalForWindow:docWindow
    modalDelegate:modalDelegate didEndSelector:didEndSelector
    contextInfo:contextInfo];
    }

    @end

    On 29 May 2008, at 1:29 pm, Scott Ribe wrote:

    >> I agree with your assessment. What business has NSApplication
    >> providing this method? it doesn't make sense.
    >
    > Probably because it involves the run loop and event dispatch, since
    > the
    > running a window as a sheet must ensure that the window to which the
    > sheet
    > is attached does not receive events, yet a click in that parent
    > window must
    > bring the pair forward... Further, I'll bet it's also there because
    > the
    > method to run a window application-modally was there first. So if
    > you think
    > about what the method must *really* do, it no longer appears to be
    > so bound
    > to the window, because it requires doing things that are clearly not
    > the
    > responsibility of the window. Yes, the method could be in NSWindow
    > and call
    > on NSApplication to get that done, but, should a window really be
    > able to
    > have such a drastic effect on the events going to other windows???
    >
    > But I had the same difficulty as everybody else locating it the
    > first time.
    > And then again, later, when I had not used it for a while ;-)
    >
    > --
    > Scott Ribe
    > <scott_ribe...>
    > http://www.killerbytes.com/
    > (303) 722-0567 voice
    >
    >
  • On May 27, 2008, at 9:06 PM, Jens Alfke wrote:

    > On 27 May '08, at 1:40 PM, Kyle Sluder wrote:
    >
    >> You're looking for +[NSApplication
    >> beginSheet:modalForWindow:modalDelegate:didEndSelector:contextInfo:],
    >
    > I'm not surprised the OP didn't find this … it's my candidate for
    > Most Misplaced Cocoa Method.

    I suspect the general sheet methods (which are instance methods, not
    class methods) are on NSApplication for largely historical reasons.
    If you look at <AppKit/NSApplication.h>, you'll see that NSApplication
    has a couple of instance methods dealing with running a modal session
    "relative to" a window:

    - (NSInteger)runModalForWindow:(NSWindow *)theWindow relativeToWindow:
    (NSWindow *)docWindow;
    - (NSModalSession)beginModalSessionForWindow:(NSWindow *)theWindow
    relativeToWindow:(NSWindow *)docWindow;

    These are effectively the OpenStep predecessor to sheets.  They're
    preceded by comments indicating that they're deprecated, but are not
    formally tagged as such in the headers.

    There are also similar -- and similarly-deprecated -- functions in
    <AppKit/NSPanel.h> for running alert panels relative to a window.

    In both cases, sheets are called out as the replacement technology,
    and the replacements are defined practically right next to the
    deprecated functionality.

      -- Chris
  • On Fri, May 30, 2008 at 3:33 PM, John Love
    <jolove+<cocoa...><jolove%<2Bcocoa...>>
    wrote:

    > Here's some code snippets that I'm having problems with .. the code is
    > sorta working in that an alert *window* appears, but not as a drop-down
    > sheet as I think the following calls for: (and even when erroneously a
    > window, it is behind the main document window)
    >
    > Two controllers .. FileController and SheetController ..
    >
    > 1) FileController.h contains
    > IBOutlet SheetController *theSheet;  .. and ..  NSWindow
    > *itsWindow; (FileController's itsWindow gets set within MyDocument.m which
    > passes its IBOutlet NSWindow *documentWindow to a method of
    > FileController)  As a result, MyDocument's documentWindow becomes
    > FileController's itsWindow
    >
    > 2) FileController.m contains
    > [theSheet showCalculateSheet:itsWindow];
    >
    > 3) SheetController.h contains
    > - (void) showCalculateSheet:(NSWindow*)docWindow;
    >
    > 4) SheetController.m contains
    >
    > - (void) showCalculateSheet:(NSWindow*)docWindow {
    > NSAlert *calculateSheet = [[[NSAlert alloc] init] autorelease];
    >
    > [calculateSheet addButtonWithTitle:@"Continue"];
    > [calculateSheet addButtonWithTitle:@"Stop and save"];
    > [calculateSheet addButtonWithTitle:@"Stop and don't save"];
    > [calculateSheet setMessageText:@"You have not finished calculating
    > your Spreadsheet.\n"
    > "Do you wish to continue
    > calculating?"];
    > [calculateSheet setAlertStyle:NSWarningAlertStyle];
    >
    > [calculateSheet beginSheetModalForWindow:docWindow modalDelegate:nil
    > didEndSelector:nil  //
    > @selector(endCalculateSheet:code:info:)
    > contextInfo:nil];
    > }
    >
    > // *if* I use a real selector method, and not nil, I cannot dismiss the
    > alert
    >
    > - (void) endCalculateSheet:(NSWindow*)theSheet code:(int)returnCode
    > info:(void*)contextInfo {
    >
    > if (returnCode == NSAlertDefaultReturn)          { // "Continue"
    >
    > } else if (returnCode == NSAlertOtherReturn)    { // "Stop and save"
    >
    > } else if (returnCode == NSAlertAlternateReturn) { // "Stop and don't
    > save"
    >
    > }
    >
    > [theSheet orderOut:self];
    >
    > }
    >
    > Thanks for everything in advance ..
    >
    > John Love
    >
    >
    >
    Jens wrote:

    My guess is that docWindow is nil. Set a breakpoint and check.

    But follow-ups should go to the list.

    John wrote back

        if (docWindow != nil)  NSLog(@"passed window is not nil");

    produced the log string.

    Touch the Future! Teach!!
  • More NSLog data:

    2008-05-31 12:33:51.001 Calculate Medical[1926:10b] NSWindow does not
    support utility styleMask 0x10
    2008-05-31 12:33:51.023 Calculate Medical[1926:10b] NSWindow does not
    support utility styleMask 0x10

    2008-05-31 12:33:55.434 Calculate Medical[1926:10b] *** -[NSAlert
    endErrorSheet:code:info:]: unrecognized selector sent to instance 0x16ed20
    2008-05-31 12:33:55.438 Calculate Medical[1926:10b] *** -[NSAlert
    endErrorSheet:code:info:]: unrecognized selector sent to instance 0x16ed20

    I put back:

    didEndSelector:@selector(endCalculateSheet:code:info:) into:

    [calculateSheet beginSheetModalForWindow:docWindow modalDelegate:nil
                      didEndSelector:@selector(endCalculateSheet:code:info:)
                      contextInfo:docWindow];

    and here's the selector method:

    - (void) endCalculateSheet:(NSWindow*)theSheet code:(int)returnCode
    info:(void*)contextInfo {
        if (returnCode == NSAlertDefaultReturn) { // "Good Bye!"

            NSLog(@"Default Button clicked");    // does not log this entry

        }

        [theSheet close];
    }

    John Love
  • 5/31/08 10:40 AM, also sprach jolove+<cocoa...>:

    > - (void) endCalculateSheet:(NSWindow*)theSheet code:(int)returnCode
    > info:(void*)contextInfo {
    > if (returnCode == NSAlertDefaultReturn) { // "Good Bye!"
    >
    > NSLog(@"Default Button clicked");    // does not log this entry
    >
    > }
    >
    > [theSheet close];
    > }

    If you didn't create the sheet with
    +alertWithMessageText:defaultButton:alternateButton:otherButton:informativeT
    extWithFormat:, then you would not get that constant. Read the NSAlert API
    reference, especially -addButtonWithTitle:

    HTH,

    Keary Suska
    Esoteritech, Inc.
    "Demystifying technology for your home or business"
  • On 31 May '08, at 9:40 AM, John Love wrote:

    > 2008-05-31 12:33:55.434 Calculate Medical[1926:10b] *** -[NSAlert
    > endErrorSheet:code:info:]: unrecognized selector sent to instance
    > 0x16ed20

    That's an exception being thrown. There's no such (public) Cocoa
    method -endErrorSheet:code:info:, so is this a method you defined, or
    are passing as a selector? It doesn't appear in the code you posted,
    but is it elsewhere in your app?

    —Jens
  • 1) I found the culprit on the sheet appearing as a separate window, and not
    a sheet .. you were right cause somehow the passed docWindow was apparently
    nil .. anyway, called my sheet routine from a different part of MyDocument.m
    .. and now the sheet appears as a sheet.

    2) the problem remaining centers on accessing the various buttons on the
    alert sheet

    ===========
    Here is the log result:

    [Session started at 2008-06-01 14:05:16 -0400.]
    2008-06-01 14:05:18.088 Calculate Medical[2267:10b] NSWindow does not
    support utility styleMask 0x10
    2008-06-01 14:05:18.097 Calculate Medical[2267:10b] NSWindow does not
    support utility styleMask 0x10
    2008-06-01 14:05:22.738 Calculate Medical[2267:10b] *** -[NSAlert close]:
    unrecognized selector sent to instance 0x1b5520
    2008-06-01 14:05:22.769 Calculate Medical[2267:10b] *** -[NSAlert close]:
    unrecognized selector sent to instance 0x1b5520

    The Debugger has exited with status 0.
    ===========
    Do not understand that utility styleMask stuff .. and .. the NSAlert is
    type-cast as a NSWindow when passed to my dlgEndSelector, so that
    "unrecognized selector" is mystifying.

    Anyway, onto the code ..

    I call this method from MyDocument.m and pass the *documentWindow IBOutlet
    from MyDocument.h

    - (void) showCalculateSheet:(NSWindow*)docWindow {
        NSAlert *calculateSheet = [[[NSAlert alloc] init] autorelease];

        [calculateSheet addButtonWithTitle:@"Continue"];
        [calculateSheet addButtonWithTitle:@"Stop and save"];
        [calculateSheet addButtonWithTitle:@"Stop and don't save"];
        [calculateSheet setMessageText:@"You have not finished calculating your
    Spreadsheet.\n"
                                        "Do you wish to continue calculating?"];
        [calculateSheet setAlertStyle:NSWarningAlertStyle];

        [calculateSheet beginSheetModalForWindow:docWindow modalDelegate:self
                          didEndSelector:@selector(endCalculateSheet:code:info:)
                          contextInfo:docWindow];
    }

    - (void) endCalculateSheet:(NSWindow*)theSheet code:(int)returnCode
    info:(void*)contextInfo {
        if (returnCode == NSAlertDefaultReturn)          // "Continue"
        {
            NSLog(@"Default Calculate Button clicked");
          }
        else if (returnCode == NSAlertOtherReturn)      // "Stop and save"
        {
            NSLog(@"Other Calculate Button clicked");
        }
        else if (returnCode == NSAlertAlternateReturn)  // "Stop and don't
    save"
        {
            NSLog(@"Third Calculate Button clicked");
        }

        [theSheet close];  // have also tried ... [theSheet orderOut:self];
    }

    Someone a long time ago warned that the learning curve slope for XCODE is
    rather steep .. it's not like I wasn't told.

    Anyway, thanks bunches as usual

    John Love
  • On 1 Jun '08, at 11:19 AM, John Love wrote:

    > the NSAlert is
    > type-cast as a NSWindow when passed to my dlgEndSelector, so that
    > "unrecognized selector" is mystifying.

    That's your problem, then: NSAlert isn't an NSWindow; it's a direct
    subclass of NSObject. Your -endCalculateSheet: method's first
    parameter should be (NSAlert*), not (NSWindow*). NSAlert.h says:

    // ... The didEndSelector should have the following signature:
    //- (void)alertDidEnd:(NSAlert *)alert returnCode:
    (NSInteger)returnCode contextInfo:(void *)contextInfo;

    This is a bit inconsistent of them since other "didEnd" methods take
    the sheet's NSPanel* as their first argument. I think they did it
    because there's no obvious way to take an NSPanel* and find the
    NSAlert that owns it.

    —Jens
  • On Jun 1, 2008, at 12:19 PM, John Love wrote:

    > 1) I found the culprit on the sheet appearing as a separate window,
    > and not
    > a sheet .. you were right cause somehow the passed docWindow was
    > apparently
    > nil .. anyway, called my sheet routine from a different part of
    > MyDocument.m
    > .. and now the sheet appears as a sheet.
    >
    > 2) the problem remaining centers on accessing the various buttons on
    > the
    > alert sheet

    You have several issues here:

    First, you can't cast the NSAlert to an NSWindow. The
    endCalculateSheet:code:info: method must have the same types as the
    prototype.

    Second, the returnCodes for the buttons will be
    NSAlertFirstButtonReturn, NSAlertSecondButtonReturn and
    NSAlertThirdButtonReturn. The ones you used are only valid if you call
    alertWithMessageText:defaultButton:alternateButton:otherButton:informativeTextWithFormat
    :.

    Third, you don't need to close or orderOut the NSAlert because it will
    automatically close when your endCalculateSheet:code:info: method
    finishes.

    See below:

    - (void) showCalculateSheet:(NSWindow*)docWindow
    {
    NSAlert *calculateSheet = [[[NSAlert alloc] init] autorelease];

    [calculateSheet addButtonWithTitle:@"Continue"];
    [calculateSheet addButtonWithTitle:@"Stop and save"];
    [calculateSheet addButtonWithTitle:@"Stop and don't save"];
    [calculateSheet setMessageText:@"Do you wish to continue
    calculating?"];
    [calculateSheet setInformativeText:@"You have not finished
    calculating your Spreadsheet."];
    [calculateSheet setAlertStyle:NSWarningAlertStyle];

    [calculateSheet beginSheetModalForWindow:docWindow
                                 modalDelegate:self
                                 didEndSelector:@selector(endCalculateSheet:code:info:)
                                    contextInfo:docWindow];
    }

    - (void) endCalculateSheet:(NSAlert*)theSheet code:(int)returnCode
    info:(void*)contextInfo
    {
    if (returnCode == NSAlertFirstButtonReturn)          // "Continue"
    {
      NSLog(@"Continue button clicked");
    }
    else if (returnCode == NSAlertSecondButtonReturn)      // "Stop and
    save"
    {
      NSLog(@"Stop and save button clicked");
    }
    else if (returnCode == NSAlertThirdButtonReturn)  // "Stop and don't
    save"
    {
      NSLog(@"Stop and don't save button clicked");
    }
    }

    --Nathan
  • Thank you everyone ... I went back to Apple Docs on Sheets and found
    everything you stated ... I apologize for not being a careful reader.

    John Love
previous month may 2008 next month
MTWTFSS
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
Go to today