macdevcenter.com
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Working with Sheets in Cocoa
Pages: 1, 2, 3

What then is this method that we're talking about? It can be anything you want it to be. However, if you want to obtain information about the sheet such as which button was pressed, or which sheet is invoking the method (if you had many sheets in your application), then you need to use the method sheetDidEnd:returnCode:contextInfo: (herein referred to as sheetDidEnd...). We'll talk more about this method in a minute when we actually implement this function.



This brings us to argument 9, contextInfo. The data type void * is simply that of a generic pointer that provides a convenient and flexible way of passing information between the method where NSBeginAlertSheet is called and the method sheetDidEnd...

With that, let's see how we can convert our alert panel into an alert sheet using the new function. Recall that the structure of our previous implementation of deleteRecord both ran the alert panel and handled code execution based on the return value of NSRunAlertButton. But we've already said that NSBeginAlertSheet doesn't work this way, so we will have to split up our old deleteRecord into a new deleteRecord and the method sheetDidEnd.... We will move all code specific to executing the record deletion out of deleteRecord: and into sheetDidEnd.... The action method deleteRecord will now look like the following:

- (IBAction)deleteRecord:(id)sender
{
  NSString *title = @"Warning!";
  NSString *defaultButton = @"Delete";
  NSString *alternateButton = @"Don't Delete";
  NSString *otherButton = nil;
  NSString *message = @"Are you sure you want to delete the selected record(s)?";

  if ( [tableView numberOfSelectedRows] == 0 )
    return;
  
  NSBeep();
  NSBeginAlertSheet(title, defaultButton, alternateButton, otherButton, mainWindow, self, @selector(sheetDidEnd:returnCode:contextInfo:), nil, nil, message);
}

That's the first part. Now we have to implement sheetDidEnd... Add the following method definition to your Controller.m file:

- (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
{
  NSEnumerator *enumerator;
  NSNumber *index;
  NSMutableArray *tempArray;
  id tempObject;

  if ( returnCode == NSAlertDefaultReturn ) {
      enumerator = [tableView selectedRowEnumerator];
      tempArray = [NSMutableArray array];
    
      while ( (index = [enumerator nextObject]) ) {
        tempObject = [records objectAtIndex:[index intValue]];
        [tempArray addObject:tempObject];
      }
    
      [records removeObjectsInArray:tempArray];
      [tableView reloadData];
      [self saveData]; 
  }
}

The code we use to delete the selected records hasn't changed at all -- it's just executed in a different place.

So that's how we make an alert sheet. You should be able to compile this code now and see the fruits of your labor. Your alert sheet should look like the one shown below.

Screenshot.
Our alert sheet.

Now we go on to a slightly more complex sheet implementation that will allow us to display the preferences window as a sheet attached to the main application window.

Sheets from arbitrary windows

In this next section, we will concern ourselves with converting our preferences window into a sheet. The method we will use to open our preferences window as a sheet attached to the main application window comes to us from NSApplication, and its name is beginSheet:modalForWindow:modalDelegate:didEndSelector:contextInfo: (we'll refer to it as beginSheet...). You should notice that many of the arguments are the same as what we had in NSBeginAlertSheet, such as modalDelegate:, didEndSelector:, and contextInfo:. The first argument is really the only new thing in this method, and all that is in the window we wish to make into a sheet. The rest of the arguments are the same as their counterparts in NSBeginAlertSheet. Let's write down openPrefsSheet with this method in it:

- (IBAction)openPrefsSheet:(id)sender
{
[NSApp beginSheet:prefsWindow 
                   modalForWindow:mainWindow 
                   modalDelegate:nil
                   didEndSelector:nil
                   contextInfo:nil];

    [NSApp runModalForWindow:prefsWindow];

    [NSApp endSheet:prefsWindow];
    [prefsWindow orderOut:self];
}

Recall that NSApp is a global constant provided to us by Cocoa that lets us send messages to our application's instance of NSApplication. You see in the example above that there is a lot of support code that allows us to work the sheet. Also notice that while beginSheet... carries many of the same arguments as NSBeginAlertSheet, we don't use them in the same way. In particular, we made the last three arguments "nil". When we used NSBeginAlertSheet we used the modal delegate and selector to indicate a method to invoke when the button is pressed. Here we don't do that. Rather we rely on the Close button's action closePrefsSheet to handle button-specific code.

The message beginSheet... attaches prefsWindow to mainWindow as a sheet, and starts what is known as a modal session. A sheet in a modal session stops all other windows and panels from receiving events such as mouse clicks, but it still allows the application to perform some long background process or operation. The next line is a runModalForWindow: message to NSApp, which creates what is known as a modal event loop for prefsWindow -- our sheet. What this means is that, like in a modal session, the application will not accept any events unless that input is directed at the window we're running a modal loop for -- prefsWindow, but unlike a modal session, the application will pause until the modal loop is ended. Note that we will still be able to interact with control in the sheet, which is crucial as a preferences window isn't much use if we can't interact with the controls!

Pages: 1, 2, 3

Next Pagearrow