Developing with Objective Pascal

Part 12: Adding Windows to a Cocoa App


Contents

Introduction
Requirements
Adding a window to your Xcode project
Displaying the window as a modal sheet
Running the app
Adapting the window


Introduction

Most Mac apps are document-based, as described in Part 2. This means they can open multiple documents, each in its own window, and most of the menu bar commands apply only to the current window or document, not to the entire application. As a result, a document-based app generally does not use modal dialogs, since a modal dialog would block access to the other windows until the user closes the dialog. Instead, document-based apps display dialogs as modal sheets, which are somewhere between modal dialogs and non-modal ones. With a modal sheet, the user can still switch to one of the other document windows, without first closing the dialog. These dialogs are often described as being document modal, rather than application modal.

Some Mac apps are both document-based and tab-based. For example, with Safari, you can choose File | New Window or File | New Tab. When you open multiple windows in Safari, you can then resize each window, position them side-by-side, etc. When you choose a menu command that displays a dialog (for example, File | Save As), the resulting modal sheet only applies to the currently focused window; you can still switch to a different window while the dialog is open. When you use multiple tabs, the dialog still applies only to the currently focused tab, but now it blocks access to the other tabs since all the tabs are in the same window. With this type of app, the user can then choose between the flexibility of multiple windows or the compactness of a single window with multiple tabs.

These notes describe how to add windows to your document-based Cocoa apps and display them as modal sheets.


Requirements


Adding a window to your Xcode project

Part 7 discusses a custom Pascal template for Xcode that you can use to add secondary views to an iOS app. A similar custom template could be used here as well (if someone's interested in creating it) to automate the multiple steps involved in adding a window. However, this article takes a different approach, starting instead with a finished window that you can add to your own project and adapt as necessary.

In case you're wondering, here are the manual steps for adding a new window to your Xcode project:

  1. Choose File | New | New File and choose Window from the User Interface tab under Mac OS X. This creates a .xib user interface file.

  2. Choose File | New | New File and choose Objective-C class from the Cocoa tab under Mac OS X, selecting NSWindowController to subclass. This creates an .h header file and an .m source file.

  3. Create a unit containing the Pascal equivalent to the ObjC class created in step 2.

  4. Wire the .xib to the ObjC class. Specifically, you must click the .xib's File's Owner button and, on the Custom Class tab, select the step 2 ObjC class from the list. You can then drag the window's outlet to the File's Owner button, thus connecting the window with the class that will manage it.
In this example, instead of creating a window manually, we'll use one that's ready to use, as follows:

  1. Unzip ObjP_ListWindow.zip that you downloaded above.

  2. Copy its files into your project folder. Xcode doesn't really care where you put the files, although the following organization should make sense:

  3. Open your project in Xcode and tell Xcode about the new files:


Displaying the window as a modal sheet

As its name suggests, the supplied window provides a way to display a list of items that the user can select from. In VCL/LCL terms, it functions very much like a TForm containing a TListBox and two TButton's. However, unlike a VCL/LCL form, we'll display the window as a modal sheet, rather than as a modal or non-modal dialog. Here's how you do that:

Tip: Review the code in ObjP_ListWindowControllerU.pas to see how the list is implemented.


Running the app

Now compile and run your app, then click the button on the document window that opens at startup. The list window should descend from from the document window's title bar as though pulled down ("unfurl" is Apple's term), rather than pop up like a modal dialog. One obvious benefit of this is that it's clear what the sheet applies to - it applies only to the window that it's attached to. The primary benefit is that it doesn't block out everything else. For example, you can continue using the program, even File | New, while the sheet is displayed.

Notice how the sheet does not have a title of its own. It's truly a subsidiary of the main window, not something that stands on its own.


Adapting the window

It's easy to adapt the list window's files and reuse them in other projects, even distribute them to other programmers. For example, to modify the window's files under a different name, just do this:

To change the window layout or add to it, just do that in Xcode as you would with any .xib. For example, since the list is implemented using an NSTableView, you could easily make it a multi-column list. Just increase the number of columns for the Table View and revise the tableView_objectValueForTableColumn_row method so it returns an appropriate value for each column. Tip: Enter a unique Identifier value for each Table Column and look for these values in your code, like so:

  if NSStrToStr(NSString(atableColumn.identifier)) = 'Column 1') then
    Result := StrToNSStr(somestring)
  else if NSStrToStr(NSString(atableColumn.identifier)) = 'Column 2') then
    Result := StrToNSStr(anotherstring);
And, as always, when in doubt refer to Apple's AppKit docs.


Copyright 2012 by Phil Hess.

macpgmr (at) fastermac (dot) net

First posted Feb. 5, 2012.