TweetFollow Us on Twitter

Dec 01 Mac OS X

Volume Number: 17 (2001)
Issue Number: 12
Column Tag: Mac OS X

by Andrew Stone

Cocoamotion

Getting more for less with Cocoa.

Ever since ‘89 I’ve been developing with the precursor to Cocoa, NeXTStep & OpenStep. In each release, new features and new concepts have been added to the Application and related frameworks. making life even easier for the third party developer. The more reusable software components that Apple provides, the less code I have to write and maintain. Imagine being able to remove more and more code from your applications! Today I removed hundreds of lines of code in my Image drawing class by using the new NSImage drawing methods which respect the current transformation matrix.

This article goes over a few ways to eradicate code that you may have accrued by using new objects and new features in the old ones. We’ll look at a simplified Image drawing object, the NSStepper, and the fairly new drag methods in NSOutlineView and NSTableView.

NSImage Gets An Upgrade

Transformations

Before Mac OS X 10.0, NSImage would draw ignoring the current transformation matrix. Now this is fine if you have a simple list of graphics - but if you have nested layers of scaled, rotated, skewed groups, it’s a royal pain in the patooti to micromanage image drawing based on depth of a hierarchy!

For compatibility with older applications, the existing NSImage drawing methods such as compositeToPoint: alway draw with only the origin of the image transformed. The image itself is drawn ignoring scale and rotation transforms with the origin at the lower left. While it has been possible to draw with the current transform by getting one of the image’s representation and calling it’s draw method, two new methods have been added to NSImage that do this for you.

- (void)drawAtPoint:(NSPoint)point fromRect:(NSRect)fromRect operation:(NSCompositingOperation)op 
  fraction:(float)delta;
  
- (void)drawInRect:(NSRect)rect fromRect:(NSRect)fromRect operation:(NSCompositingOperation)op 
  fraction:(float)delta;

If you pass in NSZeroRect for the fromRect: parameter, then the whole image is used. For drawInRect:, the image will be scaled to fit in the destination rectangle as well as transformed with the context’s current transform. Please note that if you use these routines in a flipped view, you will need to ‘undo’ the negative y scale factor and adjust the origin before calling the new methods:

      // make a new transform:
                NSAffineTransform *t = [NSAffineTransform transform];
      
      // by scaling Y negatively, we effectively flip the image:
                [t scaleXBy:1.0 yBy:-1.0];
      
      // but we also have to translate it back by its height:
                [t translateXBy:0.0 yBy:-_bounds.size.height];
      
      // apply the transform:
                [t concat];

Now you can have groups of images that are scaled and skewed, and these transformations will automatically be correctly applied to the image when it is rendered. Total lines of code: 5. Number of lines of code replaced: 250. Improvement of rendering speed: 2x. Reliability and correctness were improved greatly.

Transparency:

Using these new methods, you can add a slider to your interface which controls the transparency of the image, because the new methods take a fractional dissolve, from 0.0 fully transparent to 1.0 fully opaque. Let’s say you store the amount of transparency as a float in _dissolve - then your call to render the image would be:

   // we have translated already to the origin of the image in its current group
   // _dissolve varies from 0 for no dissolve to 1 for fully transparent:
            [image drawInRect:NSMakeRect(0.0,0.0,_bounds.size.width,_bounds.size.height) 
            fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0 - _dissolve];

PDF Optimization:

In 10.1 and earlier, there is an “over-optimization” bug in Apple’s implementation of the NSPDFImageRep whereby it draws from its bitmap cache in certain circumstances which are incorrect. For example, when you print, you want the full resolution PDF, not a standin bitmap image which will produce jaggies. Also, when you zoom in, you want the PDF to render beautifully at that scale. You might want to test if you are printing or saving or at a high level zoom, and render the image directly from the PDF data:

      // if image is a pdf, and we are printing or saving or need the actual data:

            if (needToUseRealPDFData) {
          // grab the image’s best representation, the actual NSPDFImageRep:
                NSPDFImageRep *rep = [_image bestRepresentationForDevice:nil];
         // ask it to draw within our bounds:
                [rep drawInRect:NSMakeRect(0.0,0.0,_bounds.size.width,_bounds.size.height)];
            } else 
                  [image drawInRect:NSMakeRect(0.0,0.0,_bounds.size.width,_bounds.size.height) 
                  fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0 - _dissolve];

The trick to get zoomed PDF to render onscreen at full quality is to multiply the size of the PDF image to the zoom:

   // helper method to return an integral size of an image, based on the zoom:
   
    - (NSSize)sizeForZoom:(float)zoom {
       NSSize s = [_image size];
       if (zoom == 1.0 || zoom == 0.0) return s;
       // images cannot be fractional sizes, so we round down:
       return NSMakeSize(floor(s.width * zoom), floor(s.height * zoom));
    }
    
          // Before drawing the image, we check if we are zoomed:
         // if our image is a PDF, and we’re drawing to screen, get a bigger image:
         
            if (_isPDF &&  [[NSGraphicsContext currentContext] isDrawingToScreen]  &&  zoom != 1.0) {
                NSSize s = [self sizeForZoom:zoom];
                if (_zoomedPDFImage == nil) {
                    _zoomedPDFImage = [_image copyWithZone:[self zone]];
             }
             if ( !NSEqualSizes([_zoomedPDFImage size], s))
                    [_zoomedPDFImage setSize:s];
                image = _zoomedPDFImage;
            } else {
                image = _image;
            }

NSStepper

An NSStepper provides what is known in Carbon as ‘little arrows’. These are common controls for date and time entries, but are also useful for any UI component that has values which can be “tweaked” a little bit. For example, we use them in Create to allow small adjustments to scale:


The ‘little arrows’ are a new addition to Cocoa, but have been around Carbon for awhile.

Like most control subclasses, NSStepper relies on the behavior of its custom NSStepperCell for its functionality. You don’t even need to know to this to use the stepper because the control has cover methods for the standard cell methods. Besides methods familiar from slider controls (setMaxValue:, setMinValue:, setAutorepeat:), the NSStepper allows you to set the granularity of an increment (or a decrement if the lower arrow is clicked) with setIncrement:.

You can also set whether the values loop around (as would be true in the case of rotation stepper) or not with setValueWraps:. In the example of a scale slider, for example, it makes no sense to go from the maximum value to the minimum value with one click!

Using the NSStepper in your interface consists of two parts: initializing the stepper for your requirements and keeping the value of the stepper in synch with data model, since it needs to know the original value in order to increment or decrement the value. You can initialize the stepper when loading the UI components for the first itme or directly in Interface Builder. This example is for a rotational slider: When loading the UI components the first time, you might initialize the values of the stepper, in this example, for a rotational slider. You can also set these values directly in Interface Builder, or programmatically in the File Owner’s class in awakeFromNib, which is called by the nib loading mechanism if this method exists:

- (void)awakeFromNib {
   [stepper setMinValue:0.0];
   [stepper setMaxValue:360.0];
   [stepper setIncrement:1.0];
   [stepper setValueWraps:YES];
   [stepper setAutorepeat:YES];
       // target/action set in IB
}

When inspecting a graphic that can be rotated, you reload the stepper with the current value of the graphic’s rotation:

   double currentRotation = [graphic rotation];
    [circularSlider setDoubleValue: currentRotation];
      [rotationStepper setDoubleValue: currentRotation];
   ...

The stepper handles all mousedowns and it knows whether it needs to increment or decrement the value. Let’s say you have connected the stepper to an action “incrementDecrementAction:”: The stepper has set its internal value to its incremented or decremented value, depending where the user clicked.

- (void)incrementDecrementAction:(id)aStepper {
    [self setSomeValue:[aStepper doubleValue]];  
      // take the value from the stepper
    [self reloadInspectingUI];   
         // update textfields, sliders, etc with new values 
}

The NSStepper is an excellent addition to the Cocoa developer’s toolkit, and both saves some lines of code as well as provide a standard mechanism for tweaking UI values.

Drag & Drop Support in NSTableView and NSOutlineView

The refinement of the Application framework is an ongoing process. For example, a few years ago we had to write our own code for drag and drop row reordering of table views and outlineviews (http://www.stone.com/GIFfun/GIFfunSource/Row_Moving_Protocol.html). Now, that code can be thrown away, and you can use the exposed API to accomplish this:

typedef enum { NSTableViewDropOn, NSTableViewDropAbove } NSTableViewDropOperation;

// In drag and drop, used to specify a dropOperation. For example, given a table with N rows 
(numbered with row 0 at the top visually), a row of N-1 and operation of NSTableViewDropOn would 
specify a drop on the last row. To specify a drop below the last row, one would use a row of N and 
NSTableViewDropAbove for the operation.

- (BOOL)tableView:(NSTableView *)tv writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard;
    // This method is called after it has been determined that a drag should begin, but before the drag 
    has been started.  To refuse the drag, return NO.  To start a drag, return YES and place the drag 
    data onto the pasteboard (data, owner, etc...).  The drag image and other drag related information 
    will be set up and provided by the table view once this call returns with YES.  The rows array is 
    the list of row numbers that will be participating in the drag.

- (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id )info 
   proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)op;
   
    // This method is used by NSTableView to determine a valid drop target.  Based on the mouse 
    position, the table view will suggest a proposed drop location.  This method must return a value 
    that indicates which dragging operation the data source will perform.  The data source may 
    “re-target” a drop if desired by calling setDropRow:dropOperation: and returning something other 
    than NSDragOperationNone.  One may choose to re-target for various reasons (eg. for better visual 
    feedback when inserting into a sorted position).

- (BOOL)tableView:(NSTableView*)tv acceptDrop:(id )info row:(int)row 
   dropOperation:(NSTableViewDropOperation)op;
    // This method is called when the mouse is released over an outline view that previously decided to 
    allow a drop via the validateDrop method.  The data source should incorporate the data from the 
    dragging pasteboard at this time.

Our free application, GIFfun, allows users to reorder the individual images that make up the final animated GIF. Here’ s a sample implementation of the Drag and Drop support in an NSTableView:

static int _moveRow = 0;

- (BOOL)tableView:(NSTableView *)tv writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard 
{
    int count = [gifFileArray count];
    int rowCount = [rows count];
    if (count < 2) return NO; 

    [pboard declareTypes:[NSArray arrayWithObject:GifInfoPasteBoard] owner:self];
    [pboard setPropertyList:rows forType:GifInfoPasteBoard];
    _moveRow = [[rows objectAtIndex:0]intValue];
    return YES;
}

- (unsigned int)tableView:(NSTableView*)tv validateDrop:(id )info 
   proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)op 
{
    if (row != _moveRow) {
        if (op==NSTableViewDropAbove) {
            return NSDragOperationAll;
        }
        return NSDragOperationNone;
    }
    return NSDragOperationNone;
}

- (BOOL)tableView:(NSTableView*)tv acceptDrop:(id )info row:(int)row 
   dropOperation:(NSTableViewDropOperation)op 
{
   BOOL result = [self tableView:tableView didDepositRow:_moveRow at:(int)row];
   [tableView reloadData];
   return result;
}

// here we actually do the management of the data model:

-  (BOOL)tableView:(NSTableView *)tv didDepositRow:(int)rowToMove at:(int)newPosition
{
   if (rowToMove != -1 && newPosition != -1) {
      id object = [gifFileArray objectAtIndex:rowToMove];
      if (newPosition < [gifFileArray count] - 1) {
         [gifFileArray removeObjectAtIndex:rowToMove];
         [gifFileArray insertObject:object atIndex:newPosition];
      } else {
         [gifFileArray removeObjectAtIndex:rowToMove];
         [gifFileArray addObject:object];
      }
      return YES;   // ie reload
   }
   return NO;
}

These few lines listed replace the 650 lines of code from SDTableView.h, SDTableView.m and SDMovingRowsProtocol.h. The complete source code to GIFfun is available at http://www.stone.com/GIFfun/.

Adding Features By Doing Nothing

And, if my Zen slacker coding model isn’t convincing enough, consider how you can add new features by doing nothing!

If you are using the Model-View-Controller pattern and taking advantage of the NSDocument architecture, then the introduction of Mac OS X 10.1 brought you new features, without you doing anything. Because your application links at runtime with the current version of the Application and Foundation frameworks, new features available in the kits become available to your users.

10.1 adds Hidden file extensions (which is way more Mac-like than the UNIX file extensions), ability to track documents, folders, and volumes which are renamed, and ability to save documents in a way which preserves aliases to the documents and additional document info such as maintaining file permissions, creation date, and icon settings.

Some of these features do require that you recompile your application, because the Apple engineers wanted to provide full backwards binary compatibility in programs that were linked against the Mac OS 10.0 version of the Cocoa framework.

Conclusion

Software is a process not a product, and therefore it is never finished! The frameworks that Cocoa developers rely on are continually being refined and improved, so it’s very important to carefully read the Release Notes found in /Developer/Documentation/ReleaseNotes, especially AppKit.html and Foundation.html. Always check for new functionality which can replace your home-rolled versions, thus making your application more compact and reducing the code base that you need to support.


Andrew Stone andrew@stone.com is the chief executive haquer at Stone Design Corp http://www.stone.com and divides his time between raising children, llamas & cane and writing applications for Mac OS X.

 
AAPL
$118.93
Apple Inc.
-0.07
MSFT
$47.81
Microsoft Corpora
+0.06
GOOG
$541.83
Google Inc.
+1.46

MacTech Search:
Community Search:

Software Updates via MacUpdate

Adobe Photoshop Elements 13.0 - Consumer...
Adobe Photoshop Elements 12--the #1 selling consumer photo editing software--helps you edit pictures with powerful, easy-to-use options and share them via print, the web, Facebook, and more.Version... Read more
Skype 7.2.0.412 - Voice-over-internet ph...
Skype allows you to talk to friends, family and co-workers across the Internet without the inconvenience of long distance telephone charges. Using peer-to-peer data transmission technology, Skype... Read more
HoudahSpot 3.9.6 - Advanced file search...
HoudahSpot is a powerful file search tool built upon MacOS X Spotlight. Spotlight unleashed Create detailed queries to locate the exact file you need Narrow down searches. Zero in on files Save... Read more
RapidWeaver 6.0.3 - Create template-base...
RapidWeaver is a next-generation Web design application to help you easily create professional-looking Web sites in minutes. No knowledge of complex code is required, RapidWeaver will take care of... Read more
iPhoto Library Manager 4.1.10 - Manage m...
iPhoto Library Manager lets you organize your photos into multiple iPhoto libraries. Separate your high school and college photos from your latest summer vacation pictures. Or keep some photo... Read more
iExplorer 3.5.1.9 - View and transfer al...
iExplorer is an iPhone browser for Mac lets you view the files on your iOS device. By using a drag and drop interface, you can quickly copy files and folders between your Mac and your iPhone or... Read more
MacUpdate Desktop 6.0.3 - Discover and i...
MacUpdate Desktop 6 brings seamless 1-click installs and version updates to your Mac. With a free MacUpdate account and MacUpdate Desktop 6, Mac users can now install almost any Mac app on macupdate.... Read more
SteerMouse 4.2.2 - Powerful third-party...
SteerMouse is an advanced driver for USB and Bluetooth mice. It also supports Apple Mighty Mouse very well. SteerMouse can assign various functions to buttons that Apple's software does not allow,... Read more
iMazing 1.1 - Complete iOS device manage...
iMazing (was DiskAid) is the ultimate iOS device manager with capabilities far beyond what iTunes offers. With iMazing and your iOS device (iPhone, iPad, or iPod), you can: Copy music to and from... Read more
PopChar X 7.0 - Floating window shows av...
PopChar X helps you get the most out of your font collection. With its crystal-clear interface, PopChar X provides a frustration-free way to access any font's special characters. Expanded... Read more

Latest Forum Discussions

See All

Mystery Case Files: Dire Grove, Sacred G...
Mystery Case Files: Dire Grove, Sacred Grove HD Review By Jennifer Allen on November 28th, 2014 Our Rating: iPad Only App - Designed for the iPad A decent new installment for the popular Mystery Case Files series.   | Read more »
Castaway Paradise – Tips, Tricks, and St...
Ahoy there, castaways: Were you curious about our own thoughts regarding this pristine shipwreck? Check out our Castaway Paradise review! Castaway Paradise is out for iOS, finally giving mobile gamers the opportunity to enjoy the idyllic lifestyle... | Read more »
Castaway Paradise VIP Subs are on Sale f...
Castaway Paradise VIP Subs are on Sale for a Limited Time, and a Special Holiday Update is Coming Soon Posted by Rob Rich on November 28th, 2014 [ | Read more »
Primitive Review
Primitive Review By Jordan Minor on November 28th, 2014 Our Rating: :: FOLK ARTUniversal App - Designed for iPhone and iPad True to its name, Primitive is about as straightforward as runners get.   | Read more »
7 tips to get ahead of the competition i...
7 tips to get ahead of the competition in Dynasty of Dungeons Posted by Simon Reed on November 28th, 2014 [ permalink ] Playcrab has launched their action-packed new dungeon crawler, Dynasty of Dungeons, today. | Read more »
Master of Tea Kung Fu Review
Master of Tea Kung Fu Review By Jordan Minor on November 28th, 2014 Our Rating: :: ONE DROP RULESUniversal App - Designed for iPhone and iPad Master of Tea Kung Fu is a creative and complex caffeinated brawler.   | Read more »
Monster Strike Review
Monster Strike Review By Campbell Bird on November 28th, 2014 Our Rating: :: BILLIARD STRATEGYUniversal App - Designed for iPhone and iPad Collect monsters and battle by flinging them across the battlefield in this strangely... | Read more »
Proun+ Review
Proun+ Review By Jennifer Allen on November 28th, 2014 Our Rating: :: TWITCHY RACINGUniversal App - Designed for iPhone and iPad Twitchy racing aplenty in Proun+, an enjoyably tricky title.   | Read more »
Lucha Amigos (Games)
Lucha Amigos 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Forget Ninja Turtles, and meet Wrestlers Turtles! Crazier, Spicier and…Bouncier! Sling carapaces of 7 Luchadores to knock all... | Read more »
Record of Agarest War Zero (Games)
Record of Agarest War Zero 1.0 Device: iOS Universal Category: Games Price: $7.99, Version: 1.0 (iTunes) Description: HyperDevbox Holiday Turkey Black Friday Special Pricing! To celebrate the opening of the holiday season HyperDevbox... | Read more »

Price Scanner via MacPrices.net

Up To 75% Off Infovole Text Apps Over Black F...
Infovole’s entire range of apps, including the Textkraft family of word processors for iPads and iPhones, is being offered at 50-75% off over the Black Friday and Cyber Monday weekend. The five-day... Read more
Black Friday: Up to $60 off Mac minis, NY tax...
 B&H Photo has new 2014 Mac minis on sale for up to $60 off MSRP as part of their Black Friday sale. Shipping is free, and B&H charges NY sales tax only: - 1.4GHz Mac mini: $449.99 $50 off... Read more
Black Friday: 27-inch 5K iMac for $2299, save...
 B&H Photo continues to offer Black Friday sale prices on the 27″ 3.5GHz 5K iMac, in stock today and on sale for $2299 including free shipping plus NY sales tax only. Their price is $200 off MSRP... Read more
Karalux Announces 24K Gold-Plated iPhone 6
Karalux, a Vietnam-based jewellery firm, has launched a unique 24 karat gold-plated iPhone 6 version with gold-cast monolithic dragon on its back panel. The real 24 karat gold plated enclosure doesn’... Read more
Black Friday: 13-inch 2.6GHz Retina MacBook P...
 B&H Photo has lowered their price for the 13″ 2.6GHz/128GB Retina MacBook Pro to $1159 for Black Friday. That’s $140 off MSRP, and it’s the lowest price for this model (except for Apple’s $1099... Read more
View all the Black Friday sales on our Mac Pr...
We’ve updated our Mac Price Trackers with the latest information on prices, bundles, and availability on systems from Apple’s authorized internet/catalog resellers. View Black Friday sale prices at a... Read more
Black Friday: 11-inch MacBook Air for $779, s...
 Best Buy has lowered their price for the 2014 11″ 1.4GHz/128GB MacBook Air to $779.99 for Black Friday. That’s $120 off MSRP. Choose free shipping or free local store pickup (if available). Sale... Read more
Apple Store Black Friday sale for 2014: $100...
BLACK FRIDAY The Apple Store has posted their Black Friday deals for 2014. Receive a $100 PRODUCT(RED) branded iTunes gift card with the purchase of select Macs, $50 with iPads, and $25 with iPods,... Read more
Black Friday: 15% off iTunes Gift Cards
Staples is offering 15% off $50 and $100 iTunes Gift Cards on their online store as part of their Black Friday sale. Click here for more information. Shipping is free. Best Buy is offering $100... Read more
BEVL Releases Dock Tailored for iPhone 6 and...
Seattle based BEVL has released their first product: an iPhone dock that is divergent in build quality, rock-solid function and visual simplicity to complement the iPhone. BEVL is now accepting... Read more

Jobs Board

*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, 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
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.