Using Alerts and Panels
With every program, you’ll need to design the unique features of your program while letting the Cocoa framework worry about making your program look and behave like a standard OS X program. Two common features of nearly every OS X program are alerts and panels.
An alert typically pops up on the screen to inform the user such as asking the user to verify the deletion of a file or to alert the user of a problem. A panel displays common user interface items such as a print panel that OS X programs typically display to give you options for printing, or an open panel for selecting a file.
By using alerts and panels, you can add common OS X user interface elements without having to create your own user interface or without needing to write Swift code. Alerts and panels are part of the Cocoa framework that your OS X programs can use to create a standard OS X user interface.
Using Alerts
An alert is based on the NSAlert class in the Cocoa framework. At the simplest level, you can create an alert with two lines of Swift code:
var myAlert = NSAlert()
myAlert.runModal()
The first line declares an object called myAlert that is based on the NSAlert class. Then the second line uses the runModal() method to display the alert. When an alert appears, it’s considered modal, which means the alert won’t let the user do anything until the user dismisses the alert.
The above two lines of code creates a generic alert that displays an OK button, a generic graphic image, and a generic text message as shown in Figure 18-1.
Figure 18-1. Creating a generic alert
To customize an alert, you can modify the following properties:
To see how to customize an alert, follow these steps:
@IBAction func showAlert(sender: NSButton) {
var myAlert = NSAlert()
myAlert.messageText = "Warning!"
myAlert.informativeText = "Zombies approaching"
myAlert.alertStyle = NSAlertStyle.CriticalAlertStyle
myAlert.showsSuppressionButton = true
myAlert.suppressionButton?.title = "Stop scaring me"
myAlert.runModal()
}
Figure 18-2. Displaying a customized alert
Getting Feedback from an Alert
An alert typically displays a single OK button so the user can dismiss the alert. However, an alert can get feedback from the user in one of two ways:
To determine if the user selected the suppression check box or not, you need to access the suppressionButton!.state property. If the check box is selected, the suppressionButton!.state property is 1. If the check box is clear, the suppressionButton!.state property is 0.
The second way to get feedback from an alert is by displaying two or more buttons on an alert. To add more buttons to an alert, you need to use the addButtonWithTitle method. Then to determine which button the user selected, you need to use the NSAlertFirstButtonReturn, NSAlertSecondButtonReturn, or NSAlertThirdButtonReturn constants.
If you have four or more buttons on an alert, you can check for the fourth button by using NSAlertThirdButtonReturn + 1, a fifth button by using NSAlertThirdButtonReturn + 2, and so on for each additional button beyond the third one.
To see how to identify what options the user chose in an alert, follow these steps:
@IBAction func showAlert(sender: NSButton) {
var myAlert = NSAlert()
myAlert.messageText = "Warning!"
myAlert.informativeText = "Zombies approaching"
myAlert.alertStyle = NSAlertStyle.CriticalAlertStyle
myAlert.showsSuppressionButton = true
myAlert.suppressionButton?.title = "Stop scaring me"
myAlert.addButtonWithTitle("Ignore it")
myAlert.addButtonWithTitle("Run")
myAlert.addButtonWithTitle("Panic")
myAlert.addButtonWithTitle("Do nothing")
let choice = myAlert.runModal()
switch choice {
case NSAlertFirstButtonReturn:
print ("User clicked Ignore it")
case NSAlertSecondButtonReturn:
print ("User clicked Run")
case NSAlertThirdButtonReturn:
print ("User clicked Panic")
case NSAlertThirdButtonReturn + 1:
print ("User clicked Do nothing")
default: break
}
if myAlert.suppressionButton!.state == 1 {
print ("Checked")
} else {
print ("Not checked")
}
}
The addButtonWithTitle methods create buttons on the alert where the first addButtonWithTitle method creates a default button and the other addButtonWithTitle methods create additional buttons. To capture the button that the user clicked on, the above code creates a constant called “choice.”
Then it uses a switch statement to identify which button the user clicked. Notice that the fourth button is identified by adding 1 to the NSAlertThirdButtonReturn constant.
The suppressionButton!.state property checks if the user selected the check box that appears on the alert. If its value is 1, then the user selected the check box. Otherwise if its value is 0, the check box is clear.
Figure 18-3. The alert created by Swift code
Alerts typically appear as a modal dialog that creates another window that you can move separately from the main program window. Another way to display alert is as a sheet that appears to scroll down from the title bar of the currently active window.
To make an alert appear as a sheet, you need to use the beginSheetModalForWindow method like this:
alertObject.beginSheetModalForWindow(window, completionHandler: closure)
The first parameter defines the window where you want the sheet to appear. In the AlertProgram project, the IBOutlet representing the user interface window is called a window. The second parameter is the completion handler label, which identifies the name of a special function called a closure.
A closure represents a shorthand way of writing a function. Instead of writing a function the traditional way like this:
func functionName(parameters) -> Type {
// Insert code here
return value
}
You can write a closure like this:
let closureName = { (parameters) -> Type in
// Insert code here
}
To see how to turn an alert into a sheet and use closures, follow these steps:
@IBAction func showAlert(sender: NSButton) {
var myAlert = NSAlert()
myAlert.messageText = "Warning!"
myAlert.informativeText = "Zombies approaching"
myAlert.alertStyle = NSAlertStyle.CriticalAlertStyle
myAlert.showsSuppressionButton = true
myAlert.suppressionButton?.title = "Stop scaring me"
myAlert.addButtonWithTitle("Ignore it")
myAlert.addButtonWithTitle("Run")
myAlert.addButtonWithTitle("Panic")
myAlert.addButtonWithTitle("Do nothing")
let myCode = { (choice:NSModalResponse) -> Void in
switch choice {
case NSAlertFirstButtonReturn:
print ("User clicked Ignore it")
case NSAlertSecondButtonReturn:
print ("User clicked Run")
case NSAlertThirdButtonReturn:
print ("User clicked Panic")
case NSAlertThirdButtonReturn + 1:
print ("User clicked Do nothing")
default: break
}
if myAlert.suppressionButton!.state == 1 {
print ("Checked")
} else {
print ("Not checked")
}
}
myAlert.beginSheetModalForWindow(window, completionHandler: myCode)
}
Figure 18-4. The alert as a sheet
In the above Swift code, the closure is defined as follows:
let myCode = { (choice:NSModalResponse) -> Void in
switch choice {
case NSAlertFirstButtonReturn:
print ("User clicked Ignore it")
case NSAlertSecondButtonReturn:
print ("User clicked Run")
case NSAlertThirdButtonReturn:
print ("User clicked Panic")
case NSAlertThirdButtonReturn + 1:
print ("User clicked Do nothing")
default: break
}
if myAlert.suppressionButton!.state == 1 {
print ("Checked")
} else {
print ("Not checked")
}
}
myAlert.beginSheetModalForWindow(window, completionHandler: myCode)
You could replace the closure name of “myCode” with the actual closure code in its place like this:
myAlert.beginSheetModalForWindow(window, completionHandler: { (choice:NSModalResponse) -> Void in
switch choice {
case NSAlertFirstButtonReturn:
print ("User clicked Ignore it")
case NSAlertSecondButtonReturn:
print ("User clicked Run")
case NSAlertThirdButtonReturn:
print ("User clicked Panic")
case NSAlertThirdButtonReturn + 1:
print ("User clicked Do nothing")
default: break
}
if myAlert.suppressionButton!.state == 1 {
print ("Checked")
} else {
print ("Not checked")
}
})
Putting closures inline directly within a method shortens the amount of code you need to write, but at the possible expense of making the overall code harder to understand. Separating the closure by name and then calling it by name makes it easier to reuse that closure elsewhere in your program if necessary, but also makes your code clearer at the expense of forcing you to write more code.
Choose whatever method you like best but as in all aspects of programming, stick to one style to make it easy for other programmers to understand your code later if you’re not around to explain how it works.
Using Panels
Panels represent common user interface elements that OS X programs need such as displaying an Open panel to let users select a file to open and a Save panel to let users choose a folder to store a file. The Open panel is based on the NSOpenPanel class while the Save panel is based on the NSSavePanel class.
An Open panel let the user select a file to open. The Open panel needs to return a file name if the user selected a file. Some properties that the Open panel uses include:
To see how to use an Open panel, follow these steps:
@IBAction func openPanel(sender: NSButton) {
var myOpen = NSOpenPanel()
myOpen.canChooseFiles = true
myOpen.canChooseDirectories = true
myOpen.allowsMultipleSelection = true
myOpen.beginWithCompletionHandler { (result) -> Void in
if result == NSFileHandlingPanelOKButton {
print (myOpen.URLs)
}
}
}
Figure 18-5. The Open panel
Note The Open panel only selects a file/folder, but you’ll still need to write Swift code to actually open any file the user selects.
A Save panel looks similar to an Open panel, but its purpose is to let the user select folder and define a file name to save. The Save panel needs to return a file name if the user selected a file. Some properties that the Open panel uses include:
To see how to use a Save panel, follow these steps:
@IBAction func savePanel(sender: NSButton) {
var mySave = NSSavePanel()
mySave.title = "Save a File Here"
mySave.prompt = "Save Me"
mySave.beginWithCompletionHandler { (result) -> Void in
if result == NSFileHandlingPanelOKButton {
print (mySave.URL)
print (mySave.nameFieldStringValue)
}
}
}
Figure 18-6. The condensed Save panel
Figure 18-7. The expanded Save panel
Summary
When designing a standard OS X user interface, you don’t have to create everything yourself. By taking advantage of the Cocoa framework, you can create alerts and panels that look and behave like other OS X programs with little extra coding on your own part.
Alerts let you display brief messages to the user such as warnings. You can customize an alert with text and graphics, and place two or more buttons on the alert. If you place two or more buttons on an alert, you’ll need to write Swift code to identify which button the user clicked.
An alert typically appears as a separate window, but you can also make it appear as a sheet that drops down from a window’s title.
Panels display commonly used user interface items such as an Open panel to select a file to open, and a Save panel to select a folder and a file name to save data. The Open and Save panels are part of the Cocoa framework, but you’ll need to write additional Swift code to make the Open and Save panels actually open or save a file to a hard disk.
Alerts and panels let you create standard OS X user interface elements with little additional coding. By using these features of the Cocoa framework, you can create standard OS X programs that work reliably and behave how users expect an OS X program to look and behave.