TweetFollow Us on Twitter

Oct 98 Getting Started

Volume Number: 14 (1998)
Issue Number: 10
Column Tag: Getting Started

Animation

by Dave Mark and Dan Parks Sydow

How a Mac program generates smooth, flicker-free animation

After learning the basics of programming, the new programmer invariably raises the question of how to go about including animated effects in his or her application. This is true regardless of the programmer's system of choice, but it's especially the case among those interested in programming the Mac. From its inception, the Macintosh has been the machine that brought computers and graphics together for the masses. Mac users love to see animation, and Mac programmers love to include animation in their programs. Now that previous columns have covered the basics of the Macintosh Toolbox, it's time to move on to the basics of animation. This column covers black and white animation using QuickDraw bitmaps. A couple of columns down the road (after we cover Color QuickDraw), we'll revisit this month's technique and see how to adapt it for use in color animation.

The Wrong Way to Animate

If you've ever attempted to write an arcade-style game, you've probably tried your hand at moving an object over a background. If you found that the moving object obscured the background as it moved across it you were certainly less then thrilled. The same holds true if the object appeared to flicker incessantly as it traveled its path. If you're very familiar with these failed animation methods (we all went through the process as we learned how to do it right!), you'll be pleased to find out that while the results were less than ideal, the methods were close to being correct. As such, taking a brief look at the incorrect ways of performing animation serve as a worthwhile prelude to a discussion of how to properly perform computer animation.

A programmer attempting animation for the first time often does so by drawing a background picture in a window and then drawing a foreground object atop the background. Invariably, the attempt at animation then involves repeatedly offsetting the foreground object a bit and redrawing it. While the foreground object will appear to move across the background, the result is unacceptable - a part of each previously drawn image remains exposed, leaving a blurry trail behind the moving object. Figure 1 illustrates this for a cartoon vacuum moving over a gray background (hey, every game doesn't have to be a shoot 'em up, right?).

Figure 1. The result of animating without regard for clearing the background.

Having seen the folly of his ways, the inexperienced animator next tries to remedy the situation by redrawing the background image so as to cover any old foreground image remnant. The foreground image is then offset a bit and redrawn over the fresh background. This corrects the problem of foreground image "leftovers," but introduces the new problem of flicker. When the background image is redrawn in order to cover up the foreground image, it obscures the entire drawing. Taking a snapshot of a few "frames" of this type of animation results in the three views of one window shown in Figure 2. Looking from left to right at this figure reveals that the object has moved along a bit, but in between the movement the user will notice the momentarily blanking of the window.

Figure 2. Clearing the background between foreground redrawing creates flicker.

The Offscreen Bitmap Solution

As you know from previous Getting Started columns, most programs deal with repainting a window's content by responding to updateEvts generated by the Window Manager. When an area of a window that was previously obscured needs to be redrawn, the Window Manager adds the newly revealed area to the window's update region and generates an updateEvt for the window. The technique of relying on update events to refresh a window's content area works fine for programs that don't need to incorporate smooth, fast, animated effects. When it comes to animation, the problem with this approach is that update events take time. It takes time for the Window Manager to calculate the update region and it takes time to post an event. More importantly, it takes time for your program to respond to an update event. If your program is busy responding to another event, the update event might sit in the queue for a while, leaving the window undrawn until you get around to fixing it.

When you've got a rocket ship shooting across a planet's surface, you don't want to leave any holes in the planet, waiting for your program to respond to an update event. You want to fill in the holes in real time, just like the System does when it handles your cursor.

Fortunately, the previously discussed problem of flicker and the just-mentioned problem of slow updating are both easily overcome by employing a programming technique known as offscreen bitmap animation. A bitmap is one way to represent a graphical object. On the Mac, a bitmap is a representation of a monochrome (black and white) image. The map is a grid of pixels, with each pixel considered either on or off. In specifying the state of each pixel (which in memory is represented by a single bit) in a given area, you define a single image, or picture. After defining a bitmap, your next task is to animate it. That is, your program needs to smoothly move it over a background in a window.

Your Mac's cursor is an example of a bitmap, and the cursor's movement is a perfect instance of bitmap animation. As the cursor moves around the screen, it appears to float over the background without flickering. Consider the sequence of pictures in Figure 3.

Figure 3. The movement of the cursor provides an example of smooth animation.

The leftmost part of Figure 3 shows an arrow cursor partially obscuring a hard drive icon. Once the cursor moves, it leaves an area of the hard drive icon undrawn - as shown in the center part of the figure. As shown in the rightmost part of Figure 3, before this "hole" gets noticed, the system fills it back up with its previous contents.

Implementing the Offscreen Bitmap Technique

An offscreen bitmap is a bitmap that is drawn in memory, but does not appear on screen. To achieve an animated effect, a total of three offscreen bitmaps are needed. One of these bitmaps holds a background image - the image over which a foreground image will appear to traverse. A second bitmap holds the foreground image. The third offscreen bitmap is a combination of the previous two offscreen bitmaps. This combined, or master, bitmap serves as a mixing board of sorts. Only after the foreground and background are combined in the master bitmap does anything get displayed in a window for the user to see. What gets displayed is a copy of the master bitmap. As shown in Figure 4, it is the only one of the three offscreen bitmaps that gets copied from memory to a window.

Figure 4. Using offscreen bitmaps in memory to create one "frame" of an animated sequence.

Animation begins by copying the background bitmap to the mixer, then copying the foreground bitmap to the same mixer. The mixer bitmap is then copied to a window. This combining of the foreground and background bitmaps, and the subsequent displaying of the result in a window, is performed repeatedly within a loop. In each pass through the loop there should be a slight shift in the position of the foreground image relative to the background image. The result of executing this loop is that the foreground image appears to be moving. Animation is achieved. And because the new image that's displayed in each pass through the loop is created behind the scene in memory rather than in view of the user on-screen, there is a minimum of flicker.

BitMapper

In this month's program we create three offscreen bitmaps to accomplish the animated effect of a hand moving smoothly across a window. As the user drags the mouse, the hand follows. The hand appears to track the cursor, floating along on top of the gray background. Figure 5 shows what the user of the BitMapper program sees.

Figure 5. The BitMapper window.

The floating hand is our foreground bitmap image. The framed gray pattern is the background bitmap image. As you move the mouse, the hand appears to float over the gray background, just like a cursor. What the user is seeing is the mixer bitmap - the copied version of the offscreen bitmap that represents the combining of the foreground hand bitmap with the background gray bitmap.

Creating the BitMapper Resources

To get under way, move into your CodeWarrior development folder and create a folder named BitMapper. Launch ResEdit and create a new resource file named BitMapper.rsrc inside the BitMapper folder.

You need to create two PICT resources - one to serve as the foreground image and one to act as the background image. In the BitMapper source code we'll be referencing these resources by IDs of 128 and 129, so make sure you assign those values to the pictures. In particular, give the background PICT an ID of 128 and the foreground PICT an ID of 129. You don't have to use the same pictures we're using, but you do have to make sure that PICT 128 is larger than PICT 129 so that the background is larger than the foreground!

If you've got a graphics program like ClarisDraw, CorelDraw, or Canvas, create your background by drawing a nice frame, then pasting another image inside it. Copy the whole thing to the clipboard, then paste it inside ResEdit. For the foreground, you'll want something relatively small. Use whatever image you like, but be sure to make it resource ID 129. Note that both images should be black and white only, and not color or grayscale. You can use a color image, but all colored pixels will be translated to black, so things might not come out as you planned them to.

The only other resource needed is a WIND that will define the look and position of the window that is to display the animation. If you've created a background image that is uniform (such as the solid gray image we're using), the window doesn't have to be the size of the background image - when the background image is scaled to the size of the window it won't be distorted. If you create a background image that isn't consistent (perhaps the image is a landscape with trees and so forth), you'll want to make the window's size the same as the image.

That's it for the BitMapper.rsrc file. Now quit ResEdit, making sure to first save your changes.

Creating the BitMapper Project

Launch CodeWarrior and create a new project based on the MacOS:C_C++:MacOS Toolbox:MacOS Toolbox Multi-Target stationary. You've already created a project folder, so uncheck the Create Folder check box. Name the project BitMapper.mcp and specify that the project be placed in the BitMapper folder.

In the newly created project window you'll want to remove the SillyBalls.c and SillyBalls.rsrc files and add the BitMapper.rsrc file. Our BitMapper project doesn't make use of any of the standard ANSI libraries, so go ahead and remove the ANSI Libraries folder.

Next, choose New from the File menu to create a new, empty source code window. Save it with the name BitMapper.c. Add the new file to the project by choosing Add Window from the Project menu. The full source code listing for the BitMapper program appears next in the source code walk-through. You can type it into the BitMapper.c file as you read the walk-through, or you can save some typing by downloading the whole BitMapper project from MacTech's ftp site at ftp://ftp.mactech.com/src/mactech/volume14_1998/14.10.sit.

Walking Through the Source Code

As with previous projects, BitMapper starts off with some constant definitions.

/********************* constants *********************/

#define         kMoveToFront               (WindowPtr)-1L
#define         kWINDResID                  128

const short   kBackgroundPictID =      128;
const short   kForegroundPictID =      129;

These are followed by BitMapper's function prototypes.

/********************* functions *********************/

void        ToolBoxInit( void );
WindowPtr   WindowInit( void );
PicHandle   LoadPicture( short resID );
GrafPtr     CreateBitMap( const Rect *rPtr );

The main() function starts off with several local variable declarations and a call to the routine that initializes the Toolbox.

/************************ main ***********************/

void   main( void )
{
   Rect            r;
   GrafPtr      backPortPtr, forePortPtr, mixerPortPtr;
   WindowPtr   window;
   PicHandle   backPict, forePict;
   Point         p;
   
   ToolBoxInit();

Next, a window is created. The application-defined routine WindowInit(), which is discussed ahead, returns a WindowPtr that is stored in the local variable window. A call to ShowWindow() ensures that the window becomes visible onscreen.

   window = WindowInit();
   ShowWindow(window);

Next, the background PICT is loaded. The frame of the PICT (its bounding rectangle) is normalized (its size is left the same, but it's repositioned so that its top and left coordinates are both 0).

   backPict = LoadPicture( kBackgroundPictID );
   r = (**backPict).picFrame;
   OffsetRect( &r, -r.left, -r.top );

This normalized Rect is passed on to the application-defined routine CreateBitMap(). CreateBitMap(), discussed below, creates an offscreen GrafPort the size of the specified Rect. You're used to working with a GrafPort that's associated with a window (every window has one). Even though the newly created GrafPort isn't linked to any window, it can be drawn to - just like a window's GrafPort. You can use SetPort() on it, as well as all the standard QuickDraw routines such as DrawString() and DrawPicture(). While your drawing won't appear on the screen, the drawing will affect the memory used to implement the GrafPort.

   backPortPtr = CreateBitMap( &r );

CreateBitMap() returns a pointer to the newly created GrafPort. When CreateBitMap() returns, this port is made the current port. That means any subsequent port-altering calls will affect the GrafPort referenced by backPortPtr. Next, DrawPicture() is called to draw the background PICT in the background GrafPort.

   DrawPicture( backPict, &r );

Next, the master GrafPort is created. This GrafPort is used to merge the foreground PICT with the background PICT. Once again, when this call to CreateBitMap() returns, the new GrafPort is the current port.

   mixerPortPtr = CreateBitMap( &r );

Just as we did with the background PICT, this next sequence of code loads the foreground PICT, creates a normalized bounding Rect, and finally creates a GrafPort for the foreground PICT.

   forePict = LoadPicture( kForegroundPictID );
   r = (**forePict).picFrame;
   OffsetRect( &r, -r.left, -r.top );
   
   forePortPtr = CreateBitMap( &r );

The call of CreateBitMap() leaves forePortPtr as the current port. Next, DrawPicture() is used to draw the foreground picture in this newly created GrafPort.

   DrawPicture( forePict, &r );

OK. That's about all the preliminary stuff. Now we're ready to animate. Before we do, though, we'll use HideCursor() to make the cursor invisible so that our foreground hand picture can be made to take the place of the normal pointer cursor.

   HideCursor();

Next, we'll enter a loop, waiting for the mouse button to be clicked.

   while ( !Button() )
   {

At the heart of our program is the CopyBits() Toolbox routine. CopyBits() copies one QuickDraw BitMap to another. A BitMap is the Toolbox data structure that holds one bitmap - we'll get into the BitMap data structure a little later on. This call to CopyBits() copies the background BitMap into the mixer BitMap, using the bounding rectangle associated with each of the BitMaps. The srcCopy parameter specifies how the BitMap is copied. The srcCopy parameter tells CopyBits() to replace all bits in the destination BitMap's rectangle with the bits in the source BitMap.

      CopyBits(   &(backPortPtr->portBits), 
                     &(mixerPortPtr->portBits),
                     &(backPortPtr->portBits.bounds),
                     &(mixerPortPtr->portBits.bounds),
                     srcCopy, nil );

Next, we get the current position of the mouse, in global coordinates.

      GetMouse( &p );

Now set the port to the BitMapper window, then convert the mouse position to the window's local coordinates.

      SetPort( window );
      GlobalToLocal( &p );

Next, the foreground BitMap's bounding rectangle is copied to a local variable, r, and offset by the mouse's position. Basically, r is the same size as the foreground BitMap (the pointing hand), positioned on the background BitMap (which is the same size as the window) according to the current location of the mouse.

      r = forePortPtr->portBits.bounds;
      OffsetRect( &r, p.h, p.v );

Now the foreground BitMap is copied to the mixer BitMap, using r as the destination bounding rectangle. Notice the use of srcOr instead of srcCopy. This makes the foreground BitMap transparent. To see the effect this has, try changing the srcOr to srcCopy.

      CopyBits(   &(forePortPtr->portBits), 
                     &(mixerPortPtr->portBits),
                     &(forePortPtr->portBits.bounds), 
                     &r, srcOr, nil );

Finally, the mixer BitMap is copied to the window. In short, the loop works like this: Build the window's image offscreen, then copy the combined image to the window.

      CopyBits(   &(mixerPortPtr->portBits), 
                     &(window->portBits),
                     &(mixerPortPtr->portBits.bounds), 
                     &(window->portRect),
                     srcCopy, nil );
   }
}

ToolBoxInit() is the same as it ever was...

/******************** ToolBoxInit ********************/

void   ToolBoxInit( void )
{
   InitGraf( &qd.thePort );
   InitFonts();
   InitWindows();
   InitMenus();
   TEInit();
   InitDialogs( 0L );
   InitCursor();
}

WindowInit() loads the background PICT, copying its framing rectangle into r.

/********************* WindowInit ********************/

WindowPtr   WindowInit( void )
{
   WindowPtr   window;
   PicHandle   pic;
   Rect        r;
   short       pictWidth;
   short       pictHeight;
   
   pic = LoadPicture( kBackgroundPictID );
   r = (**pic).picFrame;

From variable r the width and height of the background picture are determined.

   pictWidth  = r.right - r.left;
   pictHeight = r.bottom - r.top;

Next, a new window is created from the WIND resource. A call to SizeWindow() resizes the window to match the size of the just-loaded background picture.

   window = GetNewWindow( kWINDResID, nil, kMoveToFront);
   SizeWindow( window, pictWidth, pictHeight, true);

WindowInit() ends by returning the window pointer variable WindowPtr to the calling routine.

   return( window );
}

LoadPicture() loads the specified PICT resource. If the PICT isn't found, LoadPicture() beeps once, then exit.

/******************** LoadPicture ********************/

PicHandle   LoadPicture( short resID )
{
   PicHandle   picture;
   
   picture = GetPicture( resID );

   if ( picture == nil )
   {
      SysBeep( 10 );
      ExitToShell();
   }
}

CreateBitMap() creates a new GrafPort the size of the specified Rect. A BitMap is a QuickDraw data structure designed to hold a bitmap of an image one pixel deep (that is, capable of only displaying either black or white).

/******************* CreateBitMap ********************/

GrafPtr CreateBitMap( const Rect *rPtr )
{
   short        i;
   BitMap       *bPtr;
   GrafPtr      g;

First, a new GrafPort is allocated using NewPtr(). If the memory couldn't be allocated, beep once.

   g = (GrafPtr)NewPtr( sizeof(GrafPort) );
   if ( g == nil )
      SysBeep( 10 );

Next, a BitMap data structure is allocated. Again, if the memory was not allocated, beep once. These beeps aren't really effective - they're put in place as a weak substitute for error checking. You'll want to weave more adequate memory allocation failure handling into your overall error-handling scheme.

   bPtr = (BitMap *)NewPtr( sizeof( BitMap ) );
   if ( bPtr == nil )
      SysBeep( 10 );

Next, the specified rectangle is copied into the BitMap's bounds field. This field specifies the coordinates bounding the BitMap.

   bPtr->bounds = *rPtr;

The rowBytes field indicates how many bytes are used to store one row of the BitMap. For example, 0 through 8 pixels can be stored in 1 byte, 9 through 16 pixels in 2 bytes, etc.

   bPtr->rowBytes = (rPtr->right - rPtr->left + 7) /8;

Next, i is set to the number of rows in the bounding rectangle, and i * rowBytes bytes are allocated for the bit image itself. Again, if the memory was not allocated, beep once.

   i = rPtr->bottom - rPtr->top;
   bPtr->baseAddr = NewPtr( bPtr->rowBytes * i );

   if ( bPtr->baseAddr == nil )
      SysBeep( 10 );

Next, OpenPort() is called to initialize the new GrafPort, which is pointed to by g. OpenPort() leaves g as the current port. SetPortBits() ties the specified BitMap to the current port.

   OpenPort( g );
   SetPortBits( bPtr );

Finally, we return a pointer to the newly allocated GrafPort.

   return( g );
}

Running BitMapper

Run BitMapper by selecting Run from the Project menu. Once your code compiles, a window should appear with your background PICT drawn in it. The window will be the exact size of the background PICT.

As you move the mouse, the foreground PICT should follow the mouse's movement. Click the mouse to exit the program.

Till Next Month...

This sample code should get you on your way to successful offscreen bitmap animation. You can get more information on bitmaps and the BitMap data type in the Basic QuickDraw chapter of the Imaging With QuickDraw volume of Inside Macintosh. Once you've mastered this technique, you're ready to tackle color animation by using PixMaps and the Toolbox routine CopyPixMap(). As mentioned at the beginning of the column, we'll get to PixMap animation, but first we'll have to cover the basics of programming with Color QuickDraw. If you're anxious to prepare for next month's article, peruse the Color QuickDraw chapter of Inside Macintosh: Imaging With QuickDraw.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

TunnelBear 3.0.1 - Subscription-based pr...
TunnelBear is a subscription-based virtual private network (VPN) service and companion app, enabling you to browse the internet privately and securely. Features Browse privately - Secure your data... Read more
ExpanDrive 5.4.0 - Access cloud storage...
ExpanDrive builds cloud storage in every application, acts just like a USB drive plugged into your Mac. With ExpanDrive, you can securely access any remote file server directly from the Finder or... Read more
Opera 40.0.2308.62 - High-performance We...
Opera is a fast and secure browser trusted by millions of users. With the intuitive interface, Speed Dial and visual bookmarks for organizing favorite sites, news feature with fresh, relevant content... Read more
Apple iOS 10.0.2 - The latest version of...
iOS 10 is the biggest release of iOS ever. A massive update to Messages brings the power of the App Store to your conversations and makes messaging more personal than ever. Find your route with... Read more
Herald 7.0 - Notification plugin for Mai...
Note: Versions 2.1.3 (for OS X 10.7), 3.0.6 (for OS X 10.8), 4.0.8 (for OS X 10.9), 5.0.2 (for OS X 10.10), and 6.0.3 (for OS X 10.11) are no longer supported by the developer. Herald is a... Read more
Postbox 5.0.2 - Powerful and flexible em...
Postbox is a new email application that helps you organize your work life and get stuff done. It has all the elegance and simplicity of Apple Mail, but with more power and flexibility to manage even... Read more
Yummy FTP Pro 1.11.10 - $29.99
Yummy FTP Pro is an advanced Mac file transfer app which provides a full-featured professional toolkit combined with blazing speeds and impeccable reliability, so whether you want to transfer a few... Read more
Tidy Up 4.1.17 - $29.99
Tidy Up is a full-featured duplicate finder and disk-tidiness utility. Features With Tidy Up you can search for duplicate files and packages by the owner application, content, type, creator,... Read more
Logitech Control Center 3.9.5 - Keyboard...
Logitech Control Center (LCC) is designed to support OS X and allows you to take full advantage of your Logitech keyboard, mouse, or trackball. With the LCC you can: Browse the Internet using... Read more
Macgo Blu-ray Player 2.16.17 - Blu-ray p...
Macgo Blu-ray Player can bring you the most unforgettable Blu-ray experience on your Mac. Overview Macgo Mac Blu-ray Player can satisfy just about every need you could possibly have in a Blu-ray... Read more

How to build and care for your team in D...
Before you hit the trail and become a dog sledding legend, there’s actually a fair bit of prep work to be done. In Dog Sled Saga, you’re not only racing, you’re also building and caring for a team of furry friends. There’s a lot to consider—... | Read more »
How to win every race in Dog Sled Saga
If I had to guess, I’d say Dog Sled Saga is the most adorable racing game on the App Store right now. It’s a dog sled racing sim full of adorable, loyal puppies. Just look at those fluffy little tails wagging. Behind that cute, pixelated facade is... | Read more »
Let the war games commence in Gunship Ba...
Buzz Lightyear famously said, “This isn’t flying, this is falling – with style!” In the case of Gunship Battle: Second War, though, this really is flying - with style! The flight simulator app from Joycity puts you in control of 20 faithfully... | Read more »
How to get a high score in Fired Up
Fired Up is Noodlecake Games’ high score chasing, firefighting adventure. You take control of a wayward firefighter who propels himself up the side of a highrise with blasts of water. Sound silly? It is. It’s also pretty difficult. You can’t... | Read more »
NBA 2K17 (Games)
NBA 2K17 1.0 Device: iOS iPhone Category: Games Price: $7.99, Version: 1.0 (iTunes) Description: Following the record-breaking launch of NBA 2K16, the NBA 2K franchise continues to stake its claim as the most authentic sports video... | Read more »
Dog Sled Saga (Games)
Dog Sled Saga 1.0.1 Device: iOS Universal Category: Games Price: $3.99, Version: 1.0.1 (iTunes) Description: A game by Dan + Lisa As a rookie musher, foster a dogsledding team whose skills will grow if they're treated right. Week by... | Read more »
60 Seconds! Atomic Adventure (Games)
60 Seconds! Atomic Adventure 1.2 Device: iOS Universal Category: Games Price: $2.99, Version: 1.2 (iTunes) Description: 60 Seconds! is a dark comedy atomic adventure of scavenge and survival. Collect supplies and rescue your family... | Read more »
Tons of Bullets! (Games)
Tons of Bullets! 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Tons of BulletsA retro 2D pixel platformer infused with tons of bullets and tons of features!Fight as Kenji the Ninja and... | Read more »
Sorcery! 4 (Games)
Sorcery! 4 1.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0 (iTunes) Description: *** PLEASE NOTE: Sorcery! 4 requires a minimum of iPad 3 or iPhone 4 *** An epic adventure through a cursed citadel of monsters,... | Read more »
Building the perfect Animation Throwdown...
Animation Throwdown is a casual card game full of plenty of inside jokes from some of your favorite cartoons. It’s accessible as far as card games go, but that’s not to say that it hands your victories to you easily. There’s quite a bit that goes... | Read more »

Price Scanner via MacPrices.net

Duplicate Sweeper Free On Mac App Store For O...
To celebrate the launch of Apple’s latest macOS Sierra, Stafford, United Kingdom based Wide Angle Software has announced that its duplicate file finder software, Duplicate Sweeper, is now available... Read more
13-inch Retina MacBook Pros on sale for up to...
B&H Photo has 13″ Retina Apple MacBook Pros on sale for up to $150 off MSRP. Shipping is free, and B&H charges NY tax only: - 13″ 2.7GHz/128GB Retina MacBook Pro: $1174.99 $125 off MSRP - 13... Read more
Evidence Surfaces Pointing To New A10X Chip F...
Citing a job description for a Project Lead position at Apple’s Austin, Texas engineering labs, Motley Fool’s Ashraf Eassa deduces that development is progressing well on Apple’s next-generation in-... Read more
Check Print’R for macOS Allows Anyone to Easi...
Delaware-based Match Software has announced the release and immediate availability of Check Print’R 3.21, an important update to their easy-to-use check printing application for macOS. Check Print’R... Read more
Apple refurbished 11-inch MacBook Airs availa...
Apple has Certified Refurbished 11″ MacBook Airs (the latest models), available for up to $170 off the cost of new models. An Apple one-year warranty is included with each MacBook, and shipping is... Read more
Apple refurbished 15-inch Retina MacBook Pros...
Apple has Certified Refurbished 2015 15″ Retina MacBook Pros available for up to $380 off the cost of new models. An Apple one-year warranty is included with each model, and shipping is free: - 15″ 2... Read more
Major Version 5 Release Of Free myTuner Radio...
AppGeneration Software has announced the release of myTuner Radio 5.0.1, an important update to their live radio app for iOS and Android platforms. With myTuner Radio, you can listen to more than 30,... Read more
TheAppStore Searches And Filters iOS And macO...
Boston based InchWest has announced the official launch of TheAppStore, a free web based service that lets users search iOS and Mac apps on the web without iTunes or the App store. TheAppStore... Read more
2TB Time Capsule on sale for $199, save $100
Amazon has 2TB Apple Time Capsules on sale for $199 including free shipping. Their price is $100 off MSRP. Choose Amazon as the seller when making your purchase, rather than a third party seller. Read more
12-inch 1.2GHz Rose Gold Retina MacBook on sa...
Amazon.com has the 2016 12″ 1.2GHz Rose Gold Retina MacBook on sale for $1254.02 including free shipping. Their price is $345 off MSRP, and it’s the lowest price available for this model from any... Read more

Jobs Board

Senior *Apple* Administrator - Pratt Instit...
POSITION SUMMARY: Directs the coordination and standardization of campus-wide Apple systems, including planning, analysis and implementation of Apple -related Read more
Restaurant Manager (Neighborhood Captain) - A...
…in every aspect of daily operation. WHY YOU'LL LIKE IT: You'll be the Big Apple . You'll solve problems. You'll get to show your ability to handle the stress and Read more
*Apple* Retail - Multiple Positions- Napervi...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
US- *Apple* Store Leader Program - Apple (Un...
…Summary Learn and grow as you explore the art of leadership at the Apple Store. You'll master our retail business inside and out through training, hands-on Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.