TweetFollow Us on Twitter

The Road to Code: Windows to the World

Volume Number: 25
Issue Number: 05
Column Tag: The Road to Code

The Road to Code: Windows to the World

Windows, Panels, and Sheets

by Dave Dribin

Introduction

We've seen and used windows before; every one of our GUI applications has used one. This month we're going to talk a little bit more about windows and some of their relatives: panels and sheets.

On screen, windows are represented by the NSWindow class. We've been creating windows by using Interface Builder. Both the MainMenu.xib for non-document based applications and MyDocument.xib for document-based applications contain an instance of NSWindow. For this article, let's start with a clean Cocoa application (not a document-based application).

The Cocoa application project template provides you with a MainMenu.xib that contains the main menu and the main window. Interface Builder allows you to configure several attributes for the window, and Figure 1 shows the attributes setup for the main window.


Figure 1: Main window attributes in Interface Builder

The most important attribute for our purposes has been Visible At Launch. This means that the window is automatically displayed on the screen when the nib file is loaded by the application. AppKit automatically loads MainMenu.nib when applications startup, so this is why our main window gets displayed when the application is run. For document-based applications, AppKit loads MyDocument.nib when a new document is created, again creating the document window. (Remember that .xib files are compiled into .nib files, and .nib files are stored in your application's bundle.)

Adding another window to our application is nearly as simple as dragging a new window from the Interface Builder Library into your application. I say "nearly" because the default options (as of Interface Builder version 3.1.1) are less than ideal. Open the MainMenu.xib file. In order to help reduce confusion between our two windows, rename the main window's title to Main Window, and drag a new window from the Library. Figure 2 shows the attributes for a new window dragged from the Library.


Figure 2: Default window attributes

Two checkboxes are different than our main window: Release When Closed and One Shot. What if, however, we do not want the window to be visible at launch, and we want to show the window programmatically? We need to uncheck Visible at Launch, but we should also uncheck Release When Closed. Otherwise, the NSWindow object gets released automatically when the window is closed, and any attempts to make the window visible again will cause a crash. This commonly trips people up, beginning and advanced programmers, alike. I almost never want the behavior of Release When Closed checked, but I sometimes forget to uncheck it.

The One Shot attribute is useful for windows that are not expected to be on screen all the time and are only displayed once or twice. This allows the system to free some of the window's resources when it's not visible. Let's check that one, too. And finally, change the title to New Window. The customized attributes for our new window are shown in Figure 3.


Figure 3: Customized window attributes

Let's add a button to the main window that, when clicked, will show this new window. Start by dragging a button to the main widow. We don't need any code to display the window as we can hookup the button to actions already defined on the NSWindow class. Control drag from the button to the window and set the action to makeKeyAndOrderFront:. This action method makes the window visible, if it is not already, and then makes it the front-most window.

So, what's with the strange method name, then? AppKit uses the term key window as the window that is currently receiving user input from the keyboard and mouse. There can only be one key window in an application. In fact, NSApplication has a method called keyWindow that returns the application's key window.

It's worth noting that one of the things you get "for free" when using AppKit is the Window menu you see in most applications. Along with the commands to minimize and zoom windows is a list of all windows in the application. AppKit automatically updates this list of windows. Thus, when we make our new window visible, the Window menu will get updated as shown in Figure 4.


Figure 4: Window menu

Panels

A panel is a special kind of window that is used mainly as an auxiliary window. Panels are represented in code using the NSPanel class, which is a subclass of NSWindow. Panels differ from windows in a few respects:

  • Panels are removed from the screen when the application is not active,
  • Panels do not get listed in the Window menu, and
  • Panels can by closed by pressing the Escape key.
  • Panels can also be configured to have utility style where it uses a smaller window bar and it floats above all other windows, even when it's not the key window.

Let's add a panel to our application to get a feel for how they work. Add another button to our main window with a title of Show Panel, as shown in Figure 5.


Figure 5: Show Panel button

Drag a panel from the Library window into your nib file, and switch to the Attributes tab. The default attributes for a panel are shown in Figure 6.


Figure 6: New panel attributes

By default, it's configured to use the Utility style. You can uncheck this if you want a panel to look more like a window, such as a standard Find panel. We're going to customize the rest of these attributes a bit. First, change the title to New Panel. Again, uncheck Release When Closed and Visible At Launch, and check One Shot. The customized panel attributes are shown in Figure 7.


Figure 7: Customized panel attributes

Finally, we need to hook the button up to the panel's makeKeyAndOrderFront: action by control dragging from the button to the panel. Run your application again and notice the difference between the new window and the new panel. Notice how the panel behaves differently than the windows and that it does not show in the Window menu.

Alerts

Sometimes you want to display a simple dialog box to the user to notify them of an event or ask a simple yes/no question. You could create and layout a new window every time you wanted to do this, but that is a bit of a hassle. Fortunately, AppKit provides alerts so you can easily provide simple dialog box-like windows. An example of an alert is shown in Figure 8.


Figure 8: Sample alert window

We do need to write code to demonstrate how to use alerts, so let's create an AppDelegate class. Add this action method to your AppDelegate

- (IBAction)showAlertWindow:(id)sender
{
    NSAlert * alert = [[[NSAlert alloc] init] autorelease];
    [alert setAlertStyle:NSWarningAlertStyle];
    [alert setMessageText:@"Message Text"];
    [alert setInformativeText:@"More detailed text."];
    [alert addButtonWithTitle:@"OK"];
    [alert addButtonWithTitle:@"Cancel"];
    
    NSInteger result = [alert runModal];
    if (result == NSAlertFirstButtonReturn)
        NSLog(@"OK pressed");
    else if (result == NSAlertSecondButtonReturn)
        NSLog(@"Cancel pressed");
}

The basic sequence for using alert windows is fairly simple. We need to create an instance of the NSAlert class, setup various parameters, and finally display it. There are three kinds of alert styles, and they represent the severity of the event you are trying to present to the user:

NSInformationalAlertStyle

NSWarningAlertStyle

NSCriticalAlertStyle

These can affect the icon used; for example, a critical alert will display an exclamation mark.

The message text is a short, one-line summary of the situation, for example, "Delete the record?" The informative text can be longer and more detailed, for example, "Deleted records cannot be restored. Are you sure you want to continue?"

You can add buttons to the alert, and they are added to the window from the right and going towards the left. Notice that the "OK" button was added first, and is furthest to the right. Also, the first button added is the default button and is assigned a key equivalent of Return. That's why it is blue in Figure 8. Any button named "Cancel" is assigned a key equivalent of Escape, as well.

With our alert all configured, it's time to display it with the runModal method. It does not return until the user presses a button, and returns the user's choice. It returns a constant based on which order the buttons were added; hence NSAlertFirstButtonReturn corresponds to the "OK" button in this example.

The runModal method displays the alert as a modal window. Modal means that the rest of the user interface is not accessible until the user responds to alert. This means the user cannot interact with any of the other windows or any of the menus. The user cannot even quit your application until they deal with the alert. This kind of behavior is generally frowned upon. You do not want to display modal dialog boxes very often because they disrupt the user's ability to use your application. Use them sparingly.

With our action written, switch to Interface Builder so we can hook it up to a button. We're going to need to instantiate the AppDelegate class and a new button, and then hook up the button to the showAlertWindow: action.

Alert Sheets

There is another variant of an alert that is less intrusive than modal windows called sheets. Sheets are attached to a specific window. When displayed, they animate down from the title bar. You can think of them as modal to a specific window. You cannot interact with the attached window until you respond to the sheet, however other windows and the menus remain responsive. An example of the same alert displayed as a sheet is shown in Figure 9. The sheet has the same information as the alert window, but it is now attached to the main window.


Figure 9: Sample alert sheet

Sheets are a little bit more complicated to use for a couple reasons. First, you need to attach a sheet to a specific window. We're going to use an outlet to the main window, but there are other ways you can get a reference to a window, depending on the situation. Second, displaying a sheet is a two-step process. Let's look at the code:

@synthesize mainWindow = _mainWindow;
- (IBAction)showAlertSheet:(id)sender
{
    NSAlert * alert = [[[NSAlert alloc] init] autorelease];
    [alert setAlertStyle:NSWarningAlertStyle];
    [alert setMessageText:@"Message Text"];
    [alert setInformativeText:@"More detailed text."];
    [alert addButtonWithTitle:@"OK"];
    [alert addButtonWithTitle:@"Cancel"];
    [alert beginSheetModalForWindow:_mainWindow
                      modalDelegate:self
                     didEndSelector:@selector(alertDidEnd:result:contextInfo:)
                        contextInfo:nil];
}
- (void)alertDidEnd:(NSAlert *)alert
             result:(NSInteger)result
        contextInfo:(void *)contextInfo
{
    if (result == NSAlertFirstButtonReturn)
        NSLog(@"OK pressed");
    else if (result == NSAlertSecondButtonReturn)
        NSLog(@"Cancel pressed");
    
}

The basic setup of the NSAlert object is the same; however, we display the sheet with the beginSheetModalForWindow:... method. This method takes several arguments because displaying a sheet asynchronous. This method returns right away and does not wait for the user to respond. Thus, we need to setup a delegate method that gets called when the user finally does respond.

The reason for the change in behavior as compared to runModal has to do with some details of how the AppKit event system is designed. The simple reason is that runModal blocks the entire application from running until the user responds, whereas a sheet only "blocks" a single window. All other windows need to respond normally. By returning just after the sheet is displayed, it allows the system to handle and respond to events for other windows.

The "did end" selector of the modal delegate works in a similar fashion to the target/action behavior of buttons and menus. The selector is called on the modal delegate object and can be named whatever you like. However it must take three arguments: the alert itself, the status, and the context info. The context info is the same as you passed into the beginSheet... method. You can use this to squirrel away some information that may not be available in instance variables.

To test this out, add yet another button to the main window and hook it up to this new action. You should see the sheet appear as shown in Figure 9 and you should see the log statements when the buttons are pressed.

Loading Windows from Nibs

Alerts are great for simple cases, but sometimes you need to create your own custom window. Perhaps you want a preferences window or an info panel or you want to have alerts with more controls on them. In these cases, you'll have to create your own windows, similar to what we did above. However, rather put all auxiliary windows in the main nib, you are better off putting them into their own separate nib. By doing this, you only load the windows as you need them, thus saving memory and resources by not creating windows you may never even display.

Let's go through the steps necessary to create and use a widow that is stored in its own nib. There is a class that's very handy for loading windows from a nib file called NSWindowController. While there are other ways of loading nib files (through NSNib and NSBundle), you should generally want to use a window controller as it takes care of some memory management issues for you. Subclassing NSWindowController also provides a place to put outlets and actions associated with that window.

Start by adding a new file your application. Under the Cocoa section, choose Objective-C NSWindowController subclass, as shown in Figure 10. Set the name of the new class to MyWindowController.


Figure 10: New NSWindowController subclass

We're going to add an action to our AppDelegate class to display a window. Make the header file match Listing 1.

Listing 1: AppDelegate.h

#import <Cocoa/Cocoa.h>
@class MyWindowController;
@interface AppDelegate : NSObject
{
    NSWindow * _mainWindow;
    MyWindowController * _myWindowController;
}
@property (nonatomic, retain) IBOutlet NSWindow * mainWindow;
- (IBAction)showAlertSheet:(id)sender;
- (IBAction)showAlertWindow:(id)sender;
- (IBAction)showWindowFromNib:(id)sender;
@end

I've added an instance variable for _myWindowController. I've also added a showWindowFromNib: action method. Now, let's jump to the implementation file and add the following method:

- (IBAction)showWindowFromNib:(id)sender
{
    if (_myWindowController == nil)
        _myWindowController = [[MyWindowController alloc] init];
    [_myWindowController showWindow:self];
}

You'll have to add an #import "MyWindowController.h" to the top of the file, too. What this does is create a new MyWindowController instance, if we don't already have one. This makes sure our nib is only loaded when it is actually needed. Creating windows like this saves memory and other resources.

The showWindow: method is a method of NSWindowController that shows its associated window. It's very similar to the makeKeyAndOrderFront: method we used earlier, but it takes care of loading the window from the nib and any other housekeeping tasks that need to be done before making the window key.

Back in Interface Builder, add another button to our main window titled Show Nib Window, as shown in Figure 11. Hook up this button's action to the showWindowFromNib: action on the AppDelegate.


Figure 11: Show Nib Window button

Now let's flesh out our window controller subclass. Make the MyWindowController.h file match Listing 2.

Listing 2: MyWindowController.h

#import <Cocoa/Cocoa.h>
@interface MyWindowController : NSWindowController
{
    NSButton * _sampleCheckbox;
}
@property (nonatomic) IBOutlet NSButton * sampleCheckbox;
- (IBAction)toggleCheckbox:(id)sender;
@end

We're going to add a checkbox to the window, so I went ahead and created an outlet for it. I also created an action for the checkbox. The MyWindowController.m file is shown in Listing 3.

Listing 3: MyWindowController.m

#import "MyWindowController.h"
@implementation MyWindowController
@synthesize sampleCheckbox = _sampleCheckbox;
- (id)init
{
    self = [super initWithWindowNibName:@"MyWindow"];
    return self;
}
- (IBAction)toggleCheckbox:(id)sender
{
    NSLog(@"Checkbox state: %d", [_sampleCheckbox state]);
}
@end

The outlet and action are fairly straight forward, and the only interesting bit is the constructor. We're calling NSWindowController's constructor and giving it the name of the nib to load.

Of course, this nib file doesn't exist yet, so let's create a new nib file with a window. You can do this in Xcode by using the New File... menu. Select the Window XIB file all the way at the bottom of the Cocoa section, as shown in Figure 12. Name the new file MyWindow.xib.


Figure 12: New Window XIB

Double click on the new MyWindow.xib file to open it up in Interface Builder so we can customize it. Before adding controls to our window, we should setup File's Owner to represent our window controller. File's Owner is a bit of an oddball object in the nib. It's not freeze dried in the nib like the other objects. It's the object responsible for cleaning up the nib when it's no longer needed and it usually loads the nib, too. By default, the File's Owner class is set to NSObject instance; however, we can customize this. Since the NSWindowController sets itself up to be the owner of the nib, we can change File's Owner to our subclass. Go to the Identity tab and change the class of the object to MyWindowController, as shown in Figure 13.


Figure 13: File's Owner class

This allows us to use File's Owner as a destination for outlets and actions. Resize the window and add a checkbox to it, as shown in Figure 14. Now let's setup the outlets. There are actually two outlets we need to setup. We've got the sampleCheckbox outlet that we created in our subclass, and this needs to be hooked up to the checkbox button. But NSWindowController has its own outlet called window. You need to hook this up so the window controller knows which window to open when showWindow: is called. Finally, hookup the action of the checkbox to the toggleCheckbox: action of File's Owner.


Figure 14: Nib window

We're almost done. We need to tweak the attributes of the window itself. Specifically, we need to uncheck Release On Closed and Visible At Launch and check the One Shot attributes. The window controller takes care of making the window visible, so we do not want it visible at launch. The rationale behind the other two attributes is the same as noted above.


Figure 15: Nib window attributes

And that should be all the changes we need. You should be able to run the application and view the new window. Listing 4 is the full code for the AppDelegate.m file.

Listing 4: AppDelegate.m

#import "AppDelegate.h"
#import "MyWindowController.h"
@implementation AppDelegate
@synthesize mainWindow = _mainWindow;
- (IBAction)showAlertSheet:(id)sender
{
    NSAlert * alert = [[NSAlert alloc] init];
    [alert setAlertStyle:NSWarningAlertStyle];
    [alert setMessageText:@"Message Text"];
    [alert setInformativeText:@"More detailed text."];
    [alert addButtonWithTitle:@"OK"];
    [alert addButtonWithTitle:@"Cancel"];
    [alert beginSheetModalForWindow:_mainWindow
                      modalDelegate:self
                     didEndSelector:@selector(alertDidEnd:result:contextInfo:)
                        contextInfo:nil];
}
- (void)alertDidEnd:(NSAlert *)alert
             result:(NSInteger)result
        contextInfo:(void *)contextInfo
{
    if (result == NSAlertFirstButtonReturn)
        NSLog(@"OK pressed");
    else if (result == NSAlertSecondButtonReturn)
        NSLog(@"Cancel pressed");
    
}
- (IBAction)showAlertWindow:(id)sender
{
    NSAlert * alert = [[[NSAlert alloc] init] autorelease];
    [alert setAlertStyle:NSWarningAlertStyle];
    [alert setMessageText:@"Message Text"];
    [alert setInformativeText:@"More detailed text."];
    [alert addButtonWithTitle:@"OK"];
    [alert addButtonWithTitle:@"Cancel"];
    
    NSInteger result = [alert runModal];
    if (result == NSAlertFirstButtonReturn)
        NSLog(@"OK pressed");
    else if (result == NSAlertSecondButtonReturn)
        NSLog(@"Cancel pressed");
}
- (IBAction)showWindowFromNib:(id)sender
{
    if (_myWindowController == nil)
        _myWindowController = [[MyWindowController alloc] init];
    [_myWindowController showWindow:self];
}
@end

Conclusion

As you can see, it's actually quite easy to display windows, even when using separate nib files. I highly recommend putting each window and panel in it's own nib file. Not only is it more memory efficient, but it also reduces the clutter of the nib. If you put all of your windows and panels in a single nib, it ends up getting confusing very quickly. Also using window controllers helps separate your code into more manageable chunks.

One thing I'm going to leave as an exercise for the reader is displaying custom sheets from a nib file. It turns out that you aren't limited to NSAlert for sheets. You can use any NSWindow as a sheet. You can use the beginSheet:... method of NSApplication. Read up on the Sheets Programming Topics for details on how to do this, which you can find on Apple's Developer Connection website or in the documentation included with Xcode.


Dave Dribin has been writing professional software for over eleven years. After five years programming embedded C in the telecom industry and a brief stint riding the Internet bubble, he decided to venture out on his own. Since 2001, he has been providing independent consulting services, and in 2006, he founded Bit Maki, Inc. Find out more at http://www.bitmaki.com/ and http://www.dribin.org/dave/.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Sibelius 2020.6 - Music notation solutio...
Sibelius is the world's best-selling music notation software for Mac. It is as intuitive to use as a pen, yet so powerful that it does most things in less than the blink of an eye. The demo includes... Read more
Bookends 13.4.2 - Reference management a...
Bookends is a full-featured bibliography/reference and information-management system for students and professionals. Bookends uses the cloud to sync reference libraries on all the Macs you use.... Read more
OmniGraffle Pro 7.16 - Create diagrams,...
OmniGraffle Pro helps you draw beautiful diagrams, family trees, flow charts, org charts, layouts, and (mathematically speaking) any other directed or non-directed graphs. We've had people use... Read more
Drive Genius 6.1.0 - $79.00
Drive Genius features a comprehensive Malware Scan. Automate your malware protection. Protect your investment from any threat. The Malware Scan is part of the automated DrivePulse utility. DrivePulse... Read more
Tor Browser 9.5 - Anonymize Web browsing...
The Tor Browser Bundle is an easy-to-use portable package of Tor, Vidalia, Torbutton, and a Firefox fork preconfigured to work together out of the box. It contains a modified copy of Firefox that... Read more
VueScan 9.7.28 - Scanner software with a...
VueScan is a scanning program that works with most high-quality flatbed and film scanners to produce scans that have excellent color fidelity and color balance. VueScan is easy to use, and has... Read more
OmniGraffle 7.16 - Create diagrams, flow...
OmniGraffle helps you draw beautiful diagrams, family trees, flow charts, org charts, layouts, and (mathematically speaking) any other directed or non-directed graphs. We've had people use Graffle to... Read more
WALTR 2 2.6.26 - $39.95
WALTR 2 helps you wirelessly drag-and-drop any music, ringtones, videos, PDF, and ePub files onto your iPhone, iPad, or iPod without iTunes. It is the second major version of Softorino's critically-... Read more
Airmail 4.1 - Powerful, minimal email cl...
Airmail is an mail client with fast performance and intuitive interaction. Support for iCloud, MS Exchange, Gmail, Google Apps, IMAP, POP3, Yahoo!, AOL, Outlook.com, Live.com. Airmail was designed... Read more
Iridient Developer 3.3.12 - Powerful ima...
Iridient Developer (was RAW Developer) is a powerful image-conversion application designed specifically for OS X. Iridient Developer gives advanced photographers total control over every aspect of... Read more

Latest Forum Discussions

See All

Willy Jetman: Astromonkey's Revenge...
Barcelona-based developer Last Chicken Games are set to bring their game Willy Jetman: Astromonkey's Revenge to both iOS and Android on 9th July. The Metroidvania is already available on the likes of PS4, Switch and PC but now mobile folk will be... | Read more »
The 5 Best Mobile Real Time Strategy Gam...
Real-time strategy games feel like they’d be a perfect fit for mobile, but they’re trickier to pull off that you might think. The traditional mold of base-building and micro management can work on touch screens, but needs to be carefully honed so... | Read more »
Using your phone in a protest
I can't write about games today. There is a struggle happening in the streets right now and it needs everyone's attention. Here's some good info on how you can use your iOS device safely amidst a protest. | Read more »
Dungonian is a card-based dungeon crawle...
Dungonian is a card-based dungeon crawler from developer SandFish Games that only recently launched as a free-to-play title. It offers an extensive roster of playable heroes to collect and enemies to take down, and it's available right now for iOS... | Read more »
Steam Link Spotlight - Signs of the Sojo...
Steam Link Spotlight is a feature where we look at PC games that play exceptionally well using the Steam Link app. Our last entry was XCOM: Chimera Squad. Read about how it plays using Steam Link's new mouse and keyboard support over here. | Read more »
Steampunk Tower 2, DreamGate's sequ...
Steampunk Tower 2 is a DreamGate's follow up to their previous tower defence game. It's available now for both iOS and Android as a free-to-play title and will see players defending their lone base by kitting it out with a variety of turrets. [... | Read more »
Clash Royale: The Road to Legendary Aren...
Supercell recently celebrated its 10th anniversary and their best title, Clash Royale, is as good as it's ever been. Even for lapsed players, returning to the game is as easy as can be. If you want to join us in picking the game back up, we've put... | Read more »
Pokemon Go Fest 2020 will be a virtual e...
Niantic has announced that Pokemon Go Fest will still take place this year although understandably it won't be a physical event. Instead, it will become a virtual celebration and is set to be held on 25th and 26th July. [Read more] | Read more »
Marvel Future Fight's major May upd...
Marvel Future Fight's latest update has now landed, and it sounds like a big one. The focus this time around is on Marvel's Guardians of the Galaxy, and it introduces all-new characters, quests, and uniforms for players to collect. [Read more] | Read more »
SINoALICE, Yoko Taro and Pokelabo's...
Yoko Taro and developer Pokelabo's SINoALICE has now opened for pre-registration over on the App Store. It's already amassed 1.5 million Android pre-registrations, and it's currently slated to launch on July 1st. [Read more] | Read more »

Price Scanner via MacPrices.net

Apple restocks 27″ iMacs, Certified Refurbish...
Apple has restocked Certified Refurbished 2019 27″ iMacs starting at $1529 and up to $350 off the cost of new models. Apple’s one-year warranty is standard, shipping is free, and each iMac features a... Read more
Apple’s new 2020 13″ 4-Core MacBook Airs on s...
B&H Photo has Apple’s new 2020 13″ 4-Core MacBook Airs on sale today for $100 off Apple’s MSRP, only $1199. Expedited shipping is free to many addresses in the US. The MacBook Airs are the same... Read more
New Verizon promo: $150 off any Apple Watch w...
Verizon is offering $150 off any Apple Watch when purchased alongside an iPhone through June 10, 2020. They’re also offering up to $100 on any Apple Watch trade-in. Here are the details: “Get $150... Read more
Last year’s 13″ 2.4GHz MacBook Pros are avail...
Apple has Certified Refurbished 2019 13″ 2.4GHz/256GB 4-Core Touch Bar MacBook Pros available for $1359, $440 off original MSRP. Apple’s one-year warranty is included, shipping is free, and each... Read more
Apple’s new 2020 13″ MacBook Pros on sale for...
Apple reseller Abt Electronics has new 2020 13″ MacBook Pros on sale today for up to $140 off MSRP, starting at $1208. Shipping is free, and most configurations are in stock today. Note that Abt’s... Read more
Apple CEO Reacts To Nationwide Protests Over...
NEWS: 06.03.20 – With the recent death of a black man in the custody of a white police officer igniting outrage among Americans from all walks of life, which resulted in protests and civil unrest... Read more
At up to $420 off MSRP, these Certified Refur...
Apple has Certified Refurbished 2019 16″ MacBook Pros available for up to $420 off the cost of new models, starting at $2039. Each model features a new outer case, shipping is free, and an Apple 1-... Read more
Apple restocks refurbished 3rd generation 12....
Apple restocked select 3rd generation 12.9″ WiFi iPad Pros starting at only $699 and up to $330 off original MSRP. Each iPad comes with a standard Apple one-year warranty, outer cases are new, and... Read more
These wireless carriers are offering Apple’s...
Looking for a deal on Apple’s new iPhone SE? Apple itself offers the unlocked 64GB iPhone SE for $399 or $16.62/month. If you’re willing to try a new carrier, two of Apple’s wireless carriers are... Read more
Save $80 on the 64GB 10.5″ iPad Air with this...
Apple has 10.5″ 64GB WiFi iPad Airs models available for $80 off MSRP, Certified Refurbished. Each iPad comes with Apple’s standard one-year warranty and includes a new outer case. Shipping is free... Read more

Jobs Board

*Apple* Architect - SAIC (United States)
**Description** We are currently seeking a motivated, career and customer oriented Apple Architect to join our team in Washington, DC to begin an exciting and Read more
*Apple* Support Engineer - SAIC (United Stat...
**Description** We are currently seeking a motivated, career and customer oriented Apple Support Engineer to join our team in Washington, DC to begin an exciting and Read more
Perioperative RN - ( *Apple* Hill Surgical C...
Perioperative RN - ( Apple Hill Surgical Center) Tracking Code 60593 Job Description Monday - Friday - Full Time Days Possible Saturdays General Summary: Under the Read more
Senior Practice Manager - *Apple* Hill Eye...
Senior Practice Manager - Apple Hill Eye Center Tracking Code 61713 Job Description Apple Hill Medical Center General Summary: Under general supervision, manages Read more
*Apple* Mac Desktop Support - Global Dimensi...
…Operate and support an Active Directory (AD) server-client environment for all Apple devices operating on the BUMED network + Leverage necessary industry enterprise Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.