Learn Objective-C on the Mac - Mark Dalrymple [110]
Figure 10-9. Some standard sheets in action
Chances are, you won’t use sheets in every Cocoa app you ever build, but they are useful in situations where your application needs some sort of user input related to a particular window, and you’d rather not use a modal panel that stops input to the rest of your application. In the example just shown, for instance, without the use of sheets, that Save panel would probably run in a modal fashion, blocking input to all other windows until the user dismissed the save panel. With the use of sheets, the user can temporarily leave the ongoing Save operation and do some other interaction with the application before committing the Save.
Sheets aren’t represented by a particular class in Cocoa. Rather, they are normal windows, used in a special way. Every NSWindow can have a sheet attached to it, and every window or panel that’s normally used in a modal way can be used as a sheet as well.
Let’s see the basics of how to use a sheet by attaching an NSSavePanel to a normal window. Create a new Cocoa application project in Xcode, and save it as SheetLab. If the new project doesn’t include an app delegate (e.g. if you are running on Leopard), make a new NSObject subclass called SheetLab_AppDelegate, then open up MainMenu.xib and drag a new object from the Library, changing its class to SheetLab_AppDelegate, and connecting the NSApplication’s delegate outlet to it.
Now, edit SheetLab_AppDelegate.h, adding an outlet and an action method:
Then edit SheetLab_AppDelegate.m, adding the following method implementations:
The runSaveSheet: method grabs the standard save panel, and tells it to run modally attached to a window (using the window instance variable). It also specifies what method should be called when the sheet is done, passing in the selector of the next method, savePanelDidEnd:returnCode:contextInfo:, which gets called at the end of the modal session, at which point it can check the returnCode (to see whether the user clicked Save or Cancel), access the save panel to get the chosen filename, and so on.
Now open up MainMenu.xib in Interface Builder, and add a new button to the empty window that’s been provided for you. Label the button “Run Save Sheet,” and connect it to the app delegate’s runSaveSheet: action method. The final thing to connect here is the app delegate’s window property, so Control-drag from the app delegate to the window you have, and select window from the list of choices. Save your changes, switch back to Xcode, Build & Run, and you should see this all working. Clicking the Run Save Sheet button brings up the modal save panel, attached as a sheet on top of the window you just clicked in.
Wrap-up
This chapter provided an introduction to several key parts of the Cocoa GUI experience, as well as a few examples of how the responder chain is used. These features are crucial to making a professional-quality Mac application. Mac users tend to be pretty unforgiving when applications use non-standard behaviors without a good reason, so it’s important to know how to deal with windows and menus in ways that users will recognize. In the next chapter, we’ll build on this knowledge as we explore Cocoa’s classes for dealing with documents and their associated windows.
Chapter11
Document-Based Applications
So far, the applications we’ve built in this book all have one major shortcoming in common: each of them acts in sort of an all-or-nothing way. You’ve either got a particular piece of data in the one backing store that the application is using (if it’s using one at all), or you don’t have it anywhere. None of them has any notion of letting you split your data into the discrete, unrelated storage units that we call “documents.” Although having everything in a single database is good for some purposes, for others it’s a huge hindrance. What if you want to share just a part of your data with someone else, or you want to be able to view details for two or more of the same kind of Core Data entity, in multiple