- Published
- 18 January 2014
- Tagged
For a well-documented feature, NSAlert
's accessoryView
feature takes a little bit of fiddling to implement. NSAlert
is behind the standard pop-up modal alert window that's familiar to everyone, often popping into the foreground and grabbing your attention, refusing to go away until you acknowledge its existence:
NSAlert
boxes can, by default, display any number of buttons (c.f. the classic "Save/Delete/Cancel" triumvirate), but advanced features require a little more jiggery-pokery. This mainly comes in the form of its accessoryView
property, which allows you to add an NSView
to the alert proper. At first you might be tempted to programatically create an NSView
, adding the required elements in init
methods and the like, but it turns out it's actually a bit easier to use nib
files to hold all of that. This is how to do so.
In XCode, create a new file, and make it an NSView
nib:
Now you can add elements to your heart's content. I've set mine up with a text box and a check box, which should do for a nice-looking NSAlert
:
Now there's the question of how we access this nib when we need it. What we need to do is assign responsibility of loading this nib to some class in our program. It doesn't matter which class ends up doing this: you could make it a WindowController's job, or you could make a completely new class to "wrap" this custom NSAlert
if you wanted. For purposes of this example I've got my NSAppDelegate
subclass to do all the heavy lifting, which is a terrible idea.
The important thing is that this class gets to be the File's Owner
for your custom NSView. In XCode, select "File's Owner" from the bar on the left, then in the identity inspector on the right (⌘+⌥+3) replace the Class
field with your chosen owner:
Now your nib knows who its owner will be, but the owner still doesn't know that it should own a nib. Open up the class you chose as file's owner[1] and in the header file, add an IBOutlet
:
@interface JRAppDelegate : NSObject <NSApplicationDelegate> {
IBOutlet NSView *customView;
IBOutlet NSTextField *stringField;
IBOutlet NSButton *checkbox;
}
I've added an IBOutlet
for the view, as well as one for the string field and checkbox. I figure I'll want to grab these values later, although you could just as easily use bindings and properties to grab said values. Whatever's your poison.
Now back in the nib file, we link up our owner and our custom view:
Obviously, you can link up anything else you want to right now.
At this stage, finally, the owning class and the nib are sufficiently aware of each others' existence that we can actually do something. For ease of testing, I'm placing the following code in -[JRAppDelegate applicationDidFinishLaunching:]
, which runs pretty much as soon as the app starts:
NSAlert *a = [[NSAlert alloc] init];
a.messageText = @"Hello!";
a.informativeText = @"I'm an NSAlert.";
[a runModal];
[NSApp terminate:self];
This produces the alert we saw at the top of the post. Let's add an accessoryView
:
NSAlert *a = [[NSAlert alloc] init];
a.messageText = @"Hello!";
a.informativeText = @"I'm an NSAlert.";
a.accessoryView = customView; // <--
[a runModal];
[NSApp terminate:self];
But! If you do this and run your app, you'll find that while the alert box pops up, your view isn't included. If I set a breakpoint on the line marked // <--
above, we can soon see that customView
isn't even assigned at this point:
It turns out that simply assigning something as an IBOutlet
doesn't result in its magical instantiation at runtime: Cocoa may be magic, but it's not that magic. Instead, we have to do the alloc/init cycle ourselves, using NSBundle
:
NSAlert *a = [[NSAlert alloc] init];
a.messageText = @"Hello!";
a.informativeText = @"I'm an NSAlert.";
//Replace @"CustomView" with the name of your custom view nib
[NSBundle.mainBundle loadNibNamed:@"CustomView" owner:self topLevelObjects:nil];
a.accessoryView = customView;
[a runModal];
[NSApp terminate:self];
If we run this, finally we find our custom view occupying pride of place in the centre of our NSAlert
window:
And what a sweet child it is.
More information
Apple has a handy little help file on custom alert dialogues, which at least tells you what they're for and some basics on how to make them.
Now would be a really great time to mention that you can open any file in XCode quickly by typing ⌘+⇧+O. This is the equivalent of SublimeText's ⌘+T trick, and saves a heap of time. ↩︎