macOS NSAlert: what changed
Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.
Title: macOS NSAlert
| Old | New | Line | |
|---|---|---|---|
| 1 | + | *(macOS only)* | |
| 2 | + | ||
| 3 | + | ## Overview | |
| 4 | + | ||
| 5 | + | The NSAlert class allows you to specify an alert level, alert text, button titles, and a custom icon if required. The class also lets your alerts display a help button and provides ways for applications to offer help specific to an alert. The NSAlert class is available from Mac OS X Panther 10.3 onwards. An NSAlert object is intended for a single alert with a unique combination of title, buttons, and so on that is displayed on a particular condition. You should create an NSAlert object for each alert dialog, creating it only when you need to display it, and releasing it when you are done. | |
| 6 | + | ||
| 7 | + | ## Alert styles | |
| 8 | + | ||
| 9 | + | There are three alert styles: | |
| 10 | + | ||
| 11 | + | - Critical: Use when severe consequences may result from certain user responses (for example, a "clean install" will erase all data on a volume). This style includes a caution icon badged with the application icon. | |
| 12 | + | ||
| 13 | + | - Warning: Use to warn the user about a current or impending event. The purpose is more than informational but not critical. This is the default alert style if none is specified. It is badged with the application icon. | |
| 14 | + | ||
| 15 | + | - Informational: Use to inform the user about a current or impending event. It is badged with the application icon. | |
| 16 | + | ||
| 17 | + | Unless you change it with the *setMessageText* method, the title is "Alert" for all three styles (the actual explanatory text is set with the *setInformativeText* method). | |
| 18 | + | ||
| 19 | + | The global variable names for styles changed between macOS 10.12 (Sierra) and 10.13 (High Sierra): | |
| 20 | + | ||
| 21 | + | **macOS 10.13 (Sierra) onwards** | |
| 22 | + | ||
| 23 | + | - NSAlertStyleCritical | |
| 24 | + | - NSAlertStyleWarning | |
| 25 | + | - NSAlertStyleInformational | |
| 26 | + | ||
| 27 | + | **macOS 10.3 (Panther) through 10.12 (Sierra)** | |
| 28 | + | ||
| 29 | + | - NSCriticalAlertStyle | |
| 30 | + | - NSWarningAlertStyle | |
| 31 | + | - NSInformationalAlertStyle | |
| 32 | + | ||
| 33 | + | ## Code examples | |
| 34 | + | ||
| 35 | + | ### Modal error dialog with OK button | |
| 36 | + | ||
| 37 | + | This example demonstrates a simple modal error dialog with just error message text and an OK button to exit the dialog. Compiles with FPC 3.0.4 onwards. | |
| 38 | + | ||
| 39 | + | ``` pascal | |
| 40 | + | unit Unit1; | |
| 41 | + | ||
| 42 | + | {$mode objfpc}{$H+} | |
| 43 | + | {$modeswitch objectivec1} | |
| 44 | + | ||
| 45 | + | interface | |
| 46 | + | ||
| 47 | + | uses | |
| 48 | + | Forms, // needed for Form1 | |
| 49 | + | StdCtrls, // needed for Button1 | |
| 50 | + | CocoaAll; // needed for NSAlert, NSStr | |
| 51 | + | ||
| 52 | + | type | |
| 53 | + | ||
| 54 | + | { TForm1 } | |
| 55 | + | ||
| 56 | + | TForm1 = class(TForm) | |
| 57 | + | Button1: TButton; | |
| 58 | + | procedure Button1Click(Sender: TObject); | |
| 59 | + | private | |
| 60 | + | ||
| 61 | + | public | |
| 62 | + | ||
| 63 | + | end; | |
| 64 | + | ||
| 65 | + | var | |
| 66 | + | Form1: TForm1; | |
| 67 | + | ||
| 68 | + | implementation | |
| 69 | + | ||
| 70 | + | {$R *.lfm} | |
| 71 | + | ||
| 72 | + | {Display error message modal dialog with OK button.} | |
| 73 | + | procedure ErrorModalDlgWithOKbutton(const ErrorMsg : NSstring); | |
| 74 | + | var | |
| 75 | + | Alert : NSAlert; | |
| 76 | + | begin | |
| 77 | + | Alert := NSAlert.alloc.init; | |
| 78 | + | ||
| 79 | + | // If you want to change the default "Alert" message to something else: | |
| 80 | + | //Alert.setMessageText(NSStr('Message Text')); | |
| 81 | + | Alert.setInformativeText(ErrorMsg); | |
| 82 | + | ||
| 83 | + | Alert.runModal; | |
| 84 | + | Alert.release; | |
| 85 | + | end; | |
| 86 | + | ||
| 87 | + | { TForm1 } | |
| 88 | + | ||
| 89 | + | procedure TForm1.Button1Click(Sender: TObject); | |
| 90 | + | begin | |
| 91 | + | ErrorModalDlgWithOKbutton(NSStr('Error Message Test!')); | |
| 92 | + | end; | |
| 93 | + | ||
| 94 | + | end. | |
| 95 | + | ``` | |
| 96 | + | ||
| 97 | + | ### Modal error sheet with OK button | |
| 98 | + | ||
| 99 | + | This example demonstrates a simple modal error sheet with just error message text and an OK button to close the sheet. To compile this example you need to use FPC 3.2.0 onwards. | |
| 100 | + | ||
| 101 | + | ``` pascal | |
| 102 | + | unit Unit1; | |
| 103 | + | ||
| 104 | + | {$mode objfpc}{$H+} | |
| 105 | + | {$modeswitch objectivec1} | |
| 106 | + | ||
| 107 | + | interface | |
| 108 | + | ||
| 109 | + | uses | |
| 110 | + | Forms, // needed for Form1 | |
| 111 | + | StdCtrls, // needed for Button1 | |
| 112 | + | CocoaAll; // needed for NSAlert, NSStr | |
| 113 | + | ||
| 114 | + | type | |
| 115 | + | ||
| 116 | + | { TForm1 } | |
| 117 | + | ||
| 118 | + | TForm1 = class(TForm) | |
| 119 | + | Button1: TButton; | |
| 120 | + | procedure Button1Click(Sender: TObject); | |
| 121 | + | private | |
| 122 | + | ||
| 123 | + | public | |
| 124 | + | ||
| 125 | + | end; | |
| 126 | + | ||
| 127 | + | var | |
| 128 | + | Form1: TForm1; | |
| 129 | + | ||
| 130 | + | implementation | |
| 131 | + | ||
| 132 | + | {$R *.lfm} | |
| 133 | + | ||
| 134 | + | {Display error message modal dialog} | |
| 135 | + | procedure ModalErrorSheetWithOKbutton(const ErrorMsg: NSString); | |
| 136 | + | var | |
| 137 | + | Alert : NSAlert; | |
| 138 | + | begin | |
| 139 | + | Alert := NSAlert.alloc.init; | |
| 140 | + | ||
| 141 | + | Alert.setAlertStyle(NSCriticalAlertStyle); | |
| 142 | + | // If you want to change the default "Alert" message to something else: | |
| 143 | + | //Alert.setMessageText(NSStr('Message Text')); | |
| 144 | + | Alert.setInformativeText(ErrorMSg); | |
| 145 | + | ||
| 146 | + | Alert.beginSheetModalForWindow_completionHandler(NSView(Form1.Handle).window, nil); | |
| 147 | + | Alert.release; | |
| 148 | + | end; | |
| 149 | + | ||
| 150 | + | { TForm1 } | |
| 151 | + | ||
| 152 | + | procedure TForm1.Button1Click(Sender: TObject); | |
| 153 | + | begin | |
| 154 | + | ModalErrorSheetWithOKbutton(NSStr('Error message test')); | |
| 155 | + | end; | |
| 156 | + | ||
| 157 | + | end. | |
| 158 | + | ``` | |
| 159 | + | ||
| 160 | + | ### Modal error sheet with three buttons | |
| 161 | + | ||
| 162 | + | This example demonstrates a more complex modal error sheet with error message text and three possible buttons with which to close the sheet. Depending on which button you use to close the sheet, different actions can be taken -- in this example, we simply change the caption of a label to indicate which button was used to close the sheet. The magic to handle the buttons is in the completion handler block which executes after the sheet is closed. | |
| 163 | + | ||
| 164 | + | To compile this example you need to use FPC 3.2.0 onwards. | |
| 165 | + | ||
| 166 | + | ``` pascal | |
| 167 | + | unit Unit1; | |
| 168 | + | ||
| 169 | + | {$mode objfpc}{$H+} | |
| 170 | + | {$modeswitch objectivec1} | |
| 171 | + | {$modeswitch cblocks} | |
| 172 | + | ||
| 173 | + | interface | |
| 174 | + | ||
| 175 | + | uses | |
| 176 | + | Forms, // needed for Form1 | |
| 177 | + | StdCtrls, // needed for Button1 | |
| 178 | + | CocoaAll; // needed for NSAlert, NSstring, NSStr | |
| 179 | + | ||
| 180 | + | type | |
| 181 | + | ||
| 182 | + | { TForm1 } | |
| 183 | + | ||
| 184 | + | TForm1 = class(TForm) | |
| 185 | + | Button1: TButton; | |
| 186 | + | Label1: TLabel; | |
| 187 | + | procedure Button1Click(Sender: TObject); | |
| 188 | + | private | |
| 189 | + | ||
| 190 | + | public | |
| 191 | + | ||
| 192 | + | end; | |
| 193 | + | ||
| 194 | + | { cBlock } | |
| 195 | + | ||
| 196 | + | // setup cBlock for beginSheetModalForWindow_completionHandler method | |
| 197 | + | tBlock = reference to procedure(resultCode: NSModalResponse); cdecl; cblock; | |
| 198 | + | ||
| 199 | + | { NSAlert } | |
| 200 | + | ||
| 201 | + | NSAlert = objcclass external (NSObject) | |
| 202 | + | public | |
| 203 | + | // Need to redefine beginSheetModalForWindow_completionHandler version from | |
| 204 | + | // packages/cocoaint/src/appkit/NSAlert.inc to use our tBlock (cf OpaqueCBlock) | |
| 205 | + | procedure beginSheetModalForWindow_completionHandler(sheetWindow: NSWindow; handler: tBlock); | |
| 206 | + | message 'beginSheetModalForWindow:completionHandler:'; | |
| 207 | + | // Below not redefined - you could omit the methods you do not use | |
| 208 | + | class function alertWithError (error: NSError): NSAlert; message 'alertWithError:'; | |
| 209 | + | class function alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat(message_: NSString; | |
| 210 | + | defaultButton: NSString; alternateButton: NSString; otherButton: NSString; format: NSString): NSAlert; varargs; | |
| 211 | + | message 'alertWithMessageText:defaultButton:alternateButton:otherButton:informativeTextWithFormat:'; | |
| 212 | + | deprecated 'in 10_3, 10_10, "Use -init instead"'; | |
| 213 | + | procedure setMessageText(newValue: NSString); message 'setMessageText:'; | |
| 214 | + | function messageText: NSString; message 'messageText'; | |
| 215 | + | procedure setInformativeText(newValue: NSString); message 'setInformativeText:'; | |
| 216 | + | function informativeText: NSString; message 'informativeText'; | |
| 217 | + | procedure setIcon(newValue: NSImage); message 'setIcon:'; | |
| 218 | + | function icon: NSImage; message 'icon'; | |
| 219 | + | function addButtonWithTitle (title: NSString): NSButton; message 'addButtonWithTitle:'; | |
| 220 | + | function buttons: NSArray; message 'buttons'; | |
| 221 | + | procedure setShowsHelp(newValue: ObjCBOOL); message 'setShowsHelp:'; | |
| 222 | + | function showsHelp: ObjCBOOL; message 'showsHelp'; | |
| 223 | + | procedure setHelpAnchor(newValue: NSString); message 'setHelpAnchor:'; | |
| 224 | + | function helpAnchor: NSString; message 'helpAnchor'; | |
| 225 | + | procedure setAlertStyle(newValue: NSAlertStyle); message 'setAlertStyle:'; | |
| 226 | + | function alertStyle: NSAlertStyle; message 'alertStyle'; | |
| 227 | + | procedure setDelegate(newValue: NSAlertDelegateProtocol); message 'setDelegate:'; | |
| 228 | + | function delegate: NSAlertDelegateProtocol; message 'delegate'; | |
| 229 | + | procedure setShowsSuppressionButton(newValue: ObjCBOOL); message 'setShowsSuppressionButton:'; | |
| 230 | + | function showsSuppressionButton: ObjCBOOL; message 'showsSuppressionButton'; | |
| 231 | + | function suppressionButton: NSButton; message 'suppressionButton'; | |
| 232 | + | procedure setAccessoryView(newValue: NSView); message 'setAccessoryView:'; | |
| 233 | + | function accessoryView: NSView; message 'accessoryView'; | |
| 234 | + | procedure layout; message 'layout'; { available in 10_5 } | |
| 235 | + | function runModal: NSModalResponse; message 'runModal'; | |
| 236 | + | procedure beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo (window: NSWindow; | |
| 237 | + | delegate_: id; didEndSelector: SEL; contextInfo: pointer); | |
| 238 | + | message 'beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo:'; | |
| 239 | + | deprecated 'in 10_3, 10_10, "Use -beginSheetModalForWindow:completionHandler: instead"'; | |
| 240 | + | function window: id; message 'window'; | |
| 241 | + | end; | |
| 242 | + | ||
| 243 | + | var | |
| 244 | + | Form1: TForm1; | |
| 245 | + | ||
| 246 | + | implementation | |
| 247 | + | ||
| 248 | + | {$R *.lfm} | |
| 249 | + | ||
| 250 | + | { Completion handler } | |
| 251 | + | ||
| 252 | + | procedure myCompletionHandler(resultCode: NSModalResponse); | |
| 253 | + | begin | |
| 254 | + | if(resultCode = NSAlertFirstButtonReturn) then | |
| 255 | + | Form1.Label1.Caption := 'Exited with Button 1' | |
| 256 | + | else if(resultCode = NSAlertSecondButtonReturn) then | |
| 257 | + | Form1.Label1.Caption := 'Exited with Button 2' | |
| 258 | + | else if(resultCode = NSAlertThirdButtonReturn) then | |
| 259 | + | Form1.Label1.Caption := 'Exited with Button 3'; | |
| 260 | + | end; | |
| 261 | + | ||
| 262 | + | { Display error message with 3 buttons as a modal sheet } | |
| 263 | + | ||
| 264 | + | procedure ModalErrorSheetWithButtons(const ErrorMsg: string; ParentWindow : NSWindow); | |
| 265 | + | var | |
| 266 | + | Alert : NSAlert; | |
| 267 | + | begin | |
| 268 | + | Alert := NSAlert.alloc.init; | |
| 269 | + | ||
| 270 | + | Alert.setAlertStyle(NSCriticalAlertStyle); | |
| 271 | + | // If you want to change the default "Alert" message to something else: | |
| 272 | + | //Alert.setMessageText(NSStr('Message Text')); | |
| 273 | + | Alert.setInformativeText(NSStr(ErrorMSg)); | |
| 274 | + | ||
| 275 | + | // Note buttons are counted right to left pre-Big Sur | |
| 276 | + | // and from top to bottom with Big Sur+ | |
| 277 | + | Alert.addButtonWithTitle(NSStr('Button 1')); | |
| 278 | + | Alert.addButtonWithTitle(NSStr('Button 2')); | |
| 279 | + | Alert.addButtonWithTitle(NSStr('Button 3')); | |
| 280 | + | ||
| 281 | + | Alert.beginSheetModalForWindow_completionHandler(parentWindow, @mycompletionhandler); | |
| 282 | + | ||
| 283 | + | Alert.release; | |
| 284 | + | end; | |
| 285 | + | ||
| 286 | + | { TForm1 } | |
| 287 | + | ||
| 288 | + | procedure TForm1.Button1Click(Sender: TObject); | |
| 289 | + | begin | |
| 290 | + | ModalErrorSheetWithButtons('Error message test', NSView(Form1.Handle).window); | |
| 291 | + | end; | |
| 292 | + | ||
| 293 | + | end. | |
| 294 | + | ``` | |
| 295 | + | ||
| 296 | + | ### Suppress future alerts | |
| 297 | + | ||
| 298 | + | It is possible to suppress future alerts by adding a checkbox to the alert which allows the user to opt out of receiving the same alerts when they are triggered in future . This really only makes sense for warning and informational alerts. | |
| 299 | + | ||
| 300 | + | My somewhat contrived example below assumes that you keep track of the user's wishes by, for example, saving the user's choice as a preference which the user can then reverse if needed. For details of how to implement preferences see [[Mac Preferences Read and Write|this article]]. | |
| 301 | + | ||
| 302 | + | ``` pascal | |
| 303 | + | unit Unit1; | |
| 304 | + | ||
| 305 | + | {$mode objfpc}{$H+} | |
| 306 | + | {$modeswitch objectivec1} | |
| 307 | + | ||
| 308 | + | interface | |
| 309 | + | ||
| 310 | + | uses | |
| 311 | + | Forms, // needed for Form1 | |
| 312 | + | StdCtrls, // needed for Button1 | |
| 313 | + | CocoaAll; // needed for NSAlert, NSstring, NSStr | |
| 314 | + | ||
| 315 | + | type | |
| 316 | + | ||
| 317 | + | { TForm1 } | |
| 318 | + | ||
| 319 | + | TForm1 = class(TForm) | |
| 320 | + | Button1: TButton; | |
| 321 | + | procedure Button1Click(Sender: TObject); | |
| 322 | + | private | |
| 323 | + | ||
| 324 | + | public | |
| 325 | + | ||
| 326 | + | end; | |
| 327 | + | ||
| 328 | + | var | |
| 329 | + | Form1: TForm1; | |
| 330 | + | suppressAlertFileNotSaved: Boolean; // Whether to suppress warning file not saved | |
| 331 | + | // - retrieve from application preferences | |
| 332 | + | ||
| 333 | + | implementation | |
| 334 | + | ||
| 335 | + | {$R *.lfm} | |
| 336 | + | ||
| 337 | + | { Display warning message modal window } | |
| 338 | + | ||
| 339 | + | procedure ModalDlgWithOKButton(const ErrorMsg: string); | |
| 340 | + | var | |
| 341 | + | Alert : NSAlert; | |
| 342 | + | begin | |
| 343 | + | // Should this alert be suppressed? | |
| 344 | + | // - if so do nothing and exit. | |
| 345 | + | if(suppressAlertFileNotSaved) then | |
| 346 | + | exit; | |
| 347 | + | ||
| 348 | + | Alert := NSAlert.alloc.init; | |
| 349 | + | ||
| 350 | + | Alert.setAlertStyle(NSWarningAlertStyle); | |
| 351 | + | // If you want to change the default "Alert" message to something else: | |
| 352 | + | Alert.setMessageText(NSStr('Warning')); | |
| 353 | + | Alert.setInformativeText(NSStr(ErrorMSg)); | |
| 354 | + | // Show checkbox to suppress future warnings | |
| 355 | + | Alert.setShowsSuppressionButton(true); | |
| 356 | + | ||
| 357 | + | Alert.runModal; | |
| 358 | + | ||
| 359 | + | // Record whether to suppress future warnings | |
| 360 | + | // - check if checkbox checked | |
| 361 | + | if(Alert.suppressionButton.state = 1) then | |
| 362 | + | suppressAlertFileNotSaved := True; | |
| 363 | + | ||
| 364 | + | Alert.release; | |
| 365 | + | end; | |
| 366 | + | ||
| 367 | + | { TForm1 } | |
| 368 | + | ||
| 369 | + | procedure TForm1.Button1Click(Sender: TObject); | |
| 370 | + | begin | |
| 371 | + | ModalDlgWithOKButton('You have not saved a backup of this file!'); | |
| 372 | + | end; | |
| 373 | + | ||
| 374 | + | end. | |
| 375 | + | ``` | |
| 376 | + | ||
| 377 | + | ## See also | |
| 378 | + | ||
| 379 | + | - [[Using macOS Alert Panels]] | |
| 380 | + | ||
| 381 | + | ## External links | |
| 382 | + | ||
| 383 | + | - [Apple: AppKit Framework](https://developer.apple.com/documentation/appkit). | |
| 384 | + | - [Apple: NSAlert class](https://developer.apple.com/documentation/appkit/nsalert). | |
| 385 | + | ||
| 386 | + | --- | |
| 387 | + | *From the Free Pascal wiki, page "macOS NSAlert", as it stood on 2021-05-09 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* | |
| 388 | + | Category: Code Snippets | |
| 389 | + | Category: Platform-sensitive development | |
| 390 | + | Category: macOS |
