PATH Documentation > Release Notes
Mac OS X Leopard Developer Release Notes
Cocoa Application Framework
The Cocoa Application Framework (also referred to as the Application Kit, or AppKit) is one of the core Cocoa frameworks. It provides functionality and associated APIs for applications, including objects for graphical user interfaces (GUIs), event-handling mechanisms, application services, and drawing and image composition facilities.
This document describes the changes in AppKit since Mac OS X release 10.4. Changes in these notes since the WWDC 2007 seed of Leopard are highlighted (search for "WWDC 2007").
Note that there is more information on many of these changes in Apple's developer documentation. In some cases header files may also have additional details.
For AppKit release notes for 10.4 (aka Tiger) and earlier, please refer to the older release notes for AppKit.
Feedback
For feedback and comments about AppKit and Foundation APIs and features, you can email cocoa-feedback@group.apple.com. The intent of this address is to provide developers with a direct channel to the engineering team and the technology managers responsible for these technologies. We will read your input, and will try to acknowledge it, but we might not always have time for significant replies. Please use this address for feedback and comments on the APIs and features (for instance "please add Cocoa wrapper classes for such-and-such" or "I need API to do this and that"), but not for support type questions (for instance, "I can't install OS X" or "How does one create a new bundle in Xcode?") or comments in other areas (for instance, "why is the dock centered in Aqua?").
New features and significant changes in Leopard
The following are some the new features in AppKit, Foundation, and related areas. Note that the below table of contents is not a complete list of everything that is in this document, but it should provide a general idea of the important areas covered.
AppKit (these are described in this file):
- Support for 64-Bit Cocoa Applications
- Objective-C 2.0
- Interface Builder 3.0
- Animation Support
- NSView enhancements
- New class for managing views: NSViewController
- New class for grid-based animated layout of array of objects: NSCollectionView (originally known as NSGridView)
- Resolution Independence Improvements
- New controls for editing rules and predicates: NSRuleEditor, NSPredicateEditor
- New class for managing tracking areas: NSTrackingArea
- New class for gradient support: NSGradient
- New controller class for managing dictionary contents: NSDictionaryController
- NSTreeController improvements; new class for representing individual nodes in trees: NSTreeNode
- NSArrayController improvements
- Advanced searching, icon mode, and other features in open/save panels
- New control for displaying file system (or virtual) paths: NSPathControl
- NSMenu custom view support and other enhancements
- Print and page layout panel improvements
- Support for Uniform Type Identifiers (UTIs) in NSDocument, open/save panel, and a number of other classes
- Metadata preservation during NSDocument saving
- Support for Open Document and Open XML document formats in the text system
- Performance improvements in the text system ("non-contiguous layout")
- Changes in NSInputManager's input manager bundle loading
- NSTextView enhancements such as find panel improvements, temporary find indicator, smart quote and link support, list enhancements, etc
- Grammar checking support
- Numerous NSTableView and NSOutlineView enhancements
- NSDatePicker improvements
- NSSound API additions
- NSSpeechSynthesizer enhancements
- NSWindow changes and new window appearance, including support for Heads Up Display (HUD) window style
- Support for accessory view in NSAlert
- New class for representing dock tiles: NSDockTile
- NSSplitView enhancements
- Support for text and image effects in cells
- Standard images in NSImage
- NSBitmapImageRep enhancements
- New properties on NSBox
- NSToolbar enhancements
- Accessibility improvements
- Other enhancements and fixes
Foundation (these are described in the Foundation release notes):
Backward Compatibility
One backward compatibility mechanism that is occasionally used in the frameworks is to check for the version of the SDK an application was built against, and if an older SDK, modify the behavior to be more compatible. This is done in cases where bad incompatibility problems are predicted or discovered; and most of these are listed below in these notes.
Typically we detect where an application was built by looking at the version of the system frameworks the application was linked against. Thus, as a result of relinking your application on Leopard or against Leopard SDK, you might notice different behaviors, some of which might cause incompatibilities. In these cases because the application is being rebuilt, we expect you to address these issues at the same time as well. For this reason, if you are doing a small incremental update of your application to address a few bugs, it's usually best to continue building on the same build environment and libraries used originally, or against the original SDKs.
In some cases, we provide defaults (preferences) settings which can be used to get the old or new behavior, independent of what system an application was built against. Often these preferences are provided for debugging purposes only; in some cases the preferences can be used to globally modify the behavior of an application by registering the values (do it somewhere very early, with -[NSUserDefaults registerDefaults]).
Marking new APIs in headers
New APIs in headers are marked with the constructs:
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
...
#endif
or
- (void)aNewMethod AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
The basic definitions for these come from AvailabilityMacros.h, a standard system header. This is included from Cocoa.h, AppKit.h, and Foundation.h imports.
If you do nothing, MAC_OS_X_VERSION_MAX_ALLOWED is assumed to be MAC_OS_X_VERSION_10_5.
Runtime Version Check
There are several ways to check for new features provided by the Cocoa frameworks at runtime. One is to look for a given new class or method dynamically, and not use it if not there. Another is to use the global variable NSAppKitVersionNumber (or, in Foundation, NSFoundationVersionNumber):
APPKIT_EXTERN double NSAppKitVersionNumber;
#define NSAppKitVersionNumber10_0 577
#define NSAppKitVersionNumber10_1 620
#define NSAppKitVersionNumber10_2 663
#define NSAppKitVersionNumber10_3 743
#define NSAppKitVersionNumber10_4 824
One typical use of this is to floor() the value, and check against the values provided in NSApplication.h:
if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_0) {
/* On a 10.0.x or earlier system */
} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_1) {
/* On a 10.1 - 10.1.x system */
} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_2) {
/* On a 10.2 - 10.2.x system */
} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_3) {
/* On a 10.3 - 10.3.x system */
} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_4) {
/* On a 10.4 - 10.4.x system */
} else {
/* Leopard or later system */
}
Special cases or situations for version checking are also discussed in the release notes as appropriate. For instance some individual headers may also declare the versions numbers for NSAppKitVersionNumber where some bug fix or functionality is available in a given update, for example:
#define NSAppKitVersionWithSuchAndSuchBadBugFix 582.1
64-Bit
Leopard contains 64-bit versions of many system frameworks, enabling building and running many Cocoa apps as 64-bit.
There are some API changes in Cocoa to accomodate and enable 64-bit. Please refer to the Foundation release notes for more information on 64-bit for Cocoa.
intValue methods under 64-Bit
As described in the Foundation release notes, we have added NSInteger taking-variants of "intValue" and related methods. For the AppKit, this means the following new methods in both NSCell and NSControl:
- (NSInteger)integerValue;
- (void)setIntegerValue:(NSInteger)val;
- (void)takeIntegerValueFrom:(id)sender;
The existing intValue and related methods continue to take the native int type, which is 32-bit on both 32 and 64-bit platforms. These are among the very few methods remaining in Cocoa that take native int or unsigned int, rather than NSInteger or NSUInteger.
NSOpenGL and 64-Bit
The AppKit API declarations in NSOpenGL.h have been changed to use standard OpenGL types (GLint, GLsizei, GLenum, and GLbitfield) in place of explicit C "int" and "long" types, to help facilitate the transition to 64-bit. The effective sizes and signedness of the parameters and return values remain the same as before for 32-bit development.
APIs removed in 64-Bit AppKit
A number of classes have been removed in 64-bit version of AppKit:
- NSMenuView - You can use NSView customizations on NSMenuItem
- NSMovieView - Use QTKit
- NSMovie - Available with very limited functionality (see here); use QTKit
- NSQuickDrawView - QuickDraw is not supported in 64-bit
- NSSimpleHorizontalTypesetter - Deprecated for a while now, use the new NSTypesetter facilities
In addition, a number of previously deprecated individual APIs have also been removed from the 64-bit AppKit, including the old Display PostScript related symbols exported solely for binary compatibility under 32-bit.
Objective-C 2.0 (Updated since WWDC 2007 Seed)
Leopard includes Objective-C 2.0, which brings a number of new features to the language and runtime: Fast enumeration, garbage collection, class extensions, properties, method and class attributes, and optional methods on protocols.
There are a number of additional runtime features available in 64-bit only: Stricter access control on private instance variables and classes, "non-fragile" instance variables, and C++ compatible, "zero-cost" exceptions (which are very cheap to set up, but expensive to throw).
It is important to note that all of these features are Leopard-only. Please refer to the Objective-C 2.0 documentation for full details on these features.
Note that many Objective-C APIs in Leopard have not yet adopted these features. But in future releases, you are likely to see:
- @property used to declare properties
- Delegate declarations switched from informal protocols (categories on NSObject) to formal protocols, with optional methods
- Increased restricted visibility of private symbols in the runtime
The last point is especially important, since it would prevent applications using these symbols from launching in future releases. As always: Please do not access undeclared APIs in your applications. This includes undeclared classes, methods, and instance variables. If you have a strong need for a private API and have no workaround, please let us know.
Interface Builder 3.0 (Updated since WWDC 2007 Seed)
Leopard developer tools includes a new version of Interface Builder (IB). IB 3.0 has a redesigned interface, a much improved integration with Xcode, and ability to access many more AppKit features in your nib files than before.
IB 3.0 supports three file formats. The 2.x file format is same file format that's been in use in Interface Builder previously. It can be edited by IB 2.0, and is deployable on earlier versions of Mac OS X. The 2.x file format does not support some new features of IB, such as the ability to edit custom cell subclasses and toolbars. The 3.x file format supports all the new features, is also deployable on earlier versions of Mac OS X, but can only be edited with IB 3.0. IB 3.0 also supports a textual, human-readable format called the "xib" format. This format is equivalent to the 3.x format, but it's more SCM-friendly. It is compiled down to a 3.x file at build time.
For editing and building your project on Leopard only, we recommend the xib format, since it provides the best development time experience. If you need to build your project on Tiger, but edit your nibs on Leopard only, then you can use the 3.x format. Finally, if you wish to be able to continue to edit your nib files on Tiger, you can stick to the 2.x format. In all these cases, the files can be deployed on Tiger, but some features supported by IB 3.0 may not work on the earlier systems. IB has facilities to warn you in those cases.
Core Animation Layer Tree Rendering (Updated since WWDC 2007 Seed)
Leopard AppKit provides the means to render a Core Animation layer tree inside the bounds of any given NSView. Clients need only provide the root CALayer of the layer tree to be displayed, then enable layer tree rendering for the same view:
[view setLayer:rootLayer];
[view setWantsLayer:YES];
AppKit responds by setting up a Core Animation renderer that animates and composites the layer tree on a background thread.
The Core Animation renderer continues its asynchronous operation until layer tree rendering is disabled for the view (via a setWantsLayer: message with a parameter of NO), or the view is hidden or removed from the window. Un-hiding a view, or adding a view that has wantsLayer enabled to a window, resumes layer tree rendering.
To conserve system resources, AppKit also suspends layer tree rendering for the views in a given window when the window is ordered out, and for all the views in a process when the user's login session is switched out via the "Fast User Switching" feature. In such cases, layer tree rendering automatically resumes when the window is ordered back in, or when the user's login session is switched back in, as appropriate.
See the documentation for the QuartzCore framework's new Core Animation API for more information about layer tree creation, capabilities, and use.
New View Animation Facilities, and Layer-Backed View Drawing (Updated since WWDC 2007 Seed)
In addition to supporting rendering of user-defined Core Animation layer trees, Leopard AppKit adopts API paradigms similar to Core Animation's to allow clients to request animation of view and window properties, and also adds a new "layer-backed" mode for rendering and animation of view trees. Use of layer-backed mode is not required in order to use AppKit's new CAAnimation-based API, but does enable the use of additional new visual treatments for views (per-view overall alpha, shadows, and the ability to apply Core Image filters to rendered content), and accelerates animation of nonresizing views for the price of caching their rendered content into per-view CALayers.
In conventional view rendering, a window's view tree is drawn back-to-front into the window's backing store. The drawn view content is thus pre-composited into a single backing store, in such a way that updating any given part of the window requires re-drawing the affected parts of all the views that contribute to the result. In the new "layer-backed" view drawing mode that is now supported in Leopard, each of the views in a layer-backed view subtree has its content drawn to its own layer in an AppKit-created-and-managed CALayer tree. This carries an additional memory cost, of order (4 * pixels wide * pixels high) bytes per view, that should be weighed when considering whether and where to employ layer-backed mode in an application's user interface, but in return it enables already-rendered view content to be moved around more efficiently during animations, since the content only has to be re-compositied instead of being re-rendered from scratch.
Most of the standard views and controls that AppKit and Mac OS X's other Cocoa frameworks provide are able to function in layer-backed mode in Leopard, with the exception of certain specialized views such as WebKit WebViews and Quartz Composer QCViews, whose use in layer-backed mode is not presently supported.
Layer Size Limits and Tiled Layers (New since WWDC 2007 Seed)
One inherent limitation of rendering view content into a Core Animation layer is that the size of ordinary CALayers is constrained by the maximum OpenGL texture size supported by the host system's graphics hardware. On most current graphics hardware the effective limit is 2046x2046 pixels, beyond which size layer creation will fail. Care should therefore be taken to insure that layer-backed views do not exceed this size limit.
To get around this limitation for potentially larger document views, AppKit employs CATiledLayers to serve as the backing layers for the document views of NSScrollViews. This specialized layer type caches its content in a grid of "tiles" (of default size 256x256 pixels) that are drawn as they become visible, and can be garbage-collected when they go out of view. From the user's perspective, the tiles are added asynchronously as they are revealed during scrolling. The visual appearance of the tile addition can be minimized by enabling the "drawsBackground" property for the enclosing NSScrollView (or, equivalently, its NSClipView), and choosing a background color that most closely matches the document view content being drawn.
Note that when using layer-backed mode for an NSScrollView's document view, it's necessary for the enclosing NSScrollView, or at least its NSClipView ("content view"), to also be layer-backed in order for scrolling to function correctly.
Using Layer-Backed Views (Updated since WWDC 2007 Seed)
A quick-start guide for beginning to experiment with this functionality:
1. Choose a view that will serve as the root view for Core Animation-based view rendering, and switch on Core Animation-based rendering for that view (and implicitly its descendants) by doing [theRootView setWantsLayer:YES];
2. Properties of views and windows for which auto-animation is supported can be animated by messaging through the target object's (view's or window's) "animator" proxy:
[[someDescendantOfTheRootView animator] setFrame:newFrame];
To specify a duration in place of the global default of 0.25 seconds, enclose such messages in an NSAnimationContext that specifies the duration for animation:
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0.25];
[[someDescendantOfTheRootView animator] setFrame:newFrame];
[NSAnimationContext endGrouping];
Basic default animation parameters are provided for the following NSView and NSWindow properties, such that they will animate automatically when assigned a new target value via the view or window's animator:
for NSView: alphaValue, frame, frameOrigin, frameSize, frameRotation, frameCenterRotation, bounds, boundsOrigin, boundsSize, backgroundFilters, contentFilters, compositingFilter, shadow
for NSWindow: alphaValue, frame
Some further notes regarding the new animation and visual property additions to NSView follow, in the "NSAnimationContext", "new NSView properties", and "NSAnimation additions" sections.
NSAnimationContext
NSAnimationContext is a new class in Leopard. NSAnimationContexts are analogous to Core Animation's CATransactions, and are also somewhat similar in overall concept to NSGraphicsContexts. Each thread maintains its own stack of nestable NSAnimationContext instances, with each new instance initialized as a copy of the instance below it (so, inheriting its current properties). Currently, an NSAnimationContext exists for the sole purpose of holding the default duration to be used for "animator"-proxy-initiated animations.
Each thread starts with a current NSAnimationContext whose default duration is 0.25 seconds (the same default value used by Core Animation), meaning that value-set operations for animatable object properties that go through "animator" proxies will animate with that duration by default. To animate with a different duration, a section of code would begin a new NSAnimationContext with the desired duration, perform the desired value-set operations through "animator" proxy or proxies, then close the context when done:
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:1.0]; // Animate enclosed operations with a duration of 1 sec
[[aView animator] setFrame:newFrame];
[NSAnimationContext endGrouping];
NSAnimationContexts can be nested, allowing a given block of code to initiate animations using its own specified duration without affecting animations initiated by surrounding code.
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:1.0]; // Animate enclosed operations with a duration of 1 sec
[[aView animator] setFrame:newFrame];
...
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0.5]; // Animate alpha fades with half-second duration
[[aView animator] setAlphaValue:0.75];
[[bView animator] setAlphaValue:0.75];
[NSAnimationContext endGrouping];
...
[[bView animator] setFrame:secondFrame]; // Will animate with a duration of 1 sec
[NSAnimationContext endGrouping];
Since an "animator" proxy can be handed off to code that expects an ordinary object of the kind the proxy targets (presently, an NSView or NSWindow), it might in rare circumstances be necessary to suppress animation for code that does not explicitly go through "animator" proxy objects. This can be accomplished using an animation context with a duration of zero:
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0.0]; // Makes value-set operations take effect immediately
[aViewOrMaybeAnAnimator setFrame:newFrame];
[NSAnimationContext endGrouping];
New NSView properties (Updated since WWDC 2007 Seed)
Leopard AppKit adds some new properties to NSViews, which are described below with their corresponding accessor methods.
- (void)setWantsLayer:(BOOL)flag;
- (BOOL)wantsLayer;
The "wantsLayer" property determines whether a view and its descendants should be composited and animated using a Core Animation layer tree, enabling the use of advanced animation and compositing effects. Defaults to NO. Setting this property to YES for the rootmost view for which Core Animation-based compositing is desired is all that's needed to activate Core Animation-based view buffering, compositing, and animation for a given view subtree. The view subtree is then said to be "layer-backed", since each view is given a corresponding Core Animation layer that serves as its backing store.
- (CALayer *)layer;
The -layer method returns the view's corresponding AppKit-created-and-managed CALayer, if the view is layer-backed. Callers may use the returned pointer to message the layer directly, as a means of accessing features that aren't re-exported as NSView properties. May return nil for a view that's currently marked as layer-hosted, if AppKit hasn't yet displayed the view for the first time and thus created the view's layer. For most ordinary usage of animating views' frames and content and applying effects, awareness of and direct access to views' underlying layers is unlikely to be needed, as AppKit will be able to manage them automatically.
- (void)setLayer:(CALayer *)newLayer;
The -setLayer: method sets a given CALayer to be a view's backing layer. This causes the view to dissociate from its previously assigned layer (if any), removing that layer from its surrounding layer tree and releasing the view's reference to the layer. The new layer takes on the old layer's position in the layer tree (or is simply added to the layer tree in the appropriate place, if it isn't replacing an existing layer). A view retains its layer, but AppKit maintains only a weak reference from the layer back to the view. This method manages both associations.
-setLayer: is published to allow for the usage where a developer has an arbitrary layer tree that's not tied to a view subtree and isn't automatically managed by AppKit, and wants to render that in a view (see "Core Animation Layer Tree Rendering").
- (void)setAlphaValue:(CGFloat)viewAlpha;
- (CGFloat)alphaValue;
Sets the overall opacity value with which the view and its descendants are composited into their superview (analogous to a window's alphaValue). Defaults to 1.0. This setting may be varied independently of the class' return value for -isOpaque, and the implementation of the latter needn't take the view's alphaValue into account, since AppKit consults both values when necessary. A view's alphaValue will affect both Core Animation-based and conventional view compositing.
- (NSShadow *)shadow;
- (void)setShadow:(NSShadow *)shadow;
Sets an optional shadow to be drawn behind the view subtree. Defaults to nil. This setting only has an effect for Core Animation-based view compositing. Note that, although Core Animation's shadow model uses the same parameters as a Quartz shadow, the rendered results may differ from those achieved using Quartz shadow rendering. NSShadow is used here merely as an appropriate Cocoa encapsulation for the identical set of shadow parameters.
The following three pairs of accessor methods can be used to apply arbitrary Core Image filter effects for views that are backed by CALayers. As specified by Core Animation, the conceptual model used to apply filter operations and combine their results is:
maskop(mask, compositeop(layerop(layer), backgroundop(background)), background)
- (CIFilter *)compositingFilter;
- (void)setCompositingFilter:(CIFilter *)filter;
Sets a CIFilter that will be used to composite the view subtree over its (possibly filtered) background. Defaults to nil, which implies that source-over compositing should be used. This setting only has an effect for Core Animation-based view compositing.
- (NSArray *)contentFilters;
- (void)setContentFilters:(NSArray *)filters;
Allows the view's content to be filtered through an optional chain of CIFilters before being composited into the render destination. The supplied array of filters needn't be connected to one another, as they will be connected in series automatically by Core Animation. Defaults to nil. This setting only has an effect for Core Animation-based view compositing.
- (NSArray *)backgroundFilters;
- (void)setBackgroundFilters:(NSArray *)filters;
Allows the background behind the view's subtree to be filtered through an optional chain of CIFilters before the view subtree is composited into it. The supplied array of filters needn't be connected to one another, as they will be connected in series automatically by Core Animation. Defaults to nil. This setting only has an effect for Core Animation-based view compositing.
Layer-Backed Views and Out-of-Band Drawing
For the ordinary case of views drawn into a shared window backing store, it has historically been possible to draw into the window area that a view occupies at will, by locking focus on the view, drawing, and unlocking focus:
[view lockFocus];
/* Perform some drawing. */
[view unlockFocus];
This was sometimes used to replace some animated content in response to a timer callback, for example.
Due to the inherently different buffering semantics for layer tree based rendering, this technique cannot be used by layer-backed views. Instead, you should perform all needed drawing in -drawRect:, and use -setNeedsDisplay: and/or -setNeedsDisplayInRect: to prompt updates as needed. This is generally a preferred technique anyway, since it avoids potential inconsistencies with -drawRect:-based drawing, allows other views to contribute potentially necessary content to the affected window area, and enables AppKit to coalesce updates for greater efficiency.
Avoiding Synchronization Issues with Layer-Backed View Animations (New since WWDC 2007 Seed)
When a view is resized, the content that it draws can respond in potentially arbitrary ways. For this reason, AppKit's "animator" proxy based animation API asks resizing views to redraw their content at each step along the way, to insure correct results.
When used with layer-backed views, this can lead to synchronization problems where a single view can appear to "jitter" when its frameOrigin and frameSize are simultaneously animated, or where gaps between adjacent animating views fluctuate, because each view's frameOrigin move is being animated on a background thread by Core Animation. Such synchronization problems can be remedied by insuring that the view frame animations are initiated using a -setFrame: message to each view's animator, rather than going through -setFrameOrigin: and/or -setFrameSize: separately. This cues AppKit to animate both the frameOrigin and frameSize changes itself, so that the results will stay in sync throughout the animation.
Note that AppKit-driven animations are timer-based, and thus require normal periodic runloop servicing to occur in order for them to advance. For best results, try to avoid having lengthy operations block the runloop while AppKit-driven animations are in flight.
Layer-backed NSImageView optimization (New since WWDC 2007 Seed)
For complex views, animations that cause the frame size to change often result in sub-optimal animation performance. When the view's size changes, it must redrawn for each frame of the animation, which often lowers framerates. NSImageView is a good example of this, since rendering images can be expensive. NSImageView has been optimized in certain cases for resizing animations. The following is a set of conditions that must be met in order for the optimization to take effect:
• The image view must be uneditable and have a frame style of NSImageFrameNone.
• The image view must contain AppKit's standard NSImageCell and the view's drawRect: method must not be overridden.
• The view must be layer-backed and must be using the layer that AppKit creates for it (meaning you may not provide your own layer with -setLayer:).
• The "best" image rep (according to -bestRepresentationForDevice:) for the view's image must be an NSBitmapImageRep, NSCachedImageRep, NSPICTImageRep, or NSCGImageRep.
• The view's imageScaling must be NSImageScaleProportionallyUpOrDown with centered imageAlignment or NSImageScaleAxesIndependently or NSNSImageScaleNone with any imageAlignment. Note that the default imageScaling is NSImageScaleProportionallyDown which does not allow the optimization to take effect.
Shifting "needsDisplay" Rectangles in a View
The following method enables shifting of the receiving view's dirty rects in a single operation. The effect of this method is to get all of the receiving view's dirty rectangles, clear all dirty rectangles in the intresection of the specified clipRect and the view's bounds, shift the retrieved rectangles by the given "delta" offsets, clip the result to the intersection of clipRect and the view's bounds, and add the resultant rectangles back to the view.
- (void)translateRectsNeedingDisplayInRect:(NSRect)clipRect by:(NSSize)delta;
This method should rarely be needed, but may be useful to clients that implement their own copy-on-scroll logic.
Pixel Alignment and Transforming View Coordinates To and From "Base" Space (Updated since WWDC 2007 Seed)
In Leopard, NSView provides a new set of methods that should be used when performing pixel-alignment of view content. They provide the means to transform geometry to and from a "base" coordinate space that is pixel-aligned with the backing store into which the view is being drawn:
- (NSRect)convertRectToBase:(NSRect)aRect;
- (NSPoint)convertPointToBase:(NSPoint)aPoint;
- (NSSize)convertSizeToBase:(NSSize)aSize;
- (NSRect)convertRectFromBase:(NSRect)aRect;
- (NSPoint)convertPointFromBase:(NSPoint)aPoint;
- (NSSize)convertSizeFromBase:(NSSize)aSize;
For conventional view rendering, in which a view hierarchy is drawn flattened into a window backing store, this "base" space is the same as the coordinate system of the window, and the results of using these new methods are the same as converting geometry to and from view "nil" using the existing -covert[Rect/Point/Size]:[to/from]View: methods.
Views that are rendered into backing layers in a Core Animation layer tree, however, have their own individual backing stores, which may be aligned such that window space is not necessarily the appropriate coordinate system in which to perform pixel alignment calculations.
These new coordinate transform methods provide a way to abstract view content drawing code from the details of particular backing store configurations, and always achieve correct pixel alignment without having to special-case for layer-backed vs. conventional view rendering mode. Regardless of the underlying details of how view content is being buffered, converting to base space puts one in the native device coordinate system, in which integralizing coordinates produces pixel alignment of geometry.
When using layer-backed views at a user interface scale factor other than 1.0, note that the dimensions of a view and the dimensions of its corresponding backing layer will vary according to the scale factor, since CALayer bounds are always expressed in pixels, while NSView dimensions remain expressed in points. Most clients of layer-backed views will not have a need to perform operations directly in layer space, but for those that do it's important to use the preceding methods to convert geometric quantities between view space and layer ("base") space when appropriate.
Responding to View Hiding/Unhiding
Hiding or un-hiding a given view using the -setHidden: API affects the effective visibility of all of its descendant views. Leopard adds two new NSView methods that clients can override, if desired, to have their custom view classes respond to becoming effectively hidden or unhidden vis-à-vis the -setHidden: API:
- (void)viewDidHide;
- (void)viewDidUnhide;
A view will receive a "viewDidHide" message when its "isHiddenOrHasHiddenAncestor" state goes from NO to YES. This can happen when the view or an ancestor is marked as hidden, or when the view or an ancestor is spliced into a new view hierarchy.)
A view will receive a "viewDidUnhide" message when its "isHiddenOrHasHiddenAncestor" state goes from YES to NO. (This can happen when the view or an ancestor is marked as not hidden, or when the view or an ancestor is removed from its containing view hierarchy.)
Performing Just-Before-Drawing View Activity
NSView has a new "-viewWillDraw" API method in 10.5 that can be overridden to perform any last-minute activity that might be desired at the outset of a view hierarchy "-display..." operation.
- (void)viewWillDraw;
Most often, the activity to be performed at this time consists of some combination of view layout (assigning new frame sizes and/or positions to views) and marking additional view areas as needing display (typically as the result of performing layout of non-view content, such as text glyphs, graphics, or web content). The desired effect is to perform such computations on demand, deferred until their results are about to actually be needed, allowing for the same kind of update coalescing performance benefits that we get with the deferred display mechanism itself, rather than forcing content layout to be performed immediately when the content is established or deferred until a subsequent drawing pass.
At the outset of recursive display of part or all of a view hierarchy, which is always initiated by one of NSView's eight public "-display..." methods, AppKit recurses down the view hierarchy, sending a -viewWillDraw message to each of the views that may be involved in the display operation.
NSView's default implementation of this method is itself the mechanism for the recursion, and allows overriders the flexibility to head-recurse or tail-recurse as best suits the needs of the operations they have to perform. Conceptually, NSView's implementation looks like:
@implementation NSView
- (void)viewWillDraw {
if (any descendant of self overrides "-viewWillDraw") {
for (each subview that intersects the window area being drawn in back-to-front order) {
[subview viewWillDraw];
}
}
}
@end
So an override of this method could do:
- (void)viewWillDraw {
/* Perform some operations before recursing for descendants. */
/* Now recurse to handle all our descendants. Overrides must call up to super like this. */
[super viewWillDraw];
/* Perform some operations that might depend on descendants already having had a chance to update. */
}
During the -viewWillDraw recursion, sending of -setNeedsDisplay: and -setNeedsDisplayInRect: messages to views in the hierarchy that's about to be drawn is valid and supported, and will affect AppKit's assessment of the total area to be rendered in that drawing pass.
If desired, an implementation of -viewWillDraw can use NSView's existing -getRectsBeingDrawn:count: method to obtain a list of rectangles that bound the affected area, enabling it to restrict its efforts to that area.
Setting a View's Subviews
NSView has a new -setSubviews: API method in 10.5 that can be used to reorder a view's subviews, remove existing subviews, and/or add new subviews all via a single API entry point:
- (void)setSubviews:(NSArray *)newSubviews;
With this single method, one can:
- reorder the receiver's existing subviews
- add or remove subviews from the receiver
- potentially replace all of the receiver's previous subviews with a whole new set of views
- potentially remove all of the receiver's previous subviews, leaving it with none (think "[aView setSubviews:[NSArray array]]")
The views in the "newSubviews" array can by any combination of existing subviews of the receiver, subviews of other views, or views that currently have no superview. If "newSubviews" is nil, or contains any duplicate entries, -setSubviews: throws an invalid argument exception. (Prior to the WWDC 2007 Leopard seed, -setSubviews: would also raise an exception if any of the new subviews was already a subview of some view. This is now allowed, and simply results in the views being removed from their previous superviews before being added as subviews of the receiver.)
Given a valid "newSubviews" parameter, -setSubviews: performs whatever sorting of the subviews array, subview addition, and subview removal activity is necessary to leave the receiver with the requested new array of subviews. Thus, any member of "newSubviews" that isn't already a subview of the receiver is added. Any member of the view's existing "subviews" array that isn't in "newSubviews" is removed. And any views that are in both "subviews" and "newSubviews" is simply moved in the subviews array as needed, without being removed and re-added. The -setSubviews: method also marks affected view/window areas as needing display to reflect the new z-ordering.
Views, Focus Rings, and Drawing Performance
To help guarantee correct redraw of focus rings, AppKit may automatically draw additional parts of a window beyond those that application code marked as needing display. It may do this for the first responder view in the application's key window, if that first responder view's focusRingType property is set to a value other than NSFocusRingTypeNone. Any view that can become first responder, but doesn't draw a focus ring, should have its focusRingType set to NSFocusRingTypeNone to avoid unnecessary additional redraw.
NSAnimatablePropertyContainer protocol (Updated since WWDC 2007 Seed)
NSAnimatablePropertyContainer is a new protocol in Leopard, currently adopted by NSView and NSWindow. The methods in NSAnimatablePropertyContainer are as follows:
- (id)animator;
The -animator method returns a proxy object for the receiver that can be used to initiate implied animation of property changes. An object's "animator" should be treated as if it was the object itself, and may be passed to any code that accepts the object as a parameter. Sending of KVC-compliant "set" messages to the proxy will trigger animation for automatically animated properties of its target object, if the active NSAnimationContext in the current thread has a duration value greater than zero, and an animation to use for the property key is found by the -animationForKey: search mechanism defined below. An object's automatically animated properties are those for which [theObject animationForKey:] finds and returns an CAAnimation instead of nil, often because [[theObject class] defaultAnimationForKey:] specifies a default animation for the key.
It's perfectly valid to set a new value for a property for which an animation is currently in progress; this simply sets a new target value for that property, with animation to the new target proceeding from whatever current value the property has reached. An in-flight property animation can be stopped by setting a new value for the property with 0.0 as the surrounding NSAnimationContext's duration value.
For the common specific case of animating views:
Initiating animation via an "animator" proxy object works under both Core Animation-based and conventional view compositing. The primary difference under Core Animation-based compositing is that for intrinsic geometric properties such as the view's "frame," all animation is handled at the Core Animation level, meaning that the view's property will be immediately set to the desired target value, and the view won't see the successive intermediate values. The animation effect in such cases is purely visual and exists only in Core Animation's "render tree" backend. In contrast, under conventional (non-layer-backed) view compositing, view property animations are executed at the AppKit level, and during the course of those animations views will receive value-set operations for successive intermediate values. This is also true for animation of all developer-defined properties, under both layer-backed and conventional view compositing and animation.
- (NSDictionary *)animations;
- (void)setAnimations:(NSDictionary *)dict;
An animatable property container's optional "animations" dictionary maps NSString keys to CAAnimation values. When an occurrence matching the key fires for the view, -animationForKey: first looks in this dictionary for an animation to execute in response.
- (id)animationForKey:(NSString *)key;
When the occurrence specified by "key" fires for an object, -animationForKey: is consulted to find the animation, if any, that should be performed in response. Like its Core Animation counterpart, -[CALayer actionForKeyPath:], this method is a funnel point that defines the standard order in which the search for an animation proceeds, and is not one that clients would typically need to override. This method first checks the receiver's "animations" dictionary, then falls back to +defaultAnimationForKey: for the receiver's class.
+ (id)defaultAnimationForKey:(NSString *)key;
As described above, -animationForKey: next consults the class method +defaultAnimationForKey: when its search of an instance's "animations" dictionary doesn't turn up an animation to use for a given property change.
NSView's +defaultAnimationForKey: method returns a default animation that should be performed when the occurrence specified by "key" fires for a view, where "key" typically names a property whose value is being changed. For each of NSView's own animatable properties, NSView's implementation returns a suitable default CAAnimation to be used. For all other keys this method returns nil.
A developer implementing a custom view subclass can enable automatic animation of the subclass' added properties by overriding this method, and having it return the desired default CAAnimation to use for each of the property keys of interest. The override should defer to super for any keys it doesn't specifically handle, facilitating inheritance of default animation specifications.
@implementation MyView
+ (id)defaultAnimationForKey:(NSString *)key {
if ([key isEqualToString:@"borderColor"]) {
// By default, animate border color changes with simple linear interpolation to the new color value.
return [CABasicAnimation animation];
} else {
// Defer to super's implementation for any keys we don't specifically handle.
return [super defaultAnimationForKeyKey:key];
}
}
@end
NSCollectionView
A new view class has been added to facilitate interesting animations: NSCollectionView. You can set or bind a collection view's content to an array of objects. For each object, the collection view will create an NSCollectionViewItem, which in turn manages a view that is used to display the values of its "represented object." All views automatically create a layout to fit all items into its content and animates them if the content changes (for example, if the content is reordered, it will slide the items into the new positions).
Usually collection view items are created from a "prototype" which is set as the "itemPrototype" outlet in Interface Builder. For the view of the collection view item, you can use standard controls/views to form a "compound" view. For example, you can group an NSImageView and an NSTextField in an NSBox to form a unit that displays images and names for it. To set the view used by a collection view item, you typically use the "view" outlet.
To populate a collection view item's view with values from the represented object, you will typically create bindings from the view (or any of the subviews) to the "representedObject" of the collection view item (example: you could bind the value binding of text field to the key "representedObject.name" of the collection view item). Alternatively, you could subclass NSCollectionViewItem and make it the data source or delegate of one of the views.
Note that in early Leopard seeds (including WWDC 2006) NSCollectionView was known as NSGridView, and NSCollectionViewItem was NSLayoutItem. As foreshadowed in the release notes, these classes have changed; however, the changes are limited strictly to the two class and several method name changes:
- layoutView in NSLayoutItem has become collectionView
- minGridSize, maxGridSize, and corresponding setter methods have become minItemSize, maxItemSize, setMinItemSize:, and setMaxItemSize:
- newLayoutItemForRepresentedObject: is now newItemForRepresentedObject:
- layoutItemPrototype and setLayoutItemPrototype: are now itemPrototype and setItemPrototype:
The old names have been removed in the final version of Leopard.
Resolution Independence (New since WWDC 2007 Seed)
On Leopard, resolution independence (aka "HiDPI") is a developer feature. You can use QuartzDebug in /Developer/Applications/Performance Tools to set the user space scale factor to 1.25, 1.5, 2.0, or 3.0, then launch your application. The user interface of your application will be scaled by the user space scale factor.
Most standard controls are now drawn in high resolution, which allows them to appear crisp when running with user space scaling enabled.
In addition a number of standard images are now available in high-resolution, enabling graphics to appear crisp when scaled. Names of these images are declared in NSImage.h. Note that it's important to use these images only for the purpose indicated by the name, since the actual graphic may change in future releases. See the NSImage section for more info.
There are some known issues with the non-integral scale factors of 1.25 and 1.5.
- Most drawing should occur on integral pixel boundaries, but views are not automatically constrained to fall on pixel boundaries
- Image tiling also looks most correct if the tiled image is integral-pixel sized and adjusted as needed to avoid pixel cracks
- Tracking rects on non-integral boundaries may generate a mouseEntered: event while the mouse is still slightly outside the fractional bounds, or a mouseExited: while the mouse is still slightly inside the fractional bounds.
- Some controls on non-pixel boundaries may have jagged edges if the end caps do not vertically or horizontally align with the fill
- Text may jitter when scrolling if the scrollView is on a non-pixel boundary
You can use -[NSView centerScanRect:] to position a view or a rect within a view on pixel boundaries. On Tiger, this method used NSIntegralRect, which expanded the given rectangle outward as needed to land on integral pixel values. On Leopard, this method is size preserving, which results in better layout behavior when applied to adjacent rectangles. This change applies only to applications linked on Leopard or later for compatibility reasons. Alternatively, you can convert to window coordinates using -[NSView convertRectToBase:], round the result to integral values with rounding rules that suit your needs, then convert back to view coordinates using -[NSView convertRectFromBase:].
Because the scaling from points to pixels is non-integral when the user space scale factor is non-integral, you need to be sure not to use floor, ceil, or round on coordinates expressed in points. This rounding would be likely to result in non-integral pixel values, which would lead to the problems listed above.
NSOpenGL and resolution independence (Updated since WWDC 2007 Seed)
Applications and frameworks that are striving to become resolution-independent can encounter problems with OpenGL usage, due to the fact that many OpenGL API functions, such as glViewport(), expect their parameters in pixel units. Code that is accustomed to running under a user interface scale factor of 1.0 may contain latent errors where view dimensions were incorrectly treated as if they were in pixel units. For scale factors other than 1.0, the distinction between view space units and device (pixel) units becomes much more important to properly observe.
A common usage pattern in Cocoa-based OpenGL applications, for example, has been to pass a view's bounds dimensions directly to glViewport(), which expects to receive its parameters in pixel units:
- (void)reshape {
NSSize bounds = [self bounds];
// This is technically INCORRECT, because bounds is not expressed in pixel units.
glViewport(0, 0, bounds.size.width, bounds.size.height);
}
To help ease the transition to resolution independence for applications that use this common code pattern, Leopard AppKit automatically configures the bounds of any view that has an associated NSOpenGLContext (thus, NSOpenGLViews, as well as ordinary NSViews that are drawn into using an NSOpenGLContext) so that the bounds are expressed in pixel units, according to the current user interface scale factor. So for example, if an application has an NSOpenGLView whose frame size is 100x100 points, and that application is run at a user interface scale factor of 1.25, the NSOpenGLView's frame will remain 100x100 points, but its bounds will be reported as 125x125. That enables commonly used code constructs such as the above -reshape method to function correctly without code changes.
While this automatic workaround may suffice to provide compatibility for many applications, the ideal solution is for OpenGL client code to perform correct unit conversions where needed. For example, the above -reshape method would be more correctly written as:
- (void)reshape {
// Convert up to window space, which is in pixel units.
NSSize boundsInPixelUnits = [self convertRect:[self bounds] toView:nil];
// Now the result is glViewport()-compatible.
glViewport(0, 0, boundsInPixelUnits.size.width, boundsInPixelUnits.size.height);
}
Code that targets Mac OS 10.5 and later can use the -convertRectToBase: method instead of converting to view nil, which has the advantage of correctly producing a result in pixel units regardless of whether the view is layer-backed. (If the view is layer-backed, -convertRectToBase: converts to the coordinate space of the layer's backing store, instead of to the window's coordinate space.)
Text rendering and resolution independence
When rendering with non-integral scale factor, we recommend you use the antialiased text rendering mode. Since the glyph origin might not be pixel-aligned at layout time, rendering non-antialiased text doesn't produce optimal quality.
NSRuleEditor
NSRuleEditor is a new AppKit control introduced in Leopard. NSRuleEditor allows the user to configure "rules," similar to the rules in Mail or in the Finder search window, by selecting from popups, manipulating custom views, and adding, deleting, or rearranging rows.
NSRuleEditor:
- Supports nesting and non-nesting modes
- Gets the available popups/views from its delegate
- Is bindings-compatible
- Supports automatic generation of NSPredicates
- Supports flexible localization through a strings file or dictionary
Please refer to NSRuleEditor.h and documentation for more info on this new class.
NSPredicateEditor
NSPredicateEditor is a subclass of NSRuleEditor specialized for working with predicates. Unlike NSRuleEditor, NSPredicateEditor does not depend on its delegate to populate its rows (and does not call the populating delegate methods). Instead, its rows are populated from its objectValue, which is an NSPredicate.
NSPredicateEditor relies on instances of a new class, NSPredicateEditorRowTemplate, which is responsible for mapping back and forth between the displayed view values and various predicates.
NSPredicateEditor exposes one property, rowTemplates, which is an array of NSPredicateEditorRowTemplates.
- (void)setRowTemplates:(NSArray *)rowTemplates;
- (NSArray *)rowTemplates;
Developers will typically configure NSPredicateEditor with some NSPredicateEditorRowTemplates, either programmatically or in Interface Builder, and then set and get NSPredicates on the NSPredicateEditor. Changes to the predicate are announced with the usual target/action mechanism.
NSMenu
New menu customization APIs
Leopard now allows applications to set a custom view for a menu item, via the following new NSMenuItem methods:
- (void)setView:(NSView *)view;
- (NSView *)view;
The custom view takes over all aspects of the menu item's drawing. Mouse event processing is handled normally for the view, including mouse down, mouse up, mouse moved, mouse entered, mouse exited, mouse dragged, and scroll wheel events. In non-sticky tracking mode (manipulating menus with the mouse button held down), the view will receive mouseDragged: events. See the header file NSMenuItem.h for more information about custom menu item views.
Animation in menu item views
Views in menu items can be animated with the usual mechanisms, such as calling - [NSView setNeedsDisplay:] or - [NSView display] in a repeating timer. But be aware that menu tracking occurs with the run loop running in NSEventTrackingRunLoopMode, so any timers you add must be set to fire while in that mode.
New delegate methods and notifications
Menus will now notify their delegates when they are opened or closed, and when they highlight or unhighlight items during menu tracking. See NSMenu.h for more information about these new delegate methods.
Removal of NSMenuItem protocol
All uses of id <NSMenuItem> have been removed from AppKit in Leopard. Applications should switch to the NSMenuItem class.
NSMenuItem attributed title truncation
In versions of Mac OS X prior to Leopard, NSMenuItems with excessively long attributed titles would be clipped against the edge of the menu. In Leopard, NSMenuItem attributed titles will be truncated by default using the "NSLineBreakByTruncatingMiddle" truncation style. You may override this by specifying a different line break mode for the NSParagraphStyle attached to the attributed title string.
validateItem: no longer called
Historically, AppKit has called the validateItem: method to validate menu items, if validateMenuItem: and validateUserInterfaceItem: were not implemented. This behavior was never documented and it is now disabled for applications built on Leopard. Applications that rely on on validateItem: should switch to validateMenuItem: or validateUserInterfaceItem:.
validateMenuItem: only called on matching key equivalents
Before Leopard, typing a key equivalent would cause AppKit to attempt to validate every menu item in the menu (via validateMenuItem:). As an optimization, AppKit will now only validate the item with the matching key equivalent. Applications should use NSMenu's delegate methods to lazily populate a menu.
Command key = treated like +
As a special case, if no menu item claims ⌘= as a key equivalent, then typing ⌘= will trigger any ⌘+ key equivalent, if the pressed = key is shared with the + key. For example, on a US keyboard layout, pressing the "=" key adjacent to the "-" key on the number row will trigger any ⌘+ key equivalent, but the "=" key above the keypad will not. If the keyboard layout does not have any "+" and "=" characters on the same key, this does not apply.
Key equivalent uniqueing
AppKit tries to ensure that multiple menu items do not have the same key equivalent, because otherwise the user might accidentally invoke one while expecting to invoke the other. This is called key equivalent uniqueing. In Tiger, popup button menus were uniqued with the main menu (and in Leopard, this has been extended to also cover segmented controls). However, menu items that are thought to be aliases of one another can now share the same key equivalent, in Leopard. (Menu items are considered to be an aliases if they have the same action). Thus, in Leopard, you can have "Bold" in a popup button and also in the main menu, and both will show ⌘B as the key equivalent.
Disabled key equivalents passed through
Prior to Leopard, key equivalents corresponding to disabled menu items would be ignored. In Leopard, your application now has a chance to handle these. For example, a key equivalent for control-K on a disabled menu item will no longer block the emacs shortcut in an NSTextView, in Leopard.
ß support in key equivalents
The German sharfes s character (ß) is now supported as a key equivalent.
Menu Item Key Equivalents in non-Roman Scripts (New since WWDC 2007 Seed)
In Tiger and earlier, key equivalents were incorrectly interpreted as follows: Characters were extracted as MacRoman, and the resulting values were interpreted as if they were in the application encoding. This was not a problem in apps running with the MacRoman character encoding, but would cause problems in other cases. (The default character encoding of apps often depends on the user's localization choice.)
For example, specifying a key equivalent of the backslash @"\" (U+005C) would result in a yen sign key equivalent (U+00A5) when running under a Japanese localization, because the backslash value in MacRoman matches the yen value in MacJapanese: both are 0x5C in the respective character sets. This was the only way to obtain a yen key equivalent in Tiger.
In Leopard, key equivalents and character sets are handled correctly. To specify a yen key equivalent, specify the Unicode string containing yen (U+00A5).
For binary compatibility, Leopard will revert to Tiger's behavior for applications linked before Leopard. When updating your application for Leopard, you should ensure that, when running on Leopard, your key equivalent string contains the characters you want, instead of the MacRoman characters that match your character's code points in the application encoding.
State images implemented
The NSMenuItem methods setOnStateImage:, setOffStateImage:, and setMixedStateImage: now work as documented.
No CMM plugins in 64-bit
NSMenu does not attempt to load Contextual Menu Manager plugins in 64-bit apps. There is no public interface for creating 64-bit CMM plugins in Leopard.
Pre-existing overrides of -[NSMenuItem isHidden] (New since WWDC 2007 Seed)
During testing, some apps were found to have pre-existing implementations of -[NSMenuItem isHidden] that interfered with the new Leopard API of the same name. For compatibility reasons, AppKit will not call -[NSMenuItem isHidden] on apps built on Tiger or earlier. When you build your app on Leopard, AppKit will call your method and you may see strange behavior, such as menus disappearing. We recommend that you do not override -[NSMenuItem isHidden], and remove any existing overrides at the point that you build on Leopard.
Titles of Menus in the Menubar (New since WWDC 2007 Seed)
Consider the application's main menu, an instance of NSMenu. That menu contains NSMenuItems, such as the File menu item, and the File menu item has a submenu of type NSMenu containing items such as New, Open, etc.
In Tiger, the initial label appearing in the menu bar was taken from the submenu's title. But due to a bug, changes to this menu's title did not affect the menu bar; instead, the parent item's title was used instead. So for example, the title of the File submenu determines the initial label, but to change the the label, you must modify the title of the File item itself.
For compatibility, Leopard preserves this behavior for apps built on Tiger. However, apps built on Leopard will find that the menubar consistently reflects the titles of the submenus. The titles of the items themselves in the main menu are ignored. For maximum compatibility with both Tiger and Leopard, you should set both the title of a main menu item and the title of its submenu to the same thing.
This note only applies to items that are within the NSApplication's mainMenu, and their immediate submenus. The titles of other menus have no effect.
-[NSApplication currentEvent] set when menu tracking ends
For applications linked on or after Leopard, -[NSApplication currentEvent] will reflect the event that caused menu tracking to end during the action of the selected menu item.
representedObject in NSPopUpButtonCell and NSMenuItemCell
In Leopard, NSMenuItemCell and NSPopUpButtonCell forward both setRepresentedObject: and representedObject to their current NSMenuItem. (Prior to Leopard, NSMenuItemCell and NSPopUpButtonCell would forward representedObject, but not setRepresentedObject:)
NSPopUpButtonCell setImagePosition:
NSPopUpButtonCell now respects the following image positions: NSImageOnly, NSImageLeft, NSImageRight, NSImageOverlaps. Prior to Leopard, NSPopUpButtonCell always drew with the NSImageLeft position.
Unbordered NSPopUpButtonCells
The layout of the text in unbordered popup button cells has been modified to more closely match that of NSButtonCell. For applications linked before Leopard, the layout will be preserved in some cases.
NSPopupButton Content Placement Tag binding option
The content bindings for NSPopupButton have a new option, Content Placement Tag. This provides a way of mixing static menu items with dynamically generated menu items in a popup. When this option is set to a non-zero integer value, the content bindings will only put dynamic content in place of the one menu item in the popup whose tag value matches the option value.
The typical use for this is to place some dynamically generated menu items in one part of the popup, and the follow or precede them with a menu separator and a static "action" menu item.
NSStatusItem
For consistency with NSCell and NSControl, -[NSStatusItem sendActionOn:] now returns the old mask.
NSToolbar
NSToolbar - Interface Builder support
Interface Builder 3 supports creating toolbars and toolbar items. However, you may wish to provide additional toolbar items programatically. To accomplish this, connect the delegate outlet of the NSToolbar to your desired delegate, and implement the NSToolbar delegate methods normally. The default and allowed item identifiers will be the union of those created in Interface Builder and those returned by your delegate.
NSToolbar - Show/Hide menu items
In versions of Mac OS X prior to Leopard, interface items, including menu items, that had toggleToolbarShown: set as their action would have their titles changed to "Show Toolbar" or "Hide Toolbar." For applications linked on or after Leopard, AppKit will only flip "Show Toolbar" to "Hide Toolbar" (or their localized variations) and vice versa, as appropriate. The empty title will also be modified. Nonempty titles other than the localized variants of these strings must be managed by your application.
NSToolbar - Key equivalents can match in toolbars
Before Leopard, only NSPopUpButtons in toolbars could respond to key equivalents. In Leopard, AppKit now respects the key equivalents of all controls in (visible) toolbars, not just popup buttons.
NSToolbar - NSToolbarItem default sizes
As of Leopard, if you call setView: on an NSToolbarItem without having called setMinSize: or setMaxSize:, the toolbar item will set its minimum and maximum size equal to the view's frame size.
NSToolbar - View archival
In Tiger, NSToolbar would always "copy" your toolbar item's view when displaying it in the customization palette with an NSKeyedArchiver. In Leopard, NSToolbar will only copy your view if you set the view on two different toolbar items at once. For example, say you allocate exactly one view (perhaps in a nib) and always pass it to -[NSToolbarItem setView:] in your toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar: method. If that view is in an active toolbar item, and you display the customization palette, NSToolbar will copy that view once. If that view is not in an active toolbar item and then you display the customization palette, NSToolbar will not copy the view for the customization palette, but will copy that view at the point the user drags the corresponding toolbar item from the palette into the toolbar.
If you always ensure that you pass fresh views into setView:, then NSToolbar will never copy the view.
NSToolbar - mouseDownCanMoveWindow
Previous to Leopard, only textured and unified windows were draggable by their toolbar. In Leopard, all windows are draggable by their toolbars. Apps that placed custom views in toolbars and did not override isOpaque or mouseDownCanMoveWindow may find that their views unexpectedly become draggable in addition to responding to clicks. To prevent this, the mouseDownCanMoveWindow is considered to return NO if all of the following are true:
- The app was linked on Tiger or earlier.
- The view is in a toolbar item.
- The view does not override mouseDownCanMoveWindow.
- The window is not textured or unified title/toolbar.
NSSound
NSSound has additional features on Leopard and later. These include the ability to get the duration of a sound, change the volume of a sound, set and get the playback location within the sound, and cause a sound to loop. See the documentation or comments within the NSSound.h header for more about these new APIs.
In Tiger, NSSound would willingly create a "sound" for any valid Quicktime file, including images. In Leopard, NSSound's init methods will now return nil for files that do not have a sound track.
NSSpeechSynthesizer (New since WWDC 2007 Seed)
NSSpeechSynthesizer now provides complete access to the full power of Apple's synthesizer, including full access to third-party synthesizers compatible with Apple's Speech Synthesis framework. Developers can now get and set individual synthesizer properties, pause and continue speech, stop speech at specific boundaries, conveniently set rate and volume properties, specify external dictionaries and receive additional callbacks for errors and synchronization events. Cocoa developers that formerly needed to use the Carbon Speech Synthesis API to gain access to certain synthesizer features should now be able to use the NSSpeechSynthesizer class instead.
Also new for Leopard is Alex, an English voice that leverages advanced Apple technology to deliver natural intonation even at very fast speaking rates.
NSViewController (Updated since WWDC 2007 Seed)
A new class, NSViewController, has been added to the AppKit in Mac OS 10.5. It serves roughly the same purpose as NSWindowController, but for views instead of windows. It:
• Does the same sort of memory management of top-level objects that NSWindowController does, taking the same care to prevent reference cycles when controls are bound to the nib file's owner that NSWindowController began taking in Mac OS 10.4.
• Has a generic "represented object" property, to make it easy for you to establish bindings in the nib to an object that isn't yet known at nib-loading time or readily available to the code that's doing the nib loading.
• Has an implementation of the key-value binding NSEditor informal protocol, so that objects that own view controllers can easily make bound controls in the views commit or discard the changes the user is making.
• Has a "title" property, because we expect this to be used in many situations where the user is given the opportunity to select from several named views.
NSViewController is meant to be very reusable. For example, as described below, NSPageLayout's and NSPrintPanel's -addAccessoryController: methods each take a view controller, and set the represented object to the NSPrintInfo that is to be shown to the user. When the user dismisses a printing panel, NSPageLayout and NSPrintPanel each send NSEditor messages to each accessory view controller to ensure that the user's changes have been committed or discarded properly. The titles of the accessories are gotten from the view controllers and shown to the user in pulldown menus that the user can choose from. If your application needs to dynamically associate relatively complex views with the objects that they present to the user, you might be able to reuse NSViewController in similar ways.
NSViewController is a subclass of NSResponder, but view controllers never make themselves the next responders of the controlled views. It's the responsibility of the object that puts the controlled view in a window to insert the view controller in the responder chain if that would be worthwhile. For example, after getting the view from a view controller and adding it to a superview with -[NSView addSubview:], you can set the subview's next responder to the view controller, and then set view controller's next responder to the superview.
NSPageLayout and NSPrintPanel Accessory View Enhancements (Updated since WWDC 2007 Seed)
NSPageLayout and NSPrintPanel have always had -setAccessoryView: methods that allow you to specify custom panes to be shown in page setup and print panels, respectively. However, there has been no way to specify the corresponding title that should appear in the panel's pop-up menu of panes. There has also been no way to associate summary text that should be shown for the pane's custom print settings in the print panel, or add more than one such pane to a panel. Lastly, with the introduction of built-in preview to NSPrintPanel in Mac OS 10.5 (described in the next section), we must provide a way for you to trigger the redrawing of the preview when the user changes printing parameters that you present to the user in accessory views.
New methods have been added to the NSPageLayout class to take advantage of the new NSViewController class:
- (void)addAccessoryController:(NSViewController *)accessoryController;
- (void)removeAccessoryController:(NSViewController *)accessoryController;
- (NSArray *)accessoryControllers;
When the page setup panel is presented to the user each accessory controller is automatically sent a -setRepresentedObject: message with this object's NSPrintInfo. Each controller is also automatically sent a -title message. If that returns nil the application's short name is used in the popup menu that lets the user choose an accessory view.
Slightly different new methods have also been added to the NSPrintPanel class:
- (void)addAccessoryController:(NSViewController<NSPrintPanelAccessorizing> *)accessoryController;
- (void)removeAccessoryController:(NSViewController<NSPrintPanelAccessorizing> *)accessoryController;
- (NSArray *)accessoryControllers;
These are very similar to their NSPageLayout equivalents, except the accessory controller must also conform to the new NSPrintPanelAccessorizing protocol, which has just two methods:
- (NSArray *)localizedSummaryItems;
Return the text that summarizes the settings that the user has chosen using this print panel accessory view and that should appear in the summary pane of the print panel. It must be an array of dictionaries (not nil), each of which has an NSPrintPanelAccessorySummaryItemNameKey entry and an NSPrintPanelAccessorySummaryItemDescriptionKey entry whose values are strings. A print panel acccessory view must be KVO-compliant for "localizedSummaryItems" because NSPrintPanel observes it to keep what it displays in its Summary view up to date. (In Mac OS 10.5 there is no way for the user to see your accessory view and the Summary view at the same time, but that might not always be true in the future.)
- (NSSet *)keyPathsForValuesAffectingPreview;
Return the key paths for properties whose values affect what is drawn in the print panel's built-in preview. NSPrintPanel observes these key paths and redraws the preview when the values for any of them change. For example, if you write an accessory view that lets the user turn printing of page numbers on and off in the print panel you might provide an implementation of this method that returns a set that includes a string like @"pageNumbering", as in TextEdit's PrintPanelAccessoryController class. This protocol method is optional because it's not necessary if you're not using NSPrintPanel's built-in preview, but if you use preview you almost certainly have to implement this method properly too.
Because of the requirements of this protocol, which are dictated by the realities of providing a good user interface when customizing the print panel, you must always subclass NSViewController when adding a new-style accessory view to a print panel.
The new NSPrintPanelAccessorySummaryItemNameKey and NSPrintPanelAccessorySummaryItemDescriptionKey strings are for use by implementations of NSPrintPanelAccessorizing's -localizedSummaryItems method.
The -setAccessoryView: and -accessoryView methods are now deprecated in both NSPageLayout and NSPrintPanel.
Also deprecated are NSPageLayout's -readPrintInfo and -writePrintInfo methods, and NSPrintPanel's -updateFromPrintInfo and -finalWritePrintInfo methods. They were never very useful. NSViewController provides the functionality those methods were supposed to provide; you can implement an override of the -view method to make your accessory view's UI consistent with the print info to be presented, and overrides of the KVB -commitEditing and -commitEditingWithDelegate:didCommitSelector:contextInfo: methods to make the presented print info consistent with the values showing in your accessory view.
New NSPrintPanel Methods to Control Preview and Which Standard Controls Appear
in Mac OS 10.5 a new preview feature has been added to the AppKit's printing system. The print panel now has a view that shows the user an image of the pages to be printed. It is on by default, but you can turn it off in your application. See the backward binary compatibility note at the end of this section for more information.
In many applications it is not appropriate to present page setup panels to the user, or even include a Page Setup... item in the File menu, but there has been no other easy way to let the user specify page setup parameters to be used when printing. (New UI advice: if your application doesn't persistently store page setup parameters on a per-document basis, or have some mechanism to associate them with whatever other kind of large-scale objects your application may deal with, it probably shouldn't have a page setup panel at all.) Also, some applications need to present print panels that don't include the standard fields for letting the user specify the number of copies or the range of pages to be printed.
So, In Mac OS 10.5, a new enumeration and new methods have been added to the NSPrintPanel class so that you can control previewing and what standard controls appear in the print panel. Please see the header file and documentation for info on these options.
enum {
NSPrintPanelShowsCopies = 0x01,
NSPrintPanelShowsPageRange = 0x02,
NSPrintPanelShowsPaperSize = 0x04,
NSPrintPanelShowsOrientation = 0x08,
NSPrintPanelShowsScaling = 0x10,
NSPrintPanelShowsPageSetupAccessory = 0x100,
NSPrintPanelShowsPreview = 0x20000
};
typedef NSInteger NSPrintPanelOptions;
- (void)setOptions:(NSPrintPanelOptions)options;
- (NSPrintPanelOptions)options;
In Mac OS 10.5 an -options message sent to a freshly-created NSPrintPanel will return (NSPrintPanelShowsCopies | NSPrintPanelShowsPageRange) unless it was created by an NSPrintOperation, in which case it will also return NSPrintPanelShowsPreview. To allow your application to take advantage of controls that may be added by default in future versions of Mac OS X, get the options from the print panel you've just created, turn on and off the flags you care about, and then set the options.
Backward binary compatibility note: The behavior described above applies to applications that are linked against Mac OS 10.5 or later. In any application linked against Mac OS 10.4 or earlier, the NSPrintPanelShowsPreview option is ignored if the application has set a print panel accessory view. Until Mac OS 10.5 there hasn't been any API that would allow you to cause the print preview to be redrawn when the user changes print settings using an accessory view, and if the print preview doesn't redraw then it's showing inaccurate information, and that's worse than no preview at all.
Other NSPrintPanel Enhancements
So that you can change the title of the default button in a print panel from "Print" to something else, two new methods have been added to NSPrintPanel:
- (void)setDefaultButtonTitle:(NSString *)defaultButtonTitle;
- (NSString *)defaultButtonTitle;
The title of the default button in the print panel. You can override the standard button title, "Print," when you're using an NSPrintPanel in such a way that printing isn't actually going to happen when the user presses that button.
So that you can change the help anchor for the question mark button in a print panel to point to something customized for your application, two other new methods have been added to NSPrintPanel:
- (void)setHelpAnchor:(NSString *)helpAnchor;
- (NSString *)helpAnchor;
The HTML help anchor for the print panel. You can override the standard anchor of the print panel's help button.
NSPrintPanel's -runModal method always uses the current NSPrintOperation's NSPrintInfo, so there has been no way to present an application-modal panel that uses any other NSPrintInfo. In Mac OS 10.5 a new method has been added to fix that:
- (NSInteger)runModalWithPrintInfo:(NSPrintInfo *)printInfo;
The default implementation of -runModal now simply invokes [self runModalWithPrintInfo:[[NSPrintOperation currentOperation] printInfo]].
An accessor for the NSPrintInfo that's being presented to the user for editing has also been added:
- (NSPrintInfo *)printInfo;
A simple accessor. Your -beginSheetWithPrintInfo:... delegate can use this so it doesn't have to keep a pointer to the NSPrintInfo elsewhere while waiting for the user to dismiss the print panel.
Access to Underlying Core Printing Objects from NSPrintInfo (Updated since WWDC 2007 Seed)
In previous versions of Mac OS X there has been no public way to get the Core Printing (previously known as "Printing Manager") objects that an NSPrintInfo is actually built on top of. To make it easy for you do to exactly the same things that the PMPrintSettingsGetValue()/PMPrintSettingsSetValue() functions added in Mac OS 10.4 let you do, a new method has been added to NSPrintInfo in Mac OS 10.5. The most immediate problem that this method solves is that there has been no way for your accessory views to put settings in a place where they can be saved and restored by the printing systems presets mechanism:
- (NSMutableDictionary *)printSettings;
The print info's print settings. You can put values in this dictionary to store them in any preset that the user creates while editing this print info with a print panel. Such values must be property list objects. You can also use this dictionary to get values that have been set by other parts of the printing system, like a printer driver's print dialog extension (the same sort of values that are returned by the Carbon Printing Manager's PMPrintSettingsGetValue() function). Other parts of the printing system often use key strings like "com.apple.print.PrintSettings.PMColorSyncProfileID" but dots like those in key strings wouldn't work well with KVC, so those dots are replaced with underscores in keys that appear in this dictionary, as in "com_apple_print_PrintSettings_PMColorSyncProfileID". You should use the same convention when adding entries to this dictionary.
Because there is a substantial amount of functionality exposed by Core Printing API that is not generally applicable enough to be exposed by AppKit API, new methods have been added to NSPrintInfo in Mac OS 10.5 to allow access to the underlying Core Printing objects:
- (void * /* PMPrintSession */)PMPrintSession;
- (void * /* PMPageFormat */)PMPageFormat;
- (void * /* PMPrintSettings */)PMPrintSettings;
Return a Core Printing PMPrintSession, PMPageFormat, or PMPrintSettings object, respectively. The returned object is always consistent with the state of the NSPrintInfo at the moment the method is invoked, but isn't necessarily updated immediately if other NSPrintInfo methods like -setPaperSize: and -setPaperOrientation: are invoked. The returned object will always be valid (in the Core Printing sense). If you set any values in the returned PMPageFormat or PMPrintSettings you should afterward invoke -updateFromPMPageFormat or -updateFromPMPrintSettings, respectively. You don't also have to call PMSessionValidatePageFormat() or PMSessionValidatePrintSettings() if you do that. You should not call PMRelease() for the returned object, except of course to balance any calls of PMRetain() you do.
- (void)updateFromPMPageFormat;
- (void)updateFromPMPrintSettings;
Given that the NSPrintInfo's PMPageFormat or PMPrintSettings has been changed by something other than the NSPrintInfo itself, updates the NSPrintInfo to be consistent.
NSPrintInfo's Key-Value Coding and Key-Value Observing Compliance (New since WWDC 2007 Seed)
Despite what the comment for -[NSPrintInfo printSettings] in <AppKit/NSPrintInfo.h> says, NSPrintInfo's KVC/KVO-compliance is not really complete enough to be useful in Mac OS 10.5. You can add a key-value observer for a key path to an NSPrintInfo but, for example, the observer won't be consistently notified of changes made by the user when the NSPrintInfo is being presented by an NSPrintPanel.
NSPrintOperation Enhancements
In previous versions of Mac OS X the print job title that the printing system uses has been gotten by invoking -[NSView(NSPrinting) printJobTitle:]. Because it is often not programmatically convenient to make such information available to printed views, new methods have been added to NSPrintOperation in Mac OS 10.5, so that the code that creates the print operation can simply set the print job title right away, and not have to leave it somewhere the printed view can find it:
- (void)setJobTitle:(NSString *)jobTitle;
- (NSString *)jobTitle;
If a job title is set it overrides anything that might be gotten by sending the printed view an [NSView(NSPrinting) printJobTitle] message.
In previous versions of Mac OS X the -currentPage method has allowed you to find out exactly which page is being printed during the running of a print operation, but there has been no easy way to determine how many pages the document being printed has. In Mac OS 10.5 a new method has been added to let you do that:
- (NSRange)pageRange;
The first page number might not be 1, depending on what the printed view returned when sent an -[NSView(NSPrinting) knowsPageRange:] message.
Support for Multiple Page Orientations per Print Operation
In Mac OS 10.5 you can now change the orientation of pages while a job is being printed. To do so your printed view must do its own pagination, and indicate so by returning YES when sent -knowsPageRange: messages. Then, when sent -rectForPage: messages, it may change the orientation of the current print operation's print info, and Cocoa will heed the change. For example: [[[NSPrintOperation currentOperation] printInfo] setOrientation:theNewOrientation].
Deprecated NSPrintOperation Methods, and a Bug Fix in -[NSPrintOperation printPanel]
In Mac OS 10.5 the -setAccessoryView: and -accessoryView methods are now also deprecated in NSPrintOperation.
Also deprecated are the -setJobStyleHint: and -jobStyleHint: methods. Instead of sending one of those messages to an NSPrintOperation, just invoke -[NSPrintOperation printPanel] and send them to the returned NSPrintPanel instead. -[NSPrintOperation printPanel] has been updated to create and return a new print panel if none has been set yet. (It would usually just return nil before.)
Bug Fixes in -[NSPrintInfo paperName], -[NSPrintInfo setPaperName:], and -[NSPrinter pageSizeForPaper:]
In Mac OS 10.2 through Mac OS 10.4 there was a bug in -[NSPrintInfo paperName] that would cause it to return incorrect paper names. For example, it might return "na-letter" or "Letter" when it should return "LetterSmall" (remember, the paper "names" returned by -paperName are not for showing to users; use -localizedPaperName to get those). Likewise, -[NSPrintInfo setPaperName:] could set a paper that had the same size as the named paper but a different name. These problems were particularly noticeable when a printer supports multiple "papers" that have the same sizes but different imageable areas. The bug has been fixed in Mac OS 10.5.
In earlier versions of Mac OS X -[NSPrinter pageSizeForPaper:] only worked when passed strings that are paper names, not paper IDs, in the senses established by the Core Printing API. In Mac OS 10.5 this method now always works when passed paper IDs (the sort of string now always returned by -[NSPrintInfo paperName]). For backward binary compatibility it returns the same values that it would have in Mac OS 10.4 if passed a paper name.
Bug Fixes in NSPrintOperation/NSPrintPanel Cancellation
In earlier versions of Mac OS X there was a bug in which a print job would be sent to the printer even though the user had hit the Cancel button in the print progress panel, if rendering of the last page of the job had already begun. This bug has been fixed in Mac OS 10.5.
There was also a bug in which invoking [[[NSPrintOperation currentOperation] printInfo] setJobDisposition:NSPrintCancelJob] from within overrides of NSView(NSPrinting) methods did not prevent the part of the print job that had already been rendered from being sent to the printer. This bug has also been fixed in Mac OS 10.5. You can now programmatically cancel the current print operation in your overrides of any of the NSView(NSPrinting) methods.
In earlier versions of Mac OS X there was no way to distinguish between user cancellation and failure when invoking -[NSPrintOperation runOperationModalForWindow:delegate:didRunSelector:contextInfo:] or -[NSPrintPanel beginSheetWithPrintInfo:modalForWindow:delegate:didEndSelector:contextInfo:]. In Mac OS 10.5 these methods always set the job disposition of the presented print info to NSPrintCancelJob when the user has cancelled the print panel, just as -[NSPrintOperation runOperation] and -[NSPrintPanel runModal] always have. For backward binary compatibility this is only done if the application is linked against Mac OS 10.4 or earlier. In the case of NSPrintOperation the "presented print info" is the one that can be gotten with -[NSPrintOperation printInfo], not the one that was passed in when the NSPrintOperation was created.
There was a bug in which an NSPrintOperation created with a print info whose job disposition was NSPrintCancelJob would never result in output, regardless of what button the user used to dismiss the print panel. This bug has been fixed.
Advice for Developers Working with Printing Margins
The "Printer Margins" in the Custom Page Sizes panel are confusingly named. They are not margins in the NSPrintInfo sense, or in the sense of Microsoft Word, AppleWorks, or any app that lets the user set a document's page margins. They are the widths and heights of the areas of the page that lie outside of the imageable area for the paper size. In other words, the Custom Page Sizes panel is giving you the opportunity to simulate hardware limitations when defining a new paper size. You can use the -[NSPrintInfo imageablePageBounds] method to get the area of page that is surrounded by the printer margins.
Support for Uniform Type Identifiers (UTIs) in NSDocument-Based Applications
If your application requires Mac OS 10.5, it may stop using these legacy Info.plist keys in its CFBundleDocumentTypes entries:
CFBundleTypeExtensions
CFBundleTypeMIMETypes
CFBUndleTypeOSTypes
NSExportableAs
It may instead use the LSItemContentTypes and (new for Mac OS 10.5) NSExportableTypes keys, and declare the corresponding UTIs as appropriate in its Info.plist. Declaration of UTIs in Info.plist files is described by <http://developer.apple.com/macosx/uniformtypeidentifiers.html>. For each CFBundleDocumentTypes entry that has an LSItemContentTypes subentry, sibling subentries with the keys listed above are ignored. (Actually Cocoa's Info.plist parsing has always ignored CFBundleTypeMIMETypes subentries. In Mac OS 10.5 it continues to. LaunchServices pays attention to them though, except when there's an LSItemContentTypes sibling. So, in Mac OS 10.5 Cocoa and LaunchServices follow the same rules about ignoring LSItemContentTypes siblings.)
If your application uses the LSItemContentTypes key it must also use the NSExportableTypes key where applicable. NSExportableTypes entries serve the same purpose as NSExportableAs entries, and are likewise subentries of CFBundleDocumentTypes entries, but their values are arrays of UTIs instead of arrays of old-style free-form type names.
Use of these keys becomes optional:
CFBundleTypeName
CFBundleTypeIconFile
For each CFBundleDocumentTypes entry that has an LSItemContentTypes subentry you can provide subentries using these keys to override the default icon and kind string that would otherwise result from UTI declarations. As has always been the case, any use of CFBundleTypeName should be accompanied by an entry in the application's InfoPlist.strings files.
The meanings of CFBundleTypeRole and NSDocumentClass subentries have not changed in Mac OS 10.5.
For each CFBundleDocumentTypes entry that has an LSItemContentTypes subentry, the UTIs are used as programmatic type names by NSDocument and NSDocumentController, as described below, instead of the value of any CFBundleTypeName subentry that might also be present.
For backward binary compatibility, none of this new behavior takes affect if the application is linked against Mac OS 10.4 or earlier. A CFBundleDocumentTypes entry that has no LSItemContentTypes subentry at all has the same meaning to Cocoa as in Mac OS 10.4.
Debugging tip: In Mac OS 10.5 you can find out what NSDocumentController thinks it's found in your application's Info.plist by executing 'print-object [[NSClassFromString(@"NSDocumentController") sharedDocumentController] _typeDescriptions]' in gdb (using the Xcode Debugger Console, for instance). Do not attempt to invoke or override -_typeDescriptions in your application. It is there just for this debugging purpose, and may disappear at any time.
Shipping tip: If you want to take advantage of UTIs in your application but have it still run on both Mac OS 10.4 and Mac OS 10.5, you can merely add LSItemContentTypes (and NSExportableTypes, where appropriate) subentries to your Info.plist, and then update your overrides of NSDocumentController and NSDocument methods so that they recognize UTIs as type names in addition to the sort of document types names that were always used in Mac OS 10.4. Be careful to add LSItemContentTypes subentries to all of the app's CFBundleDocumentTypes Info.plist entries though, or things could get more complicated. See the description of -typeForContentsOfURL:error:'s new behavior down below for instance.
Support for UTIs in NSDocumentController
In Mac OS 10.5 NSDocumentController supports UTIs. In the following paragraphs an "app-declared" UTI is one that is declared in a well-formed LSItemContentTypes subentry of a well-formed CFBundleDocumentTypes Info.plist entry, in an application that is linked against Mac OS 10.5 or later. A "recognized" UTI is app-declared or conforms to one of the app-declared UTIs.
None of the NSDocumentController methods that were deprecated in Mac OS 10.4 have been updated to handle UTIs properly. You have to stop invoking and overriding deprecated methods to take advantage of UTIs in your application.
-makeUntitledDocumentOfType:error:, -makeDocumentWithContentsOfURL:ofType:error:, and -makeDocumentForURL:withContentsOfURL:ofType:error: all now accept recognized UTIs as type strings, in addition to the sort of document type names that were accepted in Mac OS 10.4. The latter two methods have also been updated to properly handle unrecognized types too. (Because -typeForContentsOfURL:error: might now return an urecognized type.)
-URLsFromRunningOpenPanel now, for each CFBundleDocumentTypes entry that declares an openable document type (as in, has a CFBundleTypeRole of Editor or Viewer, as in Mac OS 10.4) with app-declared UTIs, passes those UTIs into -runModalOpenPanel:forTypes:. For each CFBundleDocumentTypes that declares an openable document without recognized UTIs it still passes file name extensions and encoded HFS file types as in Mac OS 10.4.
-runModalOpenPanel:forTypes: nows accepts all valid UTIs in the array of type strings, in addition to the file name extensions and encoded HFS file types that were accepted in Mac OS 10.4.
-defaultType chooses a CFBundleDocumentTypes entry and considers that document type to be the default type, as in Mac OS 10.4. Once it has made that choice it now returns the first app-declared UTI for that type, if there are any, or a document type name, of the sort returned in Mac OS 10.4, if not. (So the order of elements in the LSItemContentTypes array does matter, just as the order of elements in the CFBundleDocumentTypes array always has.)
-typeForContentsOfURL:error: now uses -[NSWorkspace typeOfFile:error:] to find out the UTI of the file located by the URL. (So it might return a UTI that is not recognized. AppKit's own invocations of it take that fact into account.) For backward binary compatibility it however first tries the same thing that it did in Mac OS 10.4 (invoke -typeFromFileExtension:, possibly twice, passing an HFS file type string for the second invocation) if there are any CFBundleDocumentTypes Info.plist entries that don't have LSItemContentTypes subentries.
-documentClassForType: now accepts any recognized UTI as the type string. As in Mac OS 10.4 it sends a +readableTypes message to each class named in the results of invoking -documentClassNames, and returns the first class whose readable types include one that matches the passed-in type. "Matches" for a UTI means "conforms to," instead of merely "is equal to." (So this method will work even when passed a UTI that is not explicitly declared in the application's Info.plist, but conforms to one that is.)
-displayNameForType: now accepts all valid UTIs as the type string, in addition to the sort of document type names that were accepted in Mac OS 10.4. It returns nil when passed an invalid UTI. For backward binary compatibility it continues to not return nil when passed a non-UTI.
-fileExtensionsFromType: and -typeFromFileExtension: have not changed, but are being deprecated. -fileExtensionsFromType: does not work when passed a UTI. -typeFromFileExtension: only works when passed a file name extension used in a CFBundleDocumentTypes entry that does not have an LSItemContentTypes subentry. In general, if each of the application's CFBundleDocumentTypes Info.plist entries has a valid LSItemContentTypes subentry, and the application doesn't invoke deprecated methods like -fileNamesFromRunningOpenPanel, then these methods will never be invoked from within Cocoa.
Support for UTIs in NSDocument
In Mac OS 10.5 NSDocument's handling of types is consistent with NSDocumentController. Every non-deprecated NSDocument method that takes a type: or ofType: argument now accepts recognized UTIs as type strings, in addition to the sort of document type names that were accepted in Mac OS 10.4. -setFileType: and -fileType: don't actually interpret the file type name in any way, so they work fine with UTIs.
None of the NSDocument methods that were deprecated in Mac OS 10.4 have been updated to handle UTIs properly. You have to stop invoking and overriding deprecated methods to take advantage of UTIs in your application.
+readableTypes, +writableTypes, and -writableTypesForSaveOperation: all now return UTIs for CFBundleDocumentTypes entries with app-declared UTIs, and document types of the sort that were returned in Mac OS 10.4 for those that don't.
+isNativeType: now accepts all valid UTIs as the type string, in addition to the sort of document type names that were accepted in Mac OS 10.4. It will return YES if a passed-in UTI conforms to a recognized UTI for the document class in question, NO otherwise.
A new method has been added to NSDocument, because -[NSDocumentController fileExtensionsFromType:] is being deprecated:
- (NSString *)fileNameExtensionForType:(NSString *)typeName saveOperation:(NSSaveOperationType)saveOperation;
For a specified type, and a particular kind of save operation, return a file name extension that can be appended to a base file name. The default implementation of this method invokes [[NSWorkspace sharedWorkspace] preferredFilenameExtensionForType:typeName] if the type is a UTI or, for backward binary compatibility with Mac OS 10.4 and earlier, invokes [[NSDocumentController sharedDocumentController] fileExtensionsFromType:typeName] and chooses the first file name extension in the returned array if not.
You can override this method to customize the appending of extensions to file names by NSDocument. In Mac OS 10.5 it's only invoked from two places within Cocoa: 1) -autosaveDocumentWithDelegate:didAutosaveSelector:contextInfo: uses this method when creating a new file name for the autosaved contents. 2) -[NSDocument(NSScripting) handleSaveScriptCommand:] uses this method when adding an extension to the file name specified by a script. In all other cases the name of any file being saved will have been fully specified by the user, with the save panel (whether they know it or not).
Support for UTIs in NSPersistentDocument
In Mac OS 10.5 NSPersistentDocument's handling of types is consistent with NSDocumentController's and NSDocument's. Every NSPersistentDocument method that takes a type string argument now accepts recognized UTIs, in addition to the sort of document type names that were accepted in Mac OS 10.4.
Support for UTIs in NSOpenPanel
In Mac OS 10.5 NSOpenPanel supports UTIs. The following methods all now accept all valid UTIs as type strings, in addition to the file name extensions and encoded HFS file types that were accepted in Mac OS 10.4:
-beginSheetForDirectory:file:types:modalForWindow:modalDelegate:didEndSelector:contextInfo:
-beginForDirectory:file:types:modelessDelegate:didEndSelector:contextInfo:
-runModalForDirectory:file:types:
-runModalForTypes:
NSOpenPanel will let the user choose files whose types conform to those identified by the passed-in UTIs. So, you can let the user select any image file by passing in a UTI like public.image. Be aware however that the set of types conforming to another can be extended by any application installed on the computer, so this might not be a good idea if your application actually has to open the files the user chooses with the open panel. Typically you'll pass in UTIs for more concrete types, like public.tiff, com.adobe.pdf, or com.apple.sketch2.
Support for UTIs in NSSavePanel
In Mac OS 10.5 NSSavePanel supports UTIs. The following methods now accept or return all valid UTIs as type strings, in addition to the file name extensions that were accepted and returned in Mac OS 10.4 (encoded HFS file types have never been valid values for these methods):
-setAllowedFileTypes:
-setRequiredFileType:
-allowedFileTypes:
-requiredFileType:
Support for UTIs in NSWorkspace
In Mac OS 10.5 NSWorkspace supports UTIs. -iconForFileType: now accepts all valid UTIs as type string, in addition to the file name extensions and encoded HFS file types that were accepted in Mac OS 10.4.
Several methods have been added to NSWorkspace:
- (NSString *)typeOfFile:(NSString *)absoluteFilePath error:(NSError **)outError;
Given an absolute file path, return the uniform type identifier (UTI) of the file, if one can be determined. Otherwise, return nil after setting *outError to an NSError that encapsulates the reason why the file's type could not be determined. If the file at the end of the path is a symbolic link the type of the symbolic link itself will be returned, not the type of the linked file. You can invoke this method to get the UTI of an existing file.
- (NSString *)localizedDescriptionForType:(NSString *)typeName;
Given a UTI, return a string that describes the document type and is fit to present to the user, or nil for failure. You can invoke this method to get the name of a type that must be shown to the user, in an alert about your application's inability to handle the type, for instance.
- (NSString *)preferredFilenameExtensionForType:(NSString *)typeName;
Given a UTI, return the best file name extension to use when creating a file of that type, or nil for failure. You can invoke this method when your application has only the base name of a file that's being written and it has to append a file name extension so that the file's type can be reliably identified later on.
- (BOOL)filenameExtension:(NSString *)filenameExtension isValidForType:(NSString *)typeName;
Given a file name extension and a UTI, return YES if the file name extension is a valid tag for the identified type, NO otherwise. You can invoke this method when your application needs to check if a file name extension can be used to reliably identify the type later on. For example, NSSavePanel uses this method to validate any extension that the user types in the panel's file name field.
- (BOOL)type:(NSString *)firstTypeName conformsToType:(NSString *)secondTypeName;
Given two UTIs, return YES if the first "conforms to" to the second in the uniform type identifier hierarchy, NO otherwise. This method will always return YES if the two strings are equal, so you can also use it with other kinds of type name, including those declared in CFBundleTypeName Info.plist entries in apps that don't take advantage of the support for UTIs that was added to Cocoa in Mac OS 10.5. You can invoke this method when your application must determine whether it can handle a file of a known type, returned by -typeOfFile:error: for instance. Use this method instead of merely comparing UTIs for equality.
Support for UTIs in NSPasteboard (Updated since WWDC 2007 Seed)
In Mac OS 10.5 NSPasteboard supports UTIs. Every NSPasteboard method that takes a type string or type string array argument now accepts UTIs as type strings, in addition to the sort of pasteboard type names that were accepted in Mac OS 10.4.
-types now returns an array that contains UTIs, as well as the pasteboard type names that would be returned in Mac OS 10.4.
When one of your application's pasteboard owners' -pasteboard:provideDataForType: methods is invoked it will still always be passed the same string that was specified in the promising invocation of -declareTypes:owner: or -addTypes:owner.
When -availableTypeFromArray: encounters a UTI in the type array provided to it, it will return that UTI if the exact UTI exists anywhere in the pasteboard's array of types. If no pasteboard type matches the UTI exactly, the first type on the pasteboard that conforms to the UTI will be returned.
Support for UTIs in NSView and NSWindow (Updated since WWDC 2007 Seed)
Likewise, in Mac OS 10.5 NSView and NSWindow support UTIs. NSView and NSWindow's -registerForDraggedTypes: methods now accept UTIs as type strings, in addition to the sort of pasteboard type names that were accepted in Mac OS 10.4. NSView's -dragPromisedFilesOfTypes:fromRect:source:slideBack:event: method now accepts UTIs as type strings, in addition to the sort of file name extensions that were accepted in Mac OS 10.4.
For UTIs registered as dragged types, UTI conformance is checked instead of equality to determine if a dragging destination should be given a chance to handle a drag. For example, a view with the UTI kUTTypeImage registered as a dragged type will have its dragging destination methods called to handle a drag in its bounds when the dragging pasteboard contains any type that conforms to kUTTypeImage.
Support for UTIs in Services
You can now specify declared UTIs instead of pasteboard types as the elements of the NSSendTypes or NSReturnTypes arrays in the Services declaration part of an application's Info.plist.
Support for UTIs in Miscellaneous AppKit Classes
In earlier versions of Mac OS X, these four classes:
NSImage
NSImageRep
NSSound
NSAttributedString (in the NSAttributedStringKitAdditions category)
have all had pairs of methods that return either arrays of file type strings (file name extensions and encoded HFS file types) or pasteboard type strings. In Mac OS 10.5, these methods are joined by single new methods that just return arrays of UTIs. Also, NSImageRep's methods that take file type or pasteboard type strings are joined by a new method that just takes UTIs.
It's important to keep in mind when working with UTIs that mere string equality checking is not the correct way to check if the type identified by one UTI "conforms" to the type identified by another. See the description of the -[NSWorkspace type:conformsToType:] in the "Support for UTIs in NSWorkspace" section.
The follow sections contain the details of this change for each class.
Support for UTIs in NSImage
In NSImage, these new methods:
+ (NSArray *)imageTypes;
+ (NSArray *)imageUnfilteredTypes;
join these methods, which might be deprecated in a future release of Mac OS X, but are not yet:
+ (NSArray *)imageFileTypes;
+ (NSArray *)imagePasteboardTypes;
+ (NSArray *)imageUnfilteredFileTypes;
+ (NSArray *)imageUnfilteredPasteboardTypes;
(The old methods are not yet deprecated because you might still have a reason to override them, because the -initWithContentsOfFile:, -initWithContentsOfURL:, -initByReferencingFile:, -initByReferencingURL:, -initWithPasteboard:, and +canInitWithPasteboard: methods have not yet been updated to use UTIs when deciding which subclass of NSImageRep should be instantiated. The same is true of -[NSBundle(NSBundleImageExtension) pathForImageResource:].)
Support for UTIs in NSImageRep
In NSImageRep, these new methods:
+ (Class)imageRepClassForType:(NSString *)type;
+ (NSArray *)imageTypes;
+ (NSArray *)imageUnfilteredTypes;
join these methods, which might be deprecated in a future release of Mac OS X, but are not yet:
+ (Class)imageRepClassForFileType:(NSString *)type;
+ (Class)imageRepClassForPasteboardType:(NSString *)type;
+ (NSArray *)imageFileTypes;
+ (NSArray *)imagePasteboardTypes;
+ (NSArray *)imageUnfilteredFileTypes;
+ (NSArray *)imageUnfilteredPasteboardTypes;
(The old methods are not yet deprecated because you might still have a reason to override them, because the +imageRepsWithContentsOfFile:, +imageRepWithContentsOfFile:, +imageRepsWithContentsOfURL:, +imageRepWithContentsOfURL:, +imageRepsWithPasteboard:, +imageRepWithPasteboard:, and +canInitWithPasteboard: methods have not yet been updated to use UTIs when deciding which subclass of NSImageRep should be instantiated, or whether a subclass can be instantiated, in the case of the last method.)
Support for UTIs in NSSound
In NSSound, this new method:
+ (NSArray*)soundUnfilteredTypes;
replaces these deprecated methods:
+ (NSArray *)soundUnfilteredFileTypes;
+ (NSArray *)soundUnfilteredPasteboardTypes;
Support for UTIs in AppKit's NSAttributedStringKitAdditions Category on NSAttributedString
In NSAttributedString(NSAttributedStringKitAdditions), these new methods:
+ (NSArray *)textTypes;
+ (NSArray *)textUnfilteredTypes;
replace these deprecated methods:
+ (NSArray *)textFileTypes;
+ (NSArray *)textPasteboardTypes;
+ (NSArray *)textUnfilteredFileTypes;
+ (NSArray *)textUnfilteredPasteboardTypes;
The -initWithURL:options:documentAttributes:error:, -initWithPath:documentAttributes:, and -initWithURL:documentAttributes: methods have all been updated to use UTIs when appropriate. So have NSMutableAttributedString(NSMutableAttributedStringKitAdditions)'s -readFromURL:options:documentAttributes:error: and -readFromURL:options:documentAttributes: methods.
Rewritten NSDocument Safe Saving, and a Bug Fixing for Saving Documents That Change From Plain Files to File Packages
-[NSDocument writeSafelyToURL:ofType:forSaveOperation:error:] has been rewritten to use CarbonCore's new FSPathReplaceObject() function. Some kinds of metadata, like extended attributes and access control lists, will now more often be properly preserved during document saving, especially of file packages. Also, safe document saving is now a little safer, particularly when the disk being written to is full. For example, your users will no longer see their documents get renamed with a "~" on the end, and left that way, when document saving fails because there is not enough space on disk to save a new document revision.
In Mac OS 10.4 and earlier there was a bug in which NSDocument would malfunction when a document that was a plain file on disk was overwritten with a file package of the same name during a save operation. (Some applications use the same file name extension for both the flat-file and the directory-based variants of what is conceptually, as far as the user is concerned, the same file format.) This bug has been fixed in Mac OS 10.5.
NSDocument Checking for Modified Files At Saving Time
In Mac OS 10.5 -[NSDocument saveDocumentWithDelegate:didSaveSelector:contextInfo:] now checks to see if the document's file has been modified since the document was opened or most recently saved or reverted, in addition to the checking for file moving, renaming, and trashing that it has done since Mac OS 10.1. When it senses file modification it presents an alert telling the user "This document’s file has been changed by another application since you opened or saved it," giving them the choice of saving or not saving. For backward binary compatibility this is only done in applications linked against Mac OS 10.5 or later.
When updating your application to link against Mac OS 10.5, keep in mind that it's usually more appropriate to invoke one of NSDocument's -save… methods in your application code than one of the -write… methods. The -write… methods are there primarily for you to override. -saveToURL:ofType:forSaveOperation:error:, which is the method that's meant to always be invoked during document saving, invokes -setFileModificationDate: with the file's new modification date after it's been written (for NSSaveOperation and NSSaveAsOperation only).
Likewise, it's usually more appropriate to invoke one of NSDocument's -revert… methods in your application code code than one of the -read… methods. The -read… methods are there primarily for you to override. -revertToContentsOfURL:ofType:error:, which is the method that's meant to always be invoked during rereading of an open document, invokes -setFileModificationDate: with the file's modification date after it's been read.
Bug Fix in -[NSDocument isDocumentEdited], and New Constant Used with -[NSDocument updateChangeCount:]
In previous versions of Mac OS X there has been a bug in which saving a document, undoing changes, and then making an equal number of new changes would cause the document to appear unmodified. If the user closed the document it would simply be closed, with no warning about unsaved changes, which would be lost. This happened in any application in which the document did not send [self updateChangeCount:NSChangeCleared] during document saving (which you're not supposed to have to do). This bug has been fixed in Mac OS 10.5, by virtue of NSDocument now drawing a distinction between doing and redoing of changes. It no longer invokes [self updateChangeCount:NSChangeDone] when it receives an NSUndoManagerDidRedoChangeNotification. Now it invokes [self updateChangeCount:NSChangeRedone] instead. (NSChangeDone is still used for NSUndoManagerWillCloseUndoGroupNotification.) NSChangeRedone is new, and declared in <AppKit/NSDocument.h>.
For backward binary compatibility NSDocument only uses NSChangeRedone instead of NSChangeDone in applications linked against Mac OS 10.5 or later, or if -updateChangeCount: is not overridden.
Bug Fix in NSDocument for Nested Undo Manager Groups
In previous versions of Mac OS X there was a bug in which NSDocument's undo support did not take into account nested undo manager groups. Each NSDocument would merely send itself a [self updateChangeCount:NSChangeDone] message whenever it received an NSUndoManagerWillCloseUndoGroupNotification from its undo manager. This would cause it to improperly count how many changes the user had made so that, for example, making one undoable change represented by multiple actions in nested undo groups, and then undoing that change, would still show the document as modified. This problem was particularly noticeable in document-based Core Data applications, because NSManagedObjectContext uses nested undo manager groups. This has been fixed in Mac OS 10.5. NSDocument now checks the undo manager's grouping level when it receives NSUndoManagerWillCloseUndoGroupNotification, and only invokes -updateChangeCount: if the nesting level is less than two.
Bug Fix in -[NSDocument writeToURL:ofType:error:]'s Use of NSFileWrapper
In Mac OS 10.4, -[NSDocument writeToURL:ofType:error:] passed NO as the last argument when invoking -[NSFileWrapper writeToFile:atomically:updateFilenames:]. In Mac OS 10.5 it now passes YES so that the file wrapper and any file wrappers it contains have their file names updated during saving. -[NSDocument writeToFile:ofType:], which was deprecated in Mac OS 10.4, has not been updated in a similar way.
Bug Fix in NSDocument's Use of -[NSDocument autosavingFileType]
In Mac OS 10.4, -[NSDocument autosaveDocumentWithDelegate:didAutosaveSelector:contextInfo:] would invoke [self autosavingFileType] the first time a document was autosaved, create an autosaved contents file URL using the implied file name extension, and then keep using that URL for all subsequent autosaves. If -autosavingFileType was overridden to return different values at different times, this could result in inconsistencies. For example, the new version of TextEdit in Leopard that uses autosaving could end up autosaving an RTFD file package with an ".rtf" file name extension. (Because it overrides -autosavingFileType to account for attachments being added to the document.) This was a bug, and has been fixed in Mac OS 10.5. NSDocument now consistently uses the result of invoking -autosavingFileType when determining where to autosave.
Advice for Overriders of -[NSDocument autosavingFileType]
Even with the bug fixed mentioned above, overriding -autosavingFileType can result in incorrect behavior during reopening of autosaved documents if you're not careful. -[NSDocument initForURL:withContentsOfURL:ofType:error:], which is invoked during reopening of autosaved documents after a crash, takes two URLs, but only the type name of the autosaved contents file. The default implementation invokes [self setFileType:] with that type name, but that is often not the right thing to do, if -autosavingFileType had returned something other than -fileType during document autosaving. If you override -autosavingFile, you probably have to override -initForURL:withContentsOfURL:ofType:error: too, and make the override invoke -setFileType: with the type of the actual document file, after invoking super. See TextEdit's Document class for an example of how to do this.
Advice for Overriders of NSDocument Reading and Writing Methods
If you subclass NSDocument and override any of these methods for reading:
-readFromData:ofType:error:
-readFromFileWrapper:ofType:error:
-readFromURL:ofType:error:
Or any of these for writing:
-dataOfType:error:
-fileWrapperOfType:error:
-writeToURL:ofType:error:
-writeToURL:ofType:forSaveOperation:originalContentsURL:error:
-fileAttributesToWriteToURL:ofType:forSaveOperation:originalContentsURL:error:
-writeSafelyToURL:ofType:forSaveOperation:error:
Or any of the methods that were deprecated in favor of them, in Mac OS 10.4:
-dataRepresentationOfType:
-fileAttributesToWriteToFile:ofType:saveOperation:
-fileWrapperRepresentationOfType:
-initWithContentsOfFile:ofType:
-initWithContentsOfURL:ofType:
-loadDataRepresentation:ofType:
-loadFileWrapperRepresentation:ofType:
-readFromFile:ofType:
-readFromURL:ofType:
-writeToFile:ofType:
-writeToFile:ofType:originalFile:saveOperation:
-writeToURL:ofType:
-writeWithBackupToFile:ofType:saveOperation:
Don't invoke -fileURL (or -fileName, the method that was deprecated in favor if it, in Mac OS 10.4), -fileType, or -fileModificationDate from within your overrides. During reading, which typically happens during object initialization, there is no guarantee that NSDocument properties like the file's location or type have been set yet. Your overridden method should be able to determine everything it needs to do the reading from the passed-in parameters. During writing, your document may be being asked to write its contents to a different location, or using a different file type. Again, your overridden method should be able to determine everything it needs to do the writing from the passed-in parameters.
If your override cannot determine all of the information it needs from the passed-in parameters, consider overriding another method. For example, if you see the need to invoke -fileURL from within an override of -readFromData:ofType:error:, perhaps you should instead override -readFromURL:ofType:error:. For another example, if you see the need to invoke -fileURL from within an override of -writeToURL:ofType:error:, perhaps you should instead override -writeToURL:ofType:forSaveOperation:originalContentsURL:error:.
Advice for Overriders of -[NSDocument displayName]
Some applications have subclasses of NSDocument that override -displayName to customize the the titles of windows associated with the document. That is usually not the right thing to do. Use a subclass of NSWindowController, and override -[NSWindowController windowTitleForDocumentDisplayName:] instead. If even deeper customization is required override -[NSWindowController synchronizeWindowTitleWithDocumentName]. A document's display name is used in several other places where the custom value that an application might want to use as a window title is typically not appropriate:
- In error alerts that may be presented during reverting, saving, or printing of the document.
- In alerts that will be presented during document saving if the document has been moved, renamed, or put in the trash.
- In the alert that will be presented when the user attempts to close the document with unsaved changes.
- As the default value shown in the "Save As:" field of save panels.
It may be used in even more places in future releases of Mac OS X.
Advice for Overriders of -[NSDocument init]
If you subclass NSDocument and override -init, make sure your override never returns nil. When running on Mac OS 10.4 this will likely cause a crash in AppKit. In Mac OS 10.5 it will cause AppKit to present an error alert that is not very useful to the user (for example, "No document could be created."). If, for example, you want to prevent the creation or opening of documents under circumstances unique to your application, override a specific NSDocumentController method instead. Read the next section before doing so.
Advice for Overriders of Methods That Return NSErrors by Reference
In Mac OS 10.4 we published many new methods that return NSErrors. For example, in NSDocumentController:
- (id)openUntitledDocumentAndDisplay:(BOOL)displayDocument error:(NSError **)outError;
- (id)openDocumentWithContentsOfURL:(NSURL *)absoluteURL display:(BOOL)displayDocument error:(NSError **)outError;
etc. When overriding such methods take care to follow this rule: a method that takes an error:(NSError **)outError argument must, if it returns a value that signals failure (typically nil or NO), and if outError!=NULL, set the value of *outError to point to an NSError. It is not the responsibility of code that invokes such methods to nil-initialize the variable whose address is taken and passed as the error parameter, just so it can safely check to see if the variable's value is no longer nil after the invocation.
If you're overriding such a method to prevent some action, but you don't want an error alert to be presented to the user, return an error whose domain is NSCocoaErrorDomain and whose code is NSUserCancelledError. AppKit itself consistently presents NSErrors to the user with the machinery described at <http://developer.apple.com/documentation/Cocoa/Conceptual/ErrorHandlingCocoa/index.html>. Unless your application overrides AppKit's error presentation methods in novel ways, use of this machinery consistently results in invocations of -[NSApplication presentError:] or -[NSApplication presentError:modalForWindow:delegate:didPresentSelector:contextInfo:]. Both of these methods silently ignore NSCocoaErrorDomain/NSUserCancelledError errors. So, for example:
- (id)openDocumentWithContentsOfURL:(NSURL *)absoluteURL display:(BOOL)displayDocument error:(NSError **)outError {
/* The user double-clicked on a document in the Finder or something, but we don't want to
open it yet if our application's custom licensing panel (for example) is being shown
as an application-modal dialog right now.
*/
id openedDocument = nil;
if (_licensingPanelIsShown) {
/* Defer the opening of the document until the user has dismissed the licensing panel.
*/
... Left as an exercise to the reader ...
/* We're about to return nil, so we _must_ set *outError to something, unless of course outError is NULL.
Return an error that won't result in the presentation of an error alert. Regular Cocoa memory
management rules dictate that the invoker of this method is not responsible
for releasing the NSError, but of course +[NSError error:code:userInfo:] returns an autoreleased
object, so this is all correct.
*/
if (outError) {
*outError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil];
}
} else {
/* Just do the regular Cocoa thing. We don't have to touch outError here.
NSDocumentController's implementation of this method has
to follow the rules too, so it sets *outError if it returns nil and outError!=NULL.
*/
openedDocument = [super openDocumentWithContentsOfURL:absoluteURL display:displayDocument error:outError];
}
return openedDocument;
}
Advice for Overriders of Methods that Follow the delegate:didSomethingSelector:contextInfo: Pattern
There are methods in the AppKit, especially in the NSDocument and DocumentController classes, that all have pretty much the same three parameters:
- A delegate object to be notified when the method's operation has been completed.
- The selector of a method to invoke to do the notifying.
- A "context info," which is just a value to pass back to the delegate so it can continue with a larger overall operation, free memory, etc.
Each method is this way because a sheet may be shown during the operation performed by the method. Such methods have to return before the user has dismissed the sheet (because of the way user event dispatching is done in the AppKit), when the result of the operation is still unknown. The delegate:didSomethingSelector:contextInfo: pattern is used so that the result can be passed to the object that requested the operation when the result is finally known.
You may discover a need to override one of these methods in your application. This is easy when the override has to add some custom behavior before invoking the superclass' implementation, but it's really not obvious how to write the override when the custom behavior goes after invoking the superclass' method. Here's an example of how to do that, in a subclass of NSDocument:
- (void)canCloseDocumentWithDelegate:(id)delegate
shouldCloseSelector:(SEL)shouldCloseSelector
contextInfo:(void *)contextInfo {
/* No matter what happens, the original delegate must be messaged (to prevent memory leaks, at the
very least). Because we're not going to pass the passed-in parameters to super, we have
to record them somewhere. The easy place to record them is in the NSInvocation we're going
to create anyway to message the original delegate. The method selected by shouldCloseSelector
must have the same signature as...
- (void)document:(NSDocument *)document shouldClose:(BOOL)shouldClose contextInfo:(void *)contextInfo;
...and that dictates how we build our invocation. We don't set a value for the shouldClose:
argument (atIndex:3) because we don't know the value yet.
*/
NSInvocation *originalDelegateInvocation = [NSInvocation invocationWithMethodSignature:
[delegate methodSignatureForSelector:shouldCloseSelector]];
[originalDelegateInvocation setTarget:delegate];
[originalDelegateInvocation setSelector:shouldCloseSelector];
[originalDelegateInvocation setArgument:&self atIndex:2]; // document:
[originalDelegateInvocation setArgument:&contextInfo atIndex:4]; // contextInfo:
/* Do the regular NSDocument thing, arranging to take back control afterward. We must retain
the invocation object here because contextInfo: arguments are not automatically retained.
*/
[super canCloseDocumentWithDelegate:self
shouldCloseSelector:@selector(thisDocument:shouldClose:contextInfo:)
contextInfo:[originalDelegateInvocation retain]];
}
- (void)thisDocument:(NSDocument *)document shouldClose:(BOOL)shouldClose contextInfo:(void *)contextInfo {
NSInvocation *originalDelegateInvocation = (NSInvocation *)contextInfo;
// Is the document about to be closed?
if (shouldClose) {
// Here we can do all sorts of things with this document that's about to be closed.
}
/* A little bit of UI advice: changing the value of shouldClose here might result in confusing
behavior. For example, if the user hit the Save button in a "Do you want to save the changes..."
panel, and the save succeeded, and shouldClose is YES, canceling closing by changing
shouldClose to NO before messaging the delegate would be an odd thing to do.
*/
// Tell the original delegate that the decision to close this document or not has been made.
[originalDelegateInvocation setArgument:&shouldClose atIndex:3];
[originalDelegateInvocation invoke];
// Balance the retain we did up above.
[originalDelegateInvocation release];
}
Bug Fix for NSDocument/NSPersistentDocument Quitting-Time Hang When Using Bindings
In Mac OS 10.4 there was a bug in NSDocument that would cause an application to hang if:
- A document window has a control with a binding to a property of the document or, in a CoreData app, a managed object in the persistent document's managed object context.
- The user edits using the control but does not cause the editing to be committed (as in, types in an edit field but does not hit tab).
- The user tries to the quit the application.
- The user hits the Save button in the "Do you want to save the changes...?" alert that's presented.
This bug has been fixed in Mac OS 10.5.
New Behavior in NSWindowController at Window Closing Time
In previous versions of Mac OS X, each NSWindowController registered for NSWindowWillCloseNotification from its window and then did a number of things when it got the notification if it had a document (like removing itself from the document's window controllers and trying to close document). This way of doing things caused several problems, including:
• Other recipients of NSWindowWillCloseNotification would find that the window controller already had no document when they received their notification.
• The NSWindowController would not get the NSWindowWillCloseNotification, and the document would not be closed, if the window controller had been the window's delegate and then the window's delegate was reset (because of the way -[NSWindow setDelegate:] deregisters the previous delegate as an observer of notifications).
In Mac OS 10.5 an NSWindow that has a window controller now sends it a private message when the window did (not will) close, fixing those and other problems. NSWindowControllers no longer register for or depend on receiving NSWindowWillCloseNotification.
Also, a change that's similar to one in -[NSWindowController dealloc] has been made, and the window controller itself is now released instead of autoreleased (to balance a [self retain] that the window controller does before sending -removeWindowController:self to its document). For backward binary compatibility the new behavior is only done in applications linked against Mac OS 10.5 or later.
Bug Fixes in -[NSWindowController dealloc]
-[NSWindowController setWindow:] makes the window control the next responder of the window, which is as designed. However, this could result in the NSWindow messaging the NSWindowController's zombie, depending on which object was deallocated first. In Mac OS 10.5, -[NSWindowController dealloc] fixes this problem by sending the window -setNextResponder:nil if the window controller has a window and is its next responder.
In previous versions of Mac OS X, -[NSWindowController dealloc] autoreleased the window and the top level objects from the nib it loaded. This made it needlessly difficult to debug programming mistakes in applications. In Mac OS 10.5 -[NSWindowController dealloc] now releases the window and top level objects instead. For backward binary compatibility the new behavior is only done in applications linked against Mac OS 10.5 or later.
Advice for People Setting up Nibs that Have NSWindowController File's Owners
NSWindowController has never depended on being the controlled window's delegate to do its job. NSWindowController doesn't even implement any NSWindow delegate methods. A subclass of NSWindowController is a fine place to put implementations of NSWindow delegate methods, and if you do so you'll probably need to connect the delegate outlet of a window in a nib to the nib file's owner, an instance of your NSWindowController subclass, but you do not have to do so on NSWindowController's account.
New Behavior for NSWindowController Frame Autosave Names
In previous versions of Mac OS X, -[NSWindowController setWindow:], which is typically invoked at nib-loading time, always set the frame autosave name of the window to the window controller's own frame autosave name. This was a significant inconvenience because it meant that the name you set in the window using Interface Builder was always effectively ignored. In Mac OS 10.5 -[NSWindowController setWindow:] now only sets the window's frame autosave name if its own is something other than nil or the empty string. This means that a window's frame autosave name is now useful even when the window is loaded by a window controller. You can still explicitly invoke -[NSWindowController setWindowFrameAutosaveName:] if for some reason you need to override the window's frame autosave name at runtime. For backward binary compatibility the new behavior is only done in applications linked against Mac OS 10.5 or later.
NSSplitView Enhancements (Updated since WWDC 2007 Seed)
Several enhancements have been made to NSSplitView in Mac OS 10.5. For detailed information on all of the methods mentioned here, see the comments in <AppKit/NSSplitView.h>.
So that you can programmatically set the position of a divider to an arbitrary position, a new -setPosition:ofDividerAtIndex: method has been added.
So that you can programmatically query the range of useful values that can be passed to -setPosition:ofDividerAtIndex:, or implement relatively complex behaviors in split view delegate methods, new -minPossiblePositionOfDividerAtIndex: and -maxPossiblePositionOfDividerAtIndex: methods have been added.
You can programmatically collapse a subview by invoking -minPossiblePositionOfDividerAtIndex: or -maxPossiblePositionOfDividerAtIndex: and passing the result to -setPosition:ofDividerAtIndex:.
To make it easier for you to let the user collapse subviews by double-clicking on dividers, a new -splitView:shouldCollapseSubview:forDoubleClickOnDividerAtIndex: delegate method has been added.
Autosaving of divider positions and subview collapsing has been added to NSSplitView. It's controlled by the new -setAutosaveName: and -autosaveName: methods.
NSSplitView now puts an NSSplitViewDividerIndex entry in the user info dictionaries of NSSplitViewWillResizeSubviewsNotification and NSSplitViewDidResizeSubviewsNotification notifications it sends, and passes to the delegate's -splitViewWillResizeSubviews: and -splitViewDidResizeSubviews: methods, when the user is dragging a divider, or has double-clicked on a divider to collapse a subview, or when -setPosition:ofDividerAtIndex: is being invoked.
So that you can easily configure split views with thin dividers, NSSplitView now has -setDividerStyle: and -dividerStyle methods. The two possible styles are NSSplitViewDividerStyleThick and NSSplitViewDividerStyleThin. The default is thick.
In case NSSplitView's default divider color does not look good in in the context of your application's UI, NSSplitView now has a -dividerColor method. It's invoked by -[NSSplitView drawDividerInRect:], and returns a value based on the split view's style. You can override it to customize.
Note: -[NSSplitView dividerColor] has changed since the WWDC 2007 seed. It now returns [NSColor clearColor] instead of [[self window] backgroundColor] for the thick divider style. Also, for backward binary compatibility it behaves differently in apps linked against Mac OS 10.4 or earlier. In those older apps it returns [[self window] backgroundColor] if [self isOpaque] returns YES, nil otherwise. -[NSSplitView isOpaque] has also changed since the WWDC 2007 seed. In apps linked against Mac OS 10.5 or newer it returns YES if the divider color is opaque and (because NSSplitViews don't draw backgrounds, but do adjust their subviews to cover everything but the dividers) all of the subviews are opaque. In apps linked against Mac OS 10.4 or older it returns YES if all of the subviews are opaque and [self dividerThickness] returns the same value NSSplitView's default implementation of -dividerThickness would return and [self isPaneSplitter] returns YES.
Thin dividers would be hard to drag if the user had to precisely click on them them. To give the user a bigger area to click on, the "effective" area of thin dividers is larger than the drawn area, by two points in either direction, in Mac OS 10.5. So that you can customize this behavior, when stealing mouse clicks away from adjacent controls is not appropriate for example, there is a new -splitView:effectiveRect:forDrawnRect:ofDividerAtIndex: delegate method you can implement.
Also, you can put a divider "handle" in your UI to give the user another way to drag the divider. So that you can point a split view to the handle's area and let it manage divider dragging (and the mouse cursor too), there is a new -splitView:additionalEffectiveRectOfDividerAtIndex: delegate method you can implement.
A common UI pattern now is to provide a button to show and hide one subview or another of a split view, and completely hide the divider when the subview between it and the edge of the window is hidden. To make it easy for you to do this there is a new -splitView:shouldHideDividerAtIndex: delegate method you can implement.
Bug Fixes in NSSplitView
In Mac OS 10.5 some longstanding bugs have been fixed in NSSplitView.
-[NSSplitView adjustSubviews] now does a much better job of not letting rounding errors accumulate when it is invoked repeatedly during window resizing. This fixes a wide variety of problems involving drifting, disappearing, and reappearing subviews.
NSSplitView now uses -[NSView setHidden:] when collapsing and uncollapsing a subview instead of setting the origin of the subview's frame somewhere far, far way. One result is that controls inside collapsed subviews can no longer hold onto the key focus, and can therefore no longer interfere with a window's tabbing behavior.
Accessibility: because of NSSplitView's use of -[NSView setHidden:], collapsed subviews are automatically no longer present in the accessibility hierarchy.
-[NSSplitView adjustSubviews] now reliably apportions space to uncollapsed subviews even when their total width (in vertical split views) or height (horizontal) is zero when it is invoked.
During divider dragging NSSplitView now more reliably heeds the value returned by the delegate's -splitView:constrainMaxCoordinate:ofSubviewAt: method to prevent the user from making the collapsible subview below (in a horizontal split view) or to the right of (vertical) the divider smaller than a minimum size before it is collapsed. NSSplitView also now collapses and uncollapses adjacent subviews simultaneously as the user drags the divider, when there isn't room to show both of them uncollapsed, and both are collapsible.
NSSplitView now uses NSCursor's +resizeLeftCursor and +resizeRightCursor when appropriate, instead of just using +resizeLeftRightCursor all of the time. The same goes for +resizeUpCursor and +resizeDownCursor instead of +resizeUpDownCursor.
Advice for Programming with NSSplitView
If your application needs to know when a subview is collapsed or expanded, register for the NSSplitViewDidResizeSubviewsNotification, or implement the -splitViewDidResizeSubviews: delegate method, and use -[NSSplitView isSubviewCollapsed:]. Don't, for example, assume that a view will be collapsed soon after the delegate returned YES when sent -splitView:canCollapseSubview:.
NSWindow (Updated since WWDC 2007 Seed)
New window visibility before login
We added API to specify that a window can become visible before login. Windows that need to be shown as part of login UI should have this property set. The default setting is NO.
- (BOOL)canBecomeVisibleWithoutLogin;
- (void)setCanBecomeVisibleWithoutLogin:(BOOL)flag;
New window content sharing API
We have added API to control sharing of the window content. -setSharingType: specifies whether the window content can be read and/or written from another process. The default sharing type is NSWindowSharingReadOnly, which means other processes can read the window content (eg. for window capture) but cannot modify it. If you set your window sharing type to NSWindowSharingNone, so that the content cannot be captured, your window will also not be able to participate in a number of system services, so this setting should be used with caution. If you set your window sharing type to NSWindowSharingReadWrite, other processes can both read and modify the window content.
enum {
NSWindowSharingNone = 0, // Window contents may not be read by another process
NSWindowSharingReadOnly = 1, // Window contents may be read but not modified by another process
NSWindowSharingReadWrite = 2 // Window contents may be read or modified by another process
};
typedef NSUInteger NSWindowSharingType;
- (void)setSharingType:(NSWindowSharingType)type;
- (NSWindowSharingType)sharingType;
New preferred location setting for window backing store
We have also added -setPreferredBackingLocation: to set the preferred location for the window backing store. In general, you should not use this API unless indicated by performance measurement. The default preferred location is NSWindowBackingLocationDefault, which means that the system determines whether window backing store is kept in VRAM or main memory. You can use this API to set a preferred location for your window backing store, but the system may choose a different location. You can use -backingLocation to find the current location of your window backing store.
enum {
NSWindowBackingLocationDefault = 0, // System determines if window backing store is in VRAM or main memory
NSWindowBackingLocationVideoMemory = 1, // Window backing store is in VRAM
NSWindowBackingLocationMainMemory = 2 // Window backing store is in main memory
};
typedef NSUInteger NSWindowBackingLocation;
- (void)setPreferredBackingLocation:(NSWindowBackingLocation)backingLocation;
- (NSWindowBackingLocation)preferredBackingLocation;
- (NSWindowBackingLocation)backingLocation;
New document window icon API
We have also added API to make it easier to customize the behavior of the document icon in a window title bar. Prior to Leopard, you could call -[NSWindow setTitleWithRepresentedFilename:] or -[NSWindow setRepresentedFilename:] to indicate that a window represents a file at the given path. This causes two behaviors: The window shows a document icon appropriate for the given file path. This icon is draggable, and creates an alias, copy, or generic representation of the file when dropped. Secondly, the document icon and the title form a cmd-clickable region. A cmd-click in this region shows a popup menu. Selecting an item from this menu causes it to be shown in Finder.
In Leopard, we have added API to allow a document icon for any window, given a URL. If you call setRepresentedURL: with a valid non-nil URL, the window will show a document icon in the titlebar. If the url represents a filename or other resource with a known icon, that icon will be used as the document icon. Otherwise the default document icon will be used. The icon can be customized using [[NSWindow standardWindowButton:NSWindowDocumentIconButton] setImage:customImage]. If the URL is not nil and its path is not empty, the window will have a pop-up menu which can be shown via command-click on the area containing the document icon and title. By default, this menu will display the path components of the URL. The presence and contents of this menu can be controlled by the delegate method window:shouldPopUpDocumentPathMenu:. If the URL is nil or has an empty path, the window will not show a document icon and will not have a pop-up menu available via command-click.
- (void)setRepresentedURL:(NSURL *)url;
- (NSURL *)representedURL;
If a window has a representedURL, the window will by default show a path popup menu for a command-click on a rectangle containing the window document icon button and the window title. The window delegate may implement -window:shouldPopupDocumentPathMenu: to override NSWindow's default behavior for path popup menu. A return of NO will prevent the menu from being shown. A return of YES will cause the window to show the menu passed to this method, which by default will contain a menuItem for each path component of the representedURL. If the representedURL has no path components, the menu will have no menu items. Before returning YES, the window delegate may customize the menu by changing the menuItems. menuItems may be added or deleted, and each menuItem title, action, or target may be modified.
- (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu;
The window delegate may implement -window:shouldDragDocumentWithEvent:from:withPasteboard: to override NSWindow document icon's default drag behavior. The delegate can prohibit the drag by returning NO. Before returning NO, the delegate may implement its own dragging behavior using -[NSWindow dragImage:at:offset:event:pasteboard:source:slideBack:]. Alternatively, the delegate can enable a drag by returning YES, for example to override NSWindow's default behavior of prohibiting the drag of an edited document. Lastly, the delegate can customize the pasteboard contents before returning YES.
- (BOOL)window:(NSWindow *)window
shouldDragDocumentWithEvent:(NSEvent *)event
from:(NSPoint)dragImageLocation
withPasteboard:(NSPasteboard *)pasteboard;
New dock tile access
NSWindow now has a method to get a dock tile instance, which allows you to control some aspects of the dock tile corresponding to the miniaturized window. For further discussion, see the NSDockTile section.
- (NSDockTile *)dockTile;
New window positioning algorithm
-[NSWindow center] now uses a different algorithm to position windows. The effect of this change is that a window whose height is close to or greater than 2/3 of the visible screen height will now be positioned vertically in a way that is consistent with smaller windows.
Change in standard toolbar button action
The action of the standard toolbar button now goes through the public toggleToolbarShown: method, passing the toolbar button as the sender.
New Heads Ups Display (HUD) window style
Support has been added for a Heads Up Display (HUD) window style. A HUD window can be created using NSHUDWindowMask. The window must be an NSPanel or subclass. NSHUDWindowMask may be combined with other window styles to create a borderless or titled window with a certain appearance and behavior. Both the titled and borderless window float above other windows and are partially transparent. They hide-on-deactivate, which means a HUD window will only be visible when its owning app is active. The following combinations are valid:
NSHUDWindowMask
| NSBorderlessWindowMask - borderless window with HUD transparency and window level
or
| NSTitledWindowMask | NSUtilityWindowMask - titled window with HUD transparency and window level
and any of the following:
| NSClosableWindowMask - titled window with HUD close box, transparency, and window level
| NSResizableWindowMask - titled window with HUD resize corner, transparency, and window level
| NSNonactivatingPanelMask - no effect on appearance, but owning app will not necessarily be active when this window is the key window
the following are not valid
NSMiniaturizableWindowMask - not supported
NSTexturedBackgroundWindowMask - not supported
NSDocModalWindowMask - not supported
NSUnifiedTitleAndToolbarWindowMask - not supported
A secondary click in the title bar of a window now shows the context menu of the toolbar, if any. Likewise, a double-click in the toolbar background of such a window will minimize the window if minimize on double-click is enabled. Because there is no visual separation between the title bar and the toolbar, they are now treated more as if they were one area. A secondary click in the document icon or title bar of a window with a represented URL will now show the document pop up, just as a command click does.
New window appearance
We have modified the window appearance. The window titlebar and toolbar background are drawn with a dark gradient when key or main, and a lighter gradient when inactive. The NSUnifiedTitleAndToolbarWindowMask styleMask no longer has any effect, since all windows with toolbars now have a unified look. Windows whose styleMask includes NSTexturedBackgroundWindowMask have a window background which also darkens when key or main and lightens when inactive, and may have a second gradient in the section below the window content. Windows whose styleMask does not include NSTexturedBackgroundWindowMask have a window background which is a solid fill and does not change when key, main, or inactive.
We have modified the look of main and utility windows. A window that is main and not key now has the same title bar color as a key window, but inactive window buttons. A utility window that is not key now has an inactive title bar buttons. With this change, active title bar buttons now have a more consistent correlation with the key window state.
In Leopard, textured windows have a gradient on the top and bottom section of the window where metal was previously visible.
In many cases, we can detect the appropriate area for a textured window gradient using an algorithm based on opaque views, but in some cases we cannot, since a view may declare itself opaque then draw the window background, for example. For application windows where the window background detection doesn't work, we have provided API. We hope that most windows will look correct without invoking this API.
If the automatic calculation of window gradient does not yield the correct results, the automatic calculation can be disabled with -setAutorecalculatesContentBorderThickness:NO. If this method is called without also setting a content border thickness for a given edge, the content border thickness on that edge will be 0. A content border thickness can be set by calling -setContentBorderThickness:forEdge:. Alternatively, a window subclass can override -contentBorderThicknessForEdge:. If -setContentBorderThickness:forEdge: is called other than by NSWindow's automatic calculation, and autorecalculatesContentBorderThicknessForEdge: returns YES for the given edge, the behavior is undefined. (That is, NSWindow is likely to overwrite the custom value).
Non-textured windows can also now be told to draw the window background gradient in a border on the bottom of the window. By default, non-textured windows have no bottom border.
-setContentBorderThickness:forEdge: and contentBorderThicknessForEdge: use the coordinate system of the window content, so they are in points rather than pixels. Note that the contentBorder does not include the titlebar or toolbar, so a window that just wants the gradient in the titlebar and toolbar will have a contentBorderThickness of 0 for NSMaxYEdge.
Calling -setContentBorderThickness:forEdge:NSMinXEdge/NSMaxXEdge will raise an exception. Likewise calling -setAutorecalculatesContentBorderThickness:NO forEdge:NSMinXEdge/NSMaxXEdge will raise an exception. In a non-textured window only, calling -setContentBorderThickness:forEdge:NSMaxYEdge will raise an exception, as will calling -setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge. It is only valid to set the content border thickness of the top edge in a textured window.
The behavior of -setContentBorderThickness:forEdge:NSMinYEdge and -setAutorecalculatesContentBorderThickness:NO forEdge:NSMinYEdge for non-textured windows will do the following: The top gradient will be repeated in the bottom border, separator lines will be drawn between the content and the bottom border, and the bottom corner will be rounded. Other methods on non-textured windows or unused edges will return 0.0 or YES.
- (void)setContentBorderThickness:(CGFloat)thickness forEdge:(NSRectEdge)edge;
- (CGFloat)contentBorderThicknessForEdge:(NSRectEdge)edge;
- (void)setAutorecalculatesContentBorderThickness:(BOOL)flag forEdge:(NSRectEdge)edge;
- (BOOL)autorecalculatesContentBorderThicknessForEdge:(NSRectEdge)edge;
Window behavior modified in Spaces
We deprecated an API added earlier in Leopard for Spaces and replaced it with a more general form. Windows have different behavior under Spaces based on this API.
NSWindowCollectionBehaviorDefault windows can be either document or floating. Document windows are associated with one space at a time. If you order a normal window onscreen, it becomes associated with the current space. If you then switch spaces, the window does not show up in the new space. If you switch focus back to the window which is onscreen in another space, you get switched to the space containing the window. A TextEdit document window is an example of a document window. Floating windows are also almost space independent, except that they are associated with their owning app. If you order a floating window onscreen, it orders onscreen in the current space. If you switch spaces, the floating window shows up in the new space if and only if the owning app is active. The floating window itself is not counted as a window that can cause the app to be chosen as active on a space switch. Note that floating windows are usually, but not required to be, hide on deactivate. AppKit automatically sets windows whose window-level is non-0 to floating. The TextEdit font panel is an example of a floating window.
NSWindowCollectionBehaviorCanJoinAllSpaces windows can be thought of as space independent. If you order an all-space window onscreen, it orders onscreen in the current space. If you then switch spaces, the all-space window shows up in the new space. If you switch focus to the all-space window, you stay in the current space. The menu bar is an example of this kind of window.
NSWindowCollectionBehaviorMoveToActiveSpace windows are visible on only one space at a time, but move to the active space when needed. If you order a MoveToActiveSpace window onscreen, it becomes associated with the active space (which is the current space). If you then switch spaces, the window does not show up in the new space. If you switch focus back to the MoveToActiveSpace window, it becomes visible in the active space, rather than causing a space switch like a normal window would. The AppKit find panel is an example of a this kind of window.
enum {
NSWindowCollectionBehaviorDefault = 0,
NSWindowCollectionBehaviorCanJoinAllSpaces = 1 << 0,
NSWindowCollectionBehaviorMoveToActiveSpace = 1 << 1
};
typedef NSUInteger NSWindowCollectionBehavior;
- (void)setCollectionBehavior:(NSWindowCollectionBehavior)behavior;
- (NSWindowCollectionBehavior)collectionBehavior;
The setCanBeVisibleOnAllSpaces/canBeVisibleOnAllSpaces API, introduced earlier in Leopard, is deprecated in favor of setCollectionBehavior:/collectionBehavior
-(void)setCanBeVisibleOnAllSpaces:(BOOL)flag AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER_BUT_DEPRECATED;
-(BOOL)canBeVisibleOnAllSpaces AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER_BUT_DEPRECATED;
NSBackingStoreRetained no longer supported
NSBackingStoreRetained is no longer supported as a backing store type. Windows created with a backing store type of NSBackingStoreRetained will be silently promoted to NSBackingStoreBuffered. In the MacOSX implementation, NSBackingStoreRetained doesn't have any advantages in behavior or performance over NSBackingStoreBuffered, and on some hardware may behave significantly worse due to performance of display frame buffer access.
Sheets (New since WWDC 2007 Seed)
Since the September 2007 seed of 10.5 we have introduced a new sheet effect for standard windows. Sheets are now attached under the content border, which includes the titlebar, toolbar, and any additional content border thickness on the top edge. There is no longer any "slot" at the attachment point between the sheet and the window. Instead, there is a shadow on the top edge of the sheet where it connects to the window. If there is no toolbar or content border aside from the titlebar, the sheet is positioned below the titlebar.
Due to compatibility concerns, the additional content border thickness is only taken into account for positioning the sheet if it has been set explicitly. That is, if the window returns YES from [self autorecalculatesContentBorderThicknessForEdge:NSMaxYEdge], the auto-calculated content border thickness is not included when positioning the sheet.
NSScreen (New since WWDC 2007 Seed)
In order to return the correct screen frame information after a programmatic display configuration change, NSScreen no longer caches screen frame information. Instead, it retrieves the requested information from Quartz. This has implications for applications which may have worked around the previous NSScreen behavior where a stale screen frame could be returned after a display configuration change.
NSAlert
We have added support to NSAlert for a suppression checkbox and an accessory view. setShowsSuppressionButton: indicates whether or not the alert should contain a suppression checkbox. The default is NO. This checkbox is typically used to give the user an option to not show this alert again. If shown, the suppression button will have a default localized title similar to @"Do not show this message again." You can customize this title using [[alert suppressionButton] setTitle:]. When the alert is dismissed, you can get the state of the suppression button, using [[alert suppressionButton] state] and store the result in user defaults, for example. This setting can then be checked before showing the alert again. By default, the suppression button is positioned below the informative text, and above the accessory view (if any) and the alert buttons, and left-aligned with the informative text. However do not count on the placement of this button, since it might be moved if the alert panel user interface is changed in the future. If you need a checkbox for purposes other than suppression text, it is recommended you create your own using an accessory view.
- (void)setShowsSuppressionButton:(BOOL)flag;
- (BOOL)showsSuppressionButton;
suppressionButton returns a suppression button which may be customized, including the title and the initial state. You can also use this method to get the state of the button after the alert is dismissed, which may be stored in user defaults and checked before showing the alert again. In order to show the suppression button in the alert panel, you must call -setShowsSuppressionButton:YES.
- (NSButton *)suppressionButton;
setAccessoryView: sets the accessory view displayed in the alert panel. By default, the accessory view is positioned below the informative text and the suppression button (if any) and above the alert buttons, left-aligned with the informative text. If you want to customize the location of the accessory view, you must first call -layout. See the discussion of -layout for more information.
- (void)setAccessoryView:(NSView *)view;
- (NSView *)accessoryView;
The following method can be used to indicate that the alert panel should do immediate layout, overriding the default behavior of laying out lazily just before showing the panel. You should only call this method if you want to do your own custom layout after it returns. You should call this method only after you have finished with NSAlert customization, including setting message and informative text, and adding buttons and an accessory view if needed. You can make layout changes after this method returns, in particular to adjust the frame of an accessory view. Note that the standard layout of the alert may change in the future, so layout customization should be done with caution.
- (void)layout;
[alert setIcon:nil] now restores the application icon, as documented. Prior to this change, [alert setIcon:nil] would actually remove the icon from the alert.
If alertWithError: is called with a nil NSError, it will now return an alert that displays a generic error message rather than an empty and confusing panel. Under normal circumstances a nil argument to such a method should be considered a programming error and should result in an exception. However, since runtime errors that this method is supposed to help with are not always encountered during testing, and since the problem is already occurring in the context of a user level error that the user should know about, displaying the alert remains a reasonable approach. Clearly the problem should be fixed by finding and fixing the subsystem that is failing to properly create an NSError.
NSDockTile
We have added a new class, NSDockTile, which can be used to customize the dock tile behavior for both NSApplication and NSWindow.
An NSDockTile has a size, which corresponds to the size of the backing store in the dock, which may be bigger than the current tile size, and is defined by the current user space scale factor. Currently, it is 128x128 on a system with user space scale factor 1.0, but you should not make any assumptions about this size remaining the same in the future.
You can add a contentView to the dock tile. If you want to draw into the dock tile, you have to tell the dock tile to display, and you have to draw the whole tile. Certain system activity will also cause the dock tile to resize and/or display. Any badging will be applied after the dock tile displays.
You can badge the dock tile with a localized string representing a count. You can also specify whether or not the tile should show the application badge.
Existing API for dock tile management (setting app icon, dock tile menu, miniwindow icon, and miniwindow title) will not be replaced with dock tile API at this point. A future direction could be to move all dock tile management to NSDockTile.
@interface NSDockTile : NSObject
- (NSSize)size;
- (void)setContentView:(NSView *)view;
- (NSView *)contentView;
- (void)display;
- (void)setShowsApplicationBadge:(BOOL)flag;
- (BOOL)showsApplicationBadge;
- (void)setBadgeLabel:(NSString *)string;
- (NSString *)badgeLabel;
- (id)owner;
@end
Please refer to <AppKit/NSDockTile.h> and the documentation for further details on NSDockTile.
NSApplication
Like NSWindow, NSApplication now has a method to get a dock tile instance. It allows you to control some aspects of the dock tile corresponding to the application. For further discussion, see the NSDockTile section right above.
- (NSDockTile *)dockTile;
For applications built on Leopard or later, the dock tile icon is now restored to its default state when the application terminates, meaning badge labels and such are removed automatically. Some applications previously accomplished this by calling RestoreApplicationDockTileImage. This is incompatible with NSDockTile on Leopard, so you should modify your application if you are doing this. If you need to restore the dock tile icon on Tiger in a Leopard compatible way, you can do so by calling -[NSApp setApplicationIconImage:nil].
Tracking areas
We created a new model for mouse tracking and cursor updates. An NSTrackingArea will encapsulate the information used to create trackingRects today, including a rect (relative to view bounds), an owner (which will receive events generated on behalf of the trackingArea), a userInfo dictionary, and options as described below. Note that NSTrackingArea conforms to NSCoding and NSCopying.
An NSTrackingArea can be used like a traditional trackingRect, which will generate mouseEntered and mouseExited events as the mouse moves in and out of the area. It can also be used for cursorUpdates, which will be sent to the view under the mouse when the mouse moves in or out of the area. Lastly, it can be used to register for mouseMoved events for all mouse movement inside of the area. These options can be combined with bit-wise or to install a single NSTrackingArea that provides all three options.
You can get the NSTrackingArea that generated a mouseEntered or mouseExited event using -[event trackingArea].
An NSTrackingArea can be active only when its view is the firstResponder, or when the view is in the key window, or when the view is in any window in the active app, or always. Some of these options do not work with some of the types. For example, you cannot request cursorUpdates in an inactive app.
As with traditional trackingRects, you can specify whether you want to assume that the mouse is inside or outside of the trackingArea. Additionally, a trackingArea can be set up to stay in sync with the visibleRect of the view, which then makes one of the most common situations trivial. Lastly, you can request mouseEntered and mouseExited events to be generated while the mouse button is down (that is, during mouse dragging).
@interface NSTrackingArea : NSObject <NSCopying, NSCoding>
- (NSTrackingArea *)initWithRect:(NSRect)rect
options:(NSTrackingAreaOptions)options
owner:(id)owner
userInfo:(NSDictionary *)userInfo;
- (NSRect)rect;
- (NSTrackingAreaOptions)options;
- (id)owner;
- (NSDictionary *)userInfo;
@end
Please refer to <AppKit/NSTrackingArea.h> and the documentation for further details on NSTrackingArea.
The following API has been added to NSView:
- (void)addTrackingArea:(NSTrackingArea *)trackingArea;
A view or other object creates an NSTrackingArea, then adds it to a view using this API. Not meant to be overridden.
- (void)removeTrackingArea:(NSTrackingArea *)trackingArea;
This API is used to remove a trackingArea from a view. Not meant to be overridden.
- (NSArray *)trackingAreas;
Get the list of trackingAreas that have been added to the view. Not meant to be overridden.
- (void)updateTrackingAreas;
This will be sent to a view when something has changed which is likely to require recomputation of trackingAreas, for example a change in the size of the visibleRect. Moving a view into or out of a window will not cause this message to be sent, except that it will be sent once when the view is first created and added to a window. Should be overridden by a view to remove and add its tracking areas, and should call super.
The third part of the API improves cursor arbitration by adding a responder method, -cursorUpdate:. When an NSCursorUpdate event is received for a window, it gets routed to the view under the mouse, using normal hitTesting. The view under the mouse receives the cursorUpdate: message. This behaves like other event responder methods - if the view doesn't implement cursorUpdate:, the NSResponder implementation will send it to the nextResponder. If the view implements cursorUpdate: but decides not to handle the particular event, it should invoke super.
The following API has been added to NSResponder:
- (void)cursorUpdate:(NSEvent *)event;
Override to set cursor. Default implementation uses cursorRect if cursorRects are valid. If no cursorRect calls super, to send up responder chain.
The following API has been added to NSEvent, and is valid for NSMouseEntered and NSMouseExited events. Note that it is not valid for NSMouseMoved events.
- (NSTrackingArea *)trackingArea;
-trackingArea can be sent to an NSMouseEntered, NSMouseExited, or NSCursorUpdate event. It is not valid for an NSMouseMoved event. If the event was generated by an old-style trackingRect, -trackingArea will return nil.
We fixed a bug in the interpretation of the assumeInside flag for tracking rects added via -[NSView addTrackingRect:owener:userData:assumeInside:]. On Tiger and previous, passing YES for the assumeInside flag yielded inconsistent results. If the mouse was initially outside the tracking rect when the tracking rect was added, no event was generated, and a mouseEntered event was sometimes generated when the mouse entered the tracking rect through a subsequent mouseMoved. On Leopard, passing YES for the assumeInside flag will cause a mouseExited event to be generated if the mouse is initially outside the tracking rect. A mouseEntered will be generated when the mouse enters the tracking rect if it was initially outside. If this new behavior causes a problem for your application, you can get the Tiger behavior by setting the NSTigerBehaviorForTrackingRects user default to YES.
NSEvent (Updated since WWDC 2007 Seed)
We added methods to convert between an NSEvent and a Carbon EventRef. -eventRef is valid for all events and returns an EventRef corresponding to the NSEvent. The EventRef is retained by the NSEvent, so will be valid as long as the NSEvent is valid, and will be released when the NSEvent is freed. You can use RetainEvent to extend the lifetime of the EventRef, with a corresponding ReleaseEvent when you are done with it. If there is no EventRef corresponding to the NSEvent, -eventRef will return NULL. +eventWithEventRef: returns an autoreleased NSEvent corresponding to the EventRef. The EventRef is retained by the NSEvent and will be released when the NSEvent is freed. If there is no NSEvent corresponding to the EventRef, +eventWithEventRef: will return nil.
- (const void * /* EventRef */)eventRef;
+ (NSEvent *)eventWithEventRef:(const void * /* EventRef */)eventRef;
We also added methods to convert between an NSEvent and a CGEventRef. -CGEvent is valid for all events and returns an autoreleased CGEventRef corresponding to the NSEvent. If you want to control the lifetime of the CGEventRef, you should retain it. If there is no CGEventRef corresponding to the NSEvent, -CGEvent will return NULL. + eventWithCGEvent: returns an autoreleased NSEvent corresponding to the CGEventRef. If there is no NSEvent corresponding to the EventRef, +eventWithCGEvent: will return nil. Converting from an NSEvent to a CGEventRef can be lossy, and you should not attempt to use the key event handling facilities provided by CGEventRef.
- (CGEventRef)CGEvent;
+ (NSEvent *)eventWithCGEvent:(CGEventRef)cgEvent;
There is now a method to enable or disable mouse coalescing, and a method to query the current state. Mouse coalescing is on by default.
+ (void)setMouseCoalescingEnabled:(BOOL)flag;
+ (BOOL)isMouseCoalescingEnabled;
If you build your application on Leopard, and your application installs an event handler on the event monitor target using GetEventMonitorTarget, the monitored event will be sent to the event handler you installed rather than to -[NSApplication sendEvent:]. For applications built on Tiger or previous, the monitored event will be sent to sendEvent:. You can override this default behavior by setting NSDispatchMonitoredEvents. If NSDispatchMonitoredEvents is YES, the event will be sent to sendEvent:; if NO, it will be sent to the installed event handler.
NSScrollWheel events will now be sent to the window under the mouse, whether or not the window is active. In previous version of Mac OS X, NSScrollWheel events were only sent to the window under the mouse if the window had key focus, with the exception of utility windows which received NSScrollWheel events even when inactive.
Key equivalents
We fixed a problem where NSKeyUp events were sent to performKeyEquivalent: if the command-key was not down. This problem could cause a key equivalent to be performed twice if the receiver didn't check the event type for NSKeyDown. The fix applies only to applications built on Leopard or later, to preserve binary compatibility for any application which may have been dependent on the old behavior.
The modifier flags are now preserved in NSKeyDown <esc> events. Prior to this change, NSWindow would strip modifier flags from the <esc> key event before invoking -performKeyEquivalent:. We don't expect applications to be dependent on this behavior, but if you need to ignore modifier flags on <esc> keyDown events, you can override -performKeyEquivalent: to do so.
In -[NSWindow sendEvent:] we now send keyDown: to the first responder even if the command-key modifier is set. An NSKeyDown event will only reach this point if it was not recognized as a key equivalent. One effect of this change is to enable custom key binding entries with command-key modifiers. The change applies only to applications built on Leopard or later, to preserve binary compatibility for any application which may have been dependent on the old behavior.
NSApplication now sends a ctrl-key event to performKeyEquivalent: before sending the keyDown: event through the responder chain. This allows ctrl-key events to be used as menu key equivalents more reliably. Prior to this change, a ctrl-key event which had an emacs key binding would not be sent to performKeyEquivalent: if focus was in an NSTextView.
Dragging
-[NSDraggingInfo slideDraggedImageTo:] is now implemented to behave as documented. This change is enabled only for applications built on Leopard or later to avoid changing behavior of older binaries in ways that may be incorrect.
-draggingEnded is now implemented for applications linked on Leopard or later.
NSDatePicker
NSDatePicker - Range mode
NSDatePicker now implements the existing date range selection API for the mini-calendar style date picker. This is enabled by calling setDatePickerMode: and passing in NSRangeDateMode. The range is represented by a start date (NSDate; accessible through dateValue) and a time interval (NSTimeInterval; accessible through timeInterval). If the date picker is a textfield style date picker, setDatePickerMode: is ineffectual and timeInterval returns 0.0 as before. If the date picker is in NSSingleDateMode, then timeInterval always returns 0.0.
Date ranges can be selected in the mini-calendar interface by clicking and dragging across calendar days or by shift-clicking. The calendar's months will also advance or retreat as the mouse is dragged below or above the area where calendar days are displayed.
NSDatePicker - Small and mini sizes
The text field styles of NSDatePicker now support small and mini control sizes through the -setControlSize: method. However, this only affects the size of the internal stepper cell. The size of the text field is determined by the size of its font and number of displayed date components.
NSDatePicker - Text field only style
NSDatePicker now supports a third style: NSTextFieldDatePickerStyle. This is identical to NSTextFieldAndStepperDatePickerStyle in every way, except it does not display a stepper.
NSDatePickerCell - Creation performance
The NSDatePickerCell creation speed has been significantly improved by drastically reducing the number of NSDateFormatters created internally.
NSDatePicker - Date Arithmetic Improvements
NSDatePickerCell's date arithmetic implementation has changed substantially in Leopard, abandoning use of the obsolete NSCalendarDate class (which only supports the Gregorian calendar, and yields imprecise results for dates in the distant past -- e.g. for years circa 1500) in favor of a fully modern NSCalendar-based implementation underpinned by ICU library routines. This fixes significant editing issues for Gregorian calendar dates, while providing substantial localization improvements for non-Gregorian calendars.
Text
New Text Document Formats
The text system now has support for reading and writing both the OASIS Open Document text document format and the ECMA Office Open XML text document format. AppKit/NSAttributedString.h has two new constants for this, NSOfficeOpenXMLTextDocumentType and NSOpenDocumentTextDocumentType. The /usr/bin/textutil command has been updated to support both formats.
NSAttributedString HTML Import
Since Tiger, NSAttributedString has used WebKit for all import of HTML documents (but not for export). Since WebKit document loading is not threadsafe, this has not been safe to use on background threads. For applications linked on Leopard and later, if NSAttributedString is used to import HTML documents on a secondary thread, the usage of WebKit will be transferred to the main thread via performSelectorOnMainThread:withObject:waitUntilDone:. This makes such usage threadsafe, but it requires that the main thread be running the run loop in one of the common modes. This behavior can be overridden by setting the default NSRunWebKitOnAppKitThread to either YES (to obtain the new behavior regardless of linkage) or NO (to obtain the old behavior regardless of linkage).
In versions prior to Leopard, NSAttributedString HTML import would set the NSBackgroundColorDocumentAttribute to [NSColor whiteColor] in cases in which the HTML did not specify a background color. This will continue to be true for applications linked on system versions prior to Leopard, but on Leopard and later, for applications linked on Leopard and later, no NSBackgroundColorDocumentAttribute will be set in these cases.
NSMutableAttributedString Note
The method -[NSMutableAttributedString readFromURL:options:documentAttributes:error:] is expected to replace the text of the receiver with the contents of the desired file. (The documentation states: "Sets the contents of receiver from the file at url.")
It works as expected for plaintext formatted files. However, for RTF formatted files, the contents of the file are appended to the previous string instead of replacing the previous string. This inconsistency is a bug; when using this method with existing content it's best to clear the content away explicitly.
NSTextStorage Scripting
For applications linked on Leopard or later, we now check for nil font in scripting calls and treat it as Helvetica 12. This impacts the calls font, fontName, fontSize, setFontName:, and setFontSize:.
Non-Contiguous Layout
NSLayoutManager now has a new option, referred to as non-contiguous layout. Previously, both glyph generation and layout have always been performed in order from the beginning to the end of the document. When non-contiguous layout is turned on, however, the layout manager gains the option of performing glyph generation or layout for one portion of the document without having done so for previous sections. This can provide significant performance improvements for large documents.
Non-contiguous layout is not turned on automatically because direct clients of NSLayoutManager typically have relied on the previous behavior--for example, by forcing layout for a given glyph range, and then assuming that previous glyphs would therefore be laid out. Clients who use NSLayoutManager only indirectly--for example, those who use NSTextView without directly calling the underlying layout manager--can usually turn on non-contiguous layout without difficulty. Clients using NSLayoutManager directly will need to examine their usage before turning on non-contiguous layout.
The methods directly concerned with non-contiguous layout are as follows:
- (void)setAllowsNonContiguousLayout:(BOOL)flag;
- (BOOL)allowsNonContiguousLayout;
- (BOOL)hasNonContiguousLayout;
The first allows non-contiguous layout to be turned on and off, and the second examines the state of that setting. Note that turning the flag on allows but does not require the layout manager to use non-contiguous layout, and it may in fact choose not to do so depending on the configuration of the layout manager. In addition, there may be times at which there is no non-contiguous layout, such as when layout is complete; the third method allows the layout manager to report that to clients.
In addition, there are a number of new methods that are especially useful when working with non-contiguous layout. Previously, glyph generation and layout have been implicit side effects of calls that require that information. That is still the case, but with the possibility of non-contiguous layout it is not always obvious what portion of the document will be affected. The new methods allow this to be specified explicitly, although the layout manager reserves the right to generate glyphs or perform layout for a larger portion of the document as appropriate. In particular, if non-contiguous layout is not in use, then the range affected will always be extended back to the beginning of the document.
- (void)ensureGlyphsForCharacterRange:(NSRange)charRange;
- (void)ensureGlyphsForGlyphRange:(NSRange)glyphRange;
- (void)ensureLayoutForCharacterRange:(NSRange)charRange;
- (void)ensureLayoutForGlyphRange:(NSRange)glyphRange;
- (void)ensureLayoutForTextContainer:(NSTextContainer *)container;
- (void)ensureLayoutForBoundingRect:(NSRect)bounds inTextContainer:(NSTextContainer *)container;
New NSLayoutManager Implementation
Large portions of NSLayoutManager have been updated to use a new implementation that supports the new non-contiguous layout feature. However, independent of whether non-contiguous layout is in use, the new implementation also rationalizes and clarifies a number of other aspects of the layout manager API.
For example, setCharacterIndex:forGlyphAtIndex: nominally allows for an arbitrary mapping between glyph and character indexes. This has never actually been the case; there have always been implicit restrictions, in the sense that other NSLayoutManager functionality would fail in the presence of a nonsensical glyph-to-character mapping. Now, however, it is possible to state the restrictions and, eventually, enforce them by raising an exception when they are violated. The basic restriction is that the glyph stream may never be out of order with respect to the character stream. Multiple glyphs may map to a single character, or multiple characters to a single glyph, but all of the glyphs for a given character must follow all glyphs for preceding characters and precede all glyphs for following characters. Glyphs that would not otherwise have a character (such as hyphens) are assigned to the character for the nearest previous glyph that has characters, and characters that would not otherwise have glyphs are assigned to the glyph for the nearest previous character that has glyphs. The stock glyph generator and typesetter attempt to preserve a one-to-one character-to-glyph mapping, to the extent possible, by including padding NSNullGlyph entries; for example, if the characters 'f' and 'i' are represented by an 'fi' ligature, then the glyph stream would include an 'fi' ligature glyph followed by a null glyph so that there are two glyphs to match the two characters. However, this is neither guaranteed nor required in general.
The basic methods for examining the character-glyph mapping are characterIndexForGlyphAtIndex: and the new method
- (NSUInteger)glyphIndexForCharacterAtIndex:(NSUInteger)charIndex;
that now plays a symmetric role. Thus characterIndexForGlyphAtIndex: returns the index for the first character associated with the specified glyph, and glyphIndexForCharacterAtIndex: returns the index for the first glyph associated with the specified character. In neither case is there any special treatment for null glyphs. In the 'fi' ligature case, for example, if the null padding is used, then these methods would report an identity mapping between glyph and character indexes. Both methods also accept indexes beyond the last character or glyph; they return an index extrapolated from the last actual character or glyph index. Thus if there is an identity mapping between glyph and character indexes, then both characterIndexForGlyphAtIndex: and glyphIndexForCharacterAtIndex: will always return results numerically equal to their arguments.
The more complex methods glyphRangeForCharacterRange:actualCharacterRange: and characterRangeForGlyphRange:actualGlyphRange:, on the other hand, do take null glyphs into account. For example, to consider the 'fi' ligature case again, if glyphRangeForCharacterRange:actualCharacterRange: were to be called with a character range of length 1 covering either the 'f' or the 'i', the resulting glyph range would include both the 'fi' glyph and the null glyph, and the actual character range would include both the 'f' and the 'i' character. Likewise, if characterRangeForGlyphRange:actualGlyphRange: were to be called with a glyph range of length 1 covering either the 'fi' glyph or the null glyph, the resulting character range would include both the 'f' character and the 'i' character, and the actual glyph range would include both the 'fi' glyph and the null glyph.
Both methods also have special treatment for ranges of zero length. For example, in the 'fi' ligature case, if glyphRangeForCharacterRange:actualCharacterRange: were to be called with a character range of length 0 before the 'f', the result would be a zero-length glyph range before the 'fi' glyph. However, if if glyphRangeForCharacterRange:actualCharacterRange: were to be called with a character range of length 0 between the 'f' and the 'i', the result would be a zero-length glyph range after the null glyph, and the actual character range would be a zero-length range after the 'i' character. In general, a zero-length character range within a multi-character sequence maps to a zero-length glyph range after the last glyph associated with the sequence, with a zero-length actual character range after the last character of the sequence. The same description applies to characterRangeForGlyphRange:actualGlyphRange:, interchanging the role of character and glyph indexes.
In general, character and glyph indexes now play a symmetric role in the character-glyph mapping, and glyphRangeForCharacterRange:actualCharacterRange: and characterRangeForGlyphRange:actualGlyphRange: behave symmetrically to each other. Both glyphRangeForCharacterRange:actualCharacterRange: and characterRangeForGlyphRange:actualGlyphRange: also accept indexes beyond the the last character or glyph, but the results they return are truncated after the last character or glyph rather than being extrapolated. This is in line with their role in, for example, calculating ranges of selected characters or glyphs.
Another aspect of NSLayoutManager that has been clarified is invalidation. Previously there was a distinction between hard and soft layout invalidation; it was expected that any change to the text would cause hard invalidation of the region that actually changed, followed by soft invalidation of all subsequent portions of the document, since they might move due to the change. Usually this would happen automatically as a result of change messages sent by the text storage to the layout manager, but anyone needing to invoke layout invalidation manually would have needed to respect the distinction. In Leopard, however, that distinction is no longer required; hard layout invalidation is the only sort necessary, and the equivalent of soft invalidation is arranged automatically. As a result, a new method
- (void)invalidateLayoutForCharacterRange:(NSRange)charRange actualCharacterRange:(NSRangePointer)actualCharRange;
has been provided to supersede the existing invalidateLayoutForCharacterRange:isSoft:actualCharacterRange:, as the equivalent of the latter with the soft flag set to NO. For code intended to run on Leopard only, the new method can be used. For code intended to run on both Leopard and Tiger, the old method should be used as before, in two calls, first with the soft flag set to NO, for the range actually being changed, and subsequently with the soft flag set to YES, for the range following the portion changed, to the end of the document.
In addition, layout manager/typesetter communication has been clarified by adding a new NSLayoutManager method for the use of the typesetter, to allow it to specify explicitly when portions of the glyph stream depend on layout--for example, because they have had hyphens inserted. The typesetter calls
- (void)invalidateGlyphsOnLayoutInvalidationForGlyphRange:(NSRange)glyphRange;
to specify that a certain range of glyphs is layout-dependent, and therefore the glyphs should be invalidated the next time their layout is invalidated, so that they will be regenerated before being laid out again.
Also, a new bulk NSLayout Manager method has been added to allow the typesetter to set locations for many glyph ranges at once:
- (void)setLocations:(NSPointArray)locations
startingGlyphIndexes:(NSUInteger *)glyphIndexes
count:(NSUInteger)count
forGlyphRange:(NSRange)glyphRange;
All of the glyph indexes should lie within the specified glyph range, the first of them should be equal to glyphRange.location, and the remainder should increase monotonically. Each location will be set as the location for the range beginning at the corresponding glyph index, and continuing until the subsequent glyph index, or until the end of the glyph range for the last location. Thus this method is equivalent to calling setLocation:forStartOfGlyphRange: for a set of ranges covering all of the glyphs in glyphRange.
NSLayoutManager setShowsInvisibleCharacters:
-setShowsInvisibleCharacters: method is now functional and substitute whitespace characters with either LOZENGE U+25CA or FULL STOP U+002E depending on glyph availability of the rendering font.
Thread safety of NSLayoutManager
Generally speaking, a given layout manager (and associated objects) should not be used on more than one thread at a time. Most layout managers will be used on the main thread, since it is the main thread on which their text views are being displayed, and since background layout occurs on the main thread. If it is intended that a layout manager should be used on a background thread, first make sure that text views associated with that layout manager (if any) will not be displayed while the layout manager is being used on the background thread, and second, turn off background layout for that layout manager while it is being used on the background thread.
Miscellaneous New NSLayoutManager Methods
To go along with the existing defaultLineHeightForFont:, NSLayoutManager has publicized
- (CGFloat)defaultBaselineOffsetForFont:(NSFont *)theFont;
to allow clients to obtain the baseline offset appropriate for a particular font within a particular layout manager, given its typesetter behavior and other settings.
In addition, it has also publicized the methods
- (BOOL)usesFontLeading;
- (void)setUsesFontLeading:(BOOL)flag;
that control whether the layout manager will use leading as specified by the font. The default is YES, since in most cases this is appropriate, but there are some cases where it is not; for example, for UI text a fixed leading is often specified by UI layout guidelines. All three of these methods are available going back to Mac OS X 10.2.
Some additions have been made to the NSLayoutManager temporary attribute methods to parallel more of the NSAttributedString attribute methods.
- (id)temporaryAttribute:(NSString *)attrName atCharacterIndex:(NSUInteger)location effectiveRange:(NSRangePointer)range;
- (id)temporaryAttribute:(NSString *)attrName atCharacterIndex:(NSUInteger)location
longestEffectiveRange:(NSRangePointer)range inRange:(NSRange)rangeLimit;
- (NSDictionary *)temporaryAttributesAtCharacterIndex:(NSUInteger)location
longestEffectiveRange:(NSRangePointer)range inRange:(NSRange)rangeLimit;
- (void)addTemporaryAttribute:(NSString *)attrName value:(id)value forCharacterRange:(NSRange)charRange;
There is a new NSLayoutManager method to obtain insertion points in bulk for a given line fragment. Previously the rects used for the insertion point indicator were obtained by calling rectArrayForCharacterRange:withinSelectedCharacterRange:inTextContainer:rectCount: or the glyph-based equivalent with a zero-length range; that is still available, but it has the limitation that only one insertion point can be obtained at a time. There are many cases in which one wishes to retrieve multiple insertion points at once--for example, when one is trying to move from one to another. The method
- (NSUInteger)getLineFragmentInsertionPointsForCharacterAtIndex:(NSUInteger)charIndex
alternatePositions:(BOOL)aFlag
inDisplayOrder:(BOOL)dFlag
positions:(CGFloat *)positions
characterIndexes:(NSUInteger *)charIndexes;
allows clients to obtain all insertion points for a line fragment in one call. The caller specifies the line fragment by supplying one character index within it, and can choose whether to obtain primary or alternate insertion points, and whether they should be in logical or in display order. The return value is the number of insertion points returned. Each pointer passed in should either be NULL, or else point to sufficient memory to hold as many elements as there are insertion points in the line fragment (which cannot be more than the number of characters + 1). The positions buffer passed in will be filled in with the positions of the insertion points, in the order specified, and the charIndexes buffer passed in will be filled in with the corresponding character indexes. Positions indicate a transverse offset relative to the line fragment rect's origin. Internal caching is used to ensure that repeated calls to this method for the same line fragment (possibly with differing values for other arguments) will not be significantly more expensive than a single call.
Finally, there is a new NSLayoutManager delegate method,
- (NSDictionary *)layoutManager:(NSLayoutManager *)layoutManager
shouldUseTemporaryAttributes:(NSDictionary *)attrs
forDrawingToScreen:(BOOL)toScreen
atCharacterIndex:(NSUInteger)charIndex
effectiveRange:(NSRangePointer)effectiveCharRange;
This is sent when the layout manager is drawing and needs to decide whether to use temporary attributes or not. The delegate returns a dictionary of temporary attributes to be used, or nil to suppress the use of temporary attributes altogether. The effectiveCharRange argument is both an in and out by-reference effective range for those attributes. The default behavior if this method is not implemented is to use temporary attributes only when drawing to the screen, so an implementation to match that behavior would return attrs if toScreen is YES and nil otherwise, without changing effectiveCharRange.
NSTextView
A new property, allowedInputSourceLocales, controls the text input sources enabled for a NSTextView instance. The property can be accessed by the following accessor methods. There is a meta locale identifier, NSAllRomanInputSourcesLocaleIdentifier, available for specifying input sources that are limited for Roman script editing.
- (NSArray *)allowedInputSourceLocales;
- (void)setAllowedInputSourceLocales:(NSArray *)localeIdentifiers;
Command-delete is now bound to -deleteToBeginningOfLine:.
NSTextView Find Panel
The standard find panel for NSTextView now keeps track of the most recently used find and replace strings and displays them to the user in an NSComboBox.
In addition to communicating search strings via the find pasteboard, the standard find panel for NSTextView now also communicates search option metadata, including case sensitivity and substring matching options. This metadata is stored in a plist as the NSFindPanelSearchOptionsPboardType value on the global find pasteboard. As such, third party applications may store additional keys in this plist to communicate additional metadata as desired to support the various search options common to many third-party applications' find panels. NSTextView.h contains the AppKit-provided keys and values.
NSTextView Find Indicator
NSTextView now supports the new lozenge-style indication of find results, with a new method
- (void)showFindIndicatorForRange:(NSRange)charRange;
that causes a temporary indicator or indicators to appear around the visible portion(s) of the specified range. The indicators will automatically disappear after a certain period of time, or when the method is called again, or when any of a number of changes occur to the view (such as changes to text, to view size, or to view position). Note that this method does not itself scroll the specified range to be visible; any desired scrolling should be done before this method is called, first because the method acts only on the visible portion of the specified range, and second because scrolling will cause the indicators to disappear. Calling this method with a zero-length range will always remove any existing indicators.
Miscellaneous New NSTextView Methods
NSTextView includes a number of new flags for controlling its behavior.
- (void)setDisplaysLinkToolTips:(BOOL)flag;
- (BOOL)displaysLinkToolTips;
- (void)setAllowsImageEditing:(BOOL)flag;
- (BOOL)allowsImageEditing;
- (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag;
- (BOOL)isAutomaticQuoteSubstitutionEnabled;
- (void)toggleAutomaticQuoteSubstitution:(id)sender;
- (void)setAutomaticLinkDetectionEnabled:(BOOL)flag;
- (BOOL)isAutomaticLinkDetectionEnabled;
- (void)toggleAutomaticLinkDetection:(id)sender;
The flag for displaying link tooltips controls whether the text view will automatically supply the destination of a link as a tooltip for text with a link attribute. The default value for this is YES; clients who do not wish this must explicitly disable it. In a related change, NSTextView will no longer automatically open file: URLs in links; by default, file: URLs will be revealed in the Finder. Clients wishing to override this should implement textView:clickedOnLink:atIndex: (as a delegate) or clickedOnLink:atIndex: (in a subclass).
The image editing flag controls whether text attachments representing images should allow their contents to be edited inline in the text, provided they support this and the text view is editable. Stock text attachments currently do not have support for this feature, but may do so in the future.
Automatic quote substitution, when it is turned on, causes ASCII quotation marks and apostrophes to be automatically replaced on a context- and language-dependent basis with more typographically accurate symbols. Automatic link detection, when it is turned on, causes strings representing URLs typed in the view to be automatically made into links to those URLs. In addition, the existing smart copy/paste functionality has a newly publicized action method,
- (void)toggleSmartInsertDelete:(id)sender;
In support of automatic link detection, there is a new method on NSAttributedString,
- (NSURL *)URLAtIndex:(NSUInteger)location effectiveRange:(NSRangePointer)effectiveRange;
that returns a URL from the contents of text at the given location, if the text at the location appears to be a string representing a URL. The effective range is the range of the URL string, or of non-URL text if no apparent URL is found.
To go along with the new NSLayoutManager insertion point functionality, there is a new NSTextView method
- (NSUInteger)characterIndexForInsertionAtPoint:(NSPoint)point;
which takes a point in view coordinates and returns a character index appropriate for placing a zero-length selection for an insertion point when the mouse is over the given point. NSTextInput protocol methods generally are not suitable for uses other than those associated with text input methods, and the NSTextInput protocol method characterIndexForPoint: is no exception; it is intended only for usages related to text input methods. The new characterIndexForInsertionAtPoint: should be used for insertion points associated with mouse clicks, drag events, and so forth. For other purposes, it is better to use NSLayoutManager methods, as demonstrated in a variety of code examples.
There is a new NSTextView delegate method
- (NSMenu *)textView:(NSTextView *)view menu:(NSMenu *)menu forEvent:(NSEvent *)event atIndex:(NSUInteger)charIndex;
which allows clients to control the context menu via the delegate, instead of having to subclass and override menuForEvent:. The menu parameter is the context menu that NSTextView would otherwise provide, and the charIndex argument is the index of the character that was right-clicked.
Finally, there is a new pasteboard type used by NSTextView when copying and pasting multiple selections.
NSString *NSMultipleTextSelectionPboardType;
This type is used only when the pasteboard is representing a multiple selection. The contents for this type should be an array of NSNumbers, one for each subrange of the selection, indicating the number of paragraphs contained in each subrange. The plain or rich text contents of the pasteboard will be a string representing the contents of each subrange concatenated with paragraph breaks in between them (where they do not already end in paragraph breaks); that combined with the paragraph counts in the NSMultipleTextSelectionPboardType is sufficient to determine which portions of the contents are associated with which subrange. This mechanism has been chosen because it is consistent across plain and rich text, and across different representations of rich text. The counts may be checked for consistency by comparing the total number of paragraphs in the plain or rich text contents of the pasteboard with the total of the numbers in the NSMultipleTextSelectionPboardType array; if the two do not match, then the NSMultipleTextSelectionPboardType contents should be ignored.
Additional List Styles
In Tiger, NSTextList marker formats supported the following numbering specifier keywords: decimal, lower-roman, upper-roman, lower-alpha, upper-alpha, lower-hexadecimal, upper-hexadecimal, and octal. In addition, it also supported the following constant specifier keywords: box, check, circle, diamond, disc, hyphen, square. These keywords are aligned with the corresponding draft CSS3 list-style-type values. In Leopard, NSTextList marker formats also support the following additional numbering specifier keywords: decimal-leading-zero, lower-greek, upper-greek, lower-russian, upper-russian, cjk-ideographic, hiragana, hiragana-iroha, katakana, katakana-iroha, cjk-earthly-branch, and cjk-heavenly-stem.
NSTypesetter
There is a new NSLayoutManager-interface API superseding -layoutGlyphsInLayoutManager:startingAtGlyphIndex:maxNumberOfLineFragments:nextGlyphIndex:. NSLayoutManager sends this message to the typesetter when its -allowsNonContiguousLayout setting is YES.
- (NSRange)layoutCharactersInRange:(NSRange)characterRange
forLayoutManager:(NSLayoutManager *)layoutManager
maximumNumberOfLineFragments:(NSUInteger)maxNumLines;
The last visible line truncation
The Cocoa Text System now allows the last visible line to have an ellipsis character appended if the entire content cannot fit into the specified bounding box. The behavior can be controlled with -truncatesLastVisibleLine for text cells. The -lineBreakMode must be either NSLineBreakByWordWrapping or NSLineBreakByCharWrapping for this option to take effect.. Also, the NSStringDrawingTruncatesLastVisibleLine flag can be specified to NSStringDrawing APIs that take NSStringDrawingOptions. The NSStringDrawingUsesLineFragmentOrigin flag must also be specified for the truncation flag to take effect.
- (BOOL)truncatesLastVisibleLine;
- (void)setTruncatesLastVisibleLine:(BOOL)flag;
NSFont
The AppKit framework no longer retains NSFont instances, and they are subject to the standard retain/release scheme. For debugging purpose, you can use the NSDisableFontDeallocation key. When the value is YES, font instances are not deallocated. Also, by setting NSFontDebugLevel to non-0 value, memory space previously occupied by NSFont is more aggressively reclaimed to allow finding over-released instances easily.
NSFontDescriptor
There is a new font descriptor matching API -matchingFontDescriptorWithMandatoryKeys: that returns the most suitable normalized instance.
We're allowing the following attribute and dictionary keys. They allow applications to create font descriptors that have non-default font feature settings.
NSString *NSFontFeatureSettingsAttribute;
NSString *NSFontFeatureTypeIdentifierKey;
NSString *NSFontFeatureSelectorIdentifierKey;
NSFontManager
NSFontManager now has the following 4 new public methods. You can use -currentFontAction and -convertFontTraits: to determine the action -convertFont: would perform. With the new accessor methods for the target property, you can now redirect the target for the action invoked by -sendAction method.
- (NSFontAction)currentFontAction;
- (NSFontTraitMask)convertFontTraits:(NSFontTraitMask)traits;
- (void)setTarget:(id)aTarget;
- (id)target;
RTF Handling
The RTF writer now prefers Microsoft font encodings over Mac scripts. The generated RTF data has much better compatibility with RTF reader implementations on Windows.
NSInputManager
The automatic loading of bundles located in InputManagers folders is now officially unsupported. The conditions for valid input manager bundle is further tightened. This functionality is likely to be disabled in a future release.
1. The valid installation is now restricted to the /Library/InputManagers folder only. Bundles in other locations are silently ignored.
2. All the files in the bundle and /Library/InputManagers folder itself must be owned by the root user and admin group. No files inside the bundle can have group or other write permissions.
3. Processes running with the root privilege (getuid() == 0 or geteuid() == 0) cannot load any bundle input manager.
4. Processes running with the wheel group privilege cannot load any bundle input manager.
5. The process must be in the active workspace session at the time of loading the bundles.
6. The process must not be tainted by changing user or group id (checked by issetugid()).
7. No 64-bit processes can load any bundle input managers.
NSTextInputClient protocol
There is a new protocol, NSTextInputClient, for objects willing to participate in the Cocoa Text Input system. The protocol supersedes the old protocol, NSTextInput, and allows conforming objects to take advantage of various system supplied functionalities. The Cocoa framework uses the new protocol whenever it is adopted by the responder object. For NSTextView subclasses, the new protocol is preferred unless any of the NSTextInput methods is overridden and none of the new methods is overridden.
There is now a public attributed string property, NSMarkedClauseSegmentAttributeName, to communicate clause segments inside marked text.
Spelling and Grammar Checking
Grammar checking is a new feature associated with the existing spellchecking functionality. Any spellchecking server has the option of also providing grammar checking, by implementing the NSSpellServer delegate method
- (NSRange)spellServer:(NSSpellServer *)sender checkGrammarInString:(NSString *)stringToCheck
language:(NSString *)language details:(NSArray **)details;
The return value is intended to be the range of the next sentence or other grammatical unit that contains sections to be flagged for grammar, since grammar checking generally must be performed sentence by sentence. The details argument optionally returns by reference an array of dictionaries, each one describing a grammatical issue detected in the sentence (since a single sentence may contain more than one problem). In these dictionaries the following keys will be recognized:
NSString *NSGrammarRange;
NSString *NSGrammarUserDescription;
NSString *NSGrammarCorrections;
The value for the NSGrammarRange key should be an NSValue containing an NSRange, a subrange of the sentence range used as the return value, whose location should be an offset from the beginning of the sentence--so, for example, an NSGrammarRange for the first four characters of the overall sentence range should be {0, 4}. The value for the NSGrammarUserDescription key should be an NSString containing descriptive text about that range, to be presented directly to the user; it is intended that the user description should provide enough information to allow the user to correct the problem. A value may also be provided for the NSGrammarCorrections key, consisting of an NSArray of NSStrings representing potential substitutions to correct the problem, but it is expected that this may not be available in all cases. It is recommended that NSGrammarUserDescription be supplied in all cases; in any event, either NSGrammarUserDescription or NSGrammarCorrections must be supplied in order for something to be presented to the user. If NSGrammarRange is not present, it will be assumed to be equal to the overall sentence range. Additional keys may be added in future releases.
The corresponding client method on NSSpellChecker is
- (NSRange)checkGrammarOfString:(NSString *)stringToCheck
startingAt:(NSInteger)startingOffset
language:(NSString *)language
wrap:(BOOL)wrapFlag
inSpellDocumentWithTag:(NSInteger)tag
details:(NSArray **)details;
similar to the existing spellchecking methods. NSSpellChecker also has a new method,
- (NSArray *)availableLanguages;
suitable for use with the existing -language and -setLanguage:. This returns the list of available languages for spellchecking, in the forms specified by the spelling servers; usually these will be language abbreviations such as "fr" or "en_GB", of the sort used by NSBundle for identifying localizations. The -setLanguage: method preferentially accepts one of these, but will attempt to find an inexact match for any value it is given. Also new are the NSSpellChecker methods
- (void)learnWord:(NSString *)word;
- (BOOL)hasLearnedWord:(NSString *)word;
- (void)unlearnWord:(NSString *)word;
that allow clients access to the learning and unlearning features of the spellchecker. The learnWord: method is not actually new, but is newly public; it has always been present on NSSpellChecker. The other methods are new, but there was a previous equivalent of unlearnWord:, named forgetWord:.
NSTextView has methods to control its use of grammar checking,
- (void)setGrammarCheckingEnabled:(BOOL)flag;
- (BOOL)isGrammarCheckingEnabled;
- (void)toggleGrammarChecking:(id)sender;
If grammar checking is enabled, then it will be performed alongside spellchecking, whenever the text view checks spelling, whether continuously or manually.
Means are also now provided to control the display of the spelling and grammar indicators on text, used to highlight portions of the text that are flagged for spelling or grammar issues. These regions are denoted by a temporary attribute on the layout manager, using the key
NSString *NSSpellingStateAttributeName;
This key is available going back to Mac OS X 10.2, but its interpretation has changed. Previously, any non-zero value would cause the spelling indicator to be displayed. For Mac OS X 10.5, the (integer) value will be treated as being composed of the following flags:
enum {
NSSpellingStateSpellingFlag = (1 << 0),
NSSpellingStateGrammarFlag = (1 << 1)
};
There is a new method on NSTextView for setting the spelling state, which may be called by clients or overridden by subclassers.
- (void)setSpellingState:(NSInteger)value range:(NSRange)charRange;
This method in turn calls a new NSTextView delegate method,
- (NSInteger)textView:(NSTextView *)textView shouldSetSpellingState:(NSInteger)value range:(NSRange)affectedCharRange;
which allows the delegate to control any change in the value of the spelling state.
Text and image effects: -[NSCell backgroundStyle]
Prior to Leopard, you may have noticed that text turns white in selected table view rows. The mechanism that controlled this behavior, unfortunately, was arcane. The table would set the highlight color of the cell to the alternateSelectedControlColor and set the cell to be highlighted. The cell would then introspect its highlight color, and use white text if the color was the alternateSelectedControlColor.
Another text effect occurred in applications such as 10.4's Address Book. Text looked engraved when drawn on the metal window surface. This was done with custom drawing.
For Leopard, this kind of behavior is formalized and extended with an API, -[NSCell setBackgroundStyle:]. This provides a cell with metadata that can be used to make good decisions about drawing. There are only four possible background styles: light, dark, raised and lowered. Most backgrounds are light. Selected table rows are, for 10.5, dark, so text will draw white. A metal surface is raised, so text that draws directly on it should look engraved. Some surfaces are lower than the plane of cell drawing, in which case text should look embossed.
There are a few methods involved. -[NSCell backgroundStyle] describes the background drawn on in the method -[NSCell drawWithFrame:inView:]. -[NSCell interiorBackgroundStyle] describes the background drawn on in the method -[NSCell drawInteriorWithFrame:inView:]. What's the difference? -[NSCell drawWithFrame:inView:] is the primary top level cell drawing method - it draws the whole cell. One of the methods it calls is -[NSCell drawInteriorWithFrame:inView:], which draws interior content like text and images. If a button draws a bezel, then -[NSCell backgroundStyle] will describe the surface the button is drawing on, while -[NSCell interiorBackgroundStyle] will describe the surface of the bezel itself.
Consider the bookmarks buttons in Safari 3.0. This is a standard style of button available in Interface Builder. The buttons are placed on a metal-ish surface that is raised relative to the button, so the -backgroundStyle is NSBackgroundStyleRaised. When the button is rolled over or clicked, it draws a bezel, and the bezel appears to be lower than the interior content of the button. So, when the button is not rolled over, -interiorBackgroundStyle returns NSBackgroundStyleRaised and the text looks engraved. When the button is rolled over, -interiorBackgroundStyle returns NSBackgroundStyleLowered and the text looks embossed.
This also makes it clear why a method to directly ask a cell to draw white, or engraved, or embossed would be inappropriate. The only cells that should get white text in a selected table row are those that do not interpose any drawing between the surface of the table and the text. Only a cell itself knows whether that is the case.
The complete list of backgroundStyle-related methods for Leopard is -[NSCell backgroundStyle], -[NSCell setBackgroundStyle:], -[NSCell interiorBackgroundStyle], and -[NSSegmentedCell interiorBackgroundStyleForSegment:].
Text and image effects: -[NSCell backgroundStyle] developer responsibilities
The main idea with background styles is this: "He who knows what art looks like is responsible for describing it to whatever draws on top." Often, it's AppKit that draws the art. All buttons that draw bezels will describe them with the return from -[NSButtonCell interiorBackgroundStyle]. If you pay attention to this value, you will get some independence from changes in framework art when doing custom drawing on a button surface. This might include choosing an image to draw, or even processing an image with CoreImage filters chosen based on backgroundStyle and other state.
Similarly, if you have a custom button bezel, you should override -interiorBackgroundStyle to describe the artwork you draw. This will ensure that AppKit renders appropriate effects on top of the bezel. You shouldn't rely on your art having the same general look as drawing that you've overridden - the framework art could change in a release, leaving you out of sync.
There is no setter for -[NSButtonCell interiorBackgroundStyle] because the return value could only be incorrect if you override cell drawing. If you override cell drawing, you can override -interiorBackgroundStyle as well.
Generally, it's the control's responsibility to call -[NSCell setBackgroundStyle:] on a cell right before drawing it. For example, NSTableView will set a backgroundStyle on its cells before it draws them. This is typically light or dark, but may also be raised or lowered in the case of group rows in source lists, and potentially elsewhere. If you have a custom control subclass, you should call -setBackgroundStyle on your cells before drawing them.
A caveat: Most AppKit controls do no drawing of their own. The cell does all the drawing. In these cases, you'd want a backgroundStyle of the superview to be inherited on the subview in some fashion, but there is no support for this in Leopard. So, in some cases you'll have to help, and sometimes hardwire a background style that it'd be nicer to introspect. For example, if you place an borderless NSTextField directly on the window surface you will likely want to have a controller call setBackgroundStyle:NSBackgroundStyleRaised on the text cell to get engraved text. Button cells and segmented cells that are usually used in a particular context have an initial backgroundStyle that matches that use, but feel free to change it if it isn't correct. For example, buttons with NSRecessedBezelStyle start out with NSBackgroundStyleRaised.
Text and image effects: -[NSImage isTemplate]
So far we've mostly talked about text. Images drawing in cells also have visual effects applied. For example, images are automatically dimmed out in disabled cells and darkened when pressed.
This is extended in Leopard with a new metadata property, -[NSImage isTemplate]. An image is a 'template' if it is a black and clear mask that should not be presented to the user directly, but always processed into a final form. It's similar to a glyph in a font. If a cell is given a template image, it can perform much more sophisticated effects than usual, similar to those done with text. The image can be dark most of the time, white in a selected table view row, and engraved or embossed the same way text is.
The template property does not affect the way an image draws. It's metadata that a sophisticated client can look at. The only sophisticated client in AppKit for 10.5 is NSCell. If you need to produce an NSImage with effects pre-rendered, draw with a cell into an image.
A good example of template image processing occurs in the button that shows the bookmarks collections in Safari 3.0. This is very much a stock AppKit button. There's a single template image set on it, which is an icon of a book. That single image appears engraved or embossed depending on whether button is rolled over (really, on the -interiorBackgroundStyle of the cell).
To mark an image as a template, call -[NSImage setTemplate:]. As a convenience, in applications linked on or after 10.5, any image read off of disk via -[NSImage imageNamed:] whose name ends in @"Template" will be marked as a template as it is created. This makes it easy to use template images in Interface Builder. Just make sure your image filenames end in "Template".
One particular effect is worth calling out, because you'll see it and likely wonder exactly what triggers it: If a template image is drawing on a raised background and is also supposed to look on, it will draw with a blue glow. Think of the image as a translucent gray piece of plastic cut into the surface of the cell, with a blue backlight behind it when the cell is on. This will happen, for example, in selected segments of an NSSegmentedControl with NSSegmentStyleTexturedRounded and NSSegmentSwitchTrackingSelectAny (which means that segments behave like check boxes). You cannot rely on this glow occurring in any particular place because framework art can change, but AppKit promises to continue to do something appropriate.
For an NSButtonCell, a cell's -image will be processed to look 'on' if the cell's state is NSOnState, its showsStateBy mask contains NSContentsCellMask (which means 'use alternate image and title when on'), and it doesn't have an alternateImage. In Interface Builder, make a 'Toggle' button and give the button an image. This also sets up the highlightsBy mask, but it's likely to be what you want.
You may note that it isn't possible to both use a distinct piece of art as an alternateImage on a button or segmented control and also get the blue glow. This may be somewhat limiting, but should tend to make sure this effect is only used when appropriate. This effect helps the user to distinguish between a button that shows an action and a button that shows the state of something. Most buttons in the OS are actions. However, it has always been hard to distinguish those buttons that give state. The blue glow communicates state very effectively. Look at the calendar button in the lower left corner of iCal in 10.5. When the mini calendar pane isn't shown, the button shows an engraved mini-calendar. The user might think of this as either an action 'show mini-calendar' or as a state, 'mini-calendar is off'. Luckily, both interpretations agree on the effect of clicking the button - it should show the calendar. Once the user clicks the button, the correct interpretation becomes very clear: The calendar icon glows blue when the pane is shown. This is obviously showing state, and the user will realize that if he clicks again then the button will turn off and the calendar pane will be hidden.
Text and image effects: NSStatusItem
It's worth mentioning separately that NSStatusItem is template image savvy. If you use template images for your status items, you will get rendering appropriate for the look of the menu bar in Leopard and beyond. Unfortunately, if you use a custom view in a status item, we do not have API to propagate a background style from the status item to the view. You will have to hardwire the background styles.
NSButton metrics
-[NSButton sizeToFit] applied to buttons with bezel style NSRecessedBezelStyle or NSRoundRectBezelStyle now produces buttons which are two pixels narrower than the buttons produced on Tiger. The new buttons have the same dimensions as those in Safari's bookmarks bar.
sizeToFit is substantially different for buttons with NSCircularBezelStyle for applications linked on and after Leopard. The Tiger 'regular' size for this button was very large, really too large to be used anywhere. What was called the 'small' size was something that would generally be called 'regular', and looked correct next to other regular sized controls.
In Leopard, buttons with NSCircularBezelStyle are available in regular, small and mini sizes where the art for the small and mini sizes is new, and the regular size is what used to be called small. For compatibility, the art chosen at draw time is no longer based on the marked controlSize of the button cell, it's based on the size of the frame in which the cell is drawn. The largest art that fits in the provided frame will be used, including the very large art that was available in Tiger. The upshot is that (1) existing buttons should continue to draw they way they did, (2) new buttons will be smaller. It is a bad idea, however, to call sizeToFit at runtime on circular buttons in an app linked on Leopard but that has to run on Tiger as well. The sizeToFit call will produce visually different buttons when running on Tiger vs Leopard. If you need to do this, write your own sizing routine and call it instead of sizeToFit.
Standard images
Leopard includes a number of standard images that developers may find useful. These images are available as names for use with -[NSImage imageNamed:]. For example, NSImageNameBookmarkTemplate is the image used in Safari for the button that shows the bookmark collections view. See NSImage.h for all of the names.
The string values of the constants are also documented so that the images can be used in Interface Builder. The string corresponding to NSImageNameBookmarkTemplate is @"NSBookmarkTemplate". Note that this image is a template and conforms to the template naming convention, as discussed in the release notes section on -[NSImage isTemplate].
Most images are named by a specific function or situation where they are intended to be used. In some cases, the artwork may be more generic than the name. For example, the image for NSImageNameInvalidDataFreestandingTemplate is an arrow in 10.5. Please do not use an image outside of the function for which it is intended - the artwork can change between releases. The invalid data image could change to a yellow exclamation-point-in-triangle icon. If there is no image available for the situation you're interested in, please file a bug and use your own custom art in the meantime.
There is an informal naming convention used with some images. If an image has "Freestanding" in the name, it's usable as a small inline image without any additional bezel. For example, Safari uses NSImageNameStopProgressTemplate (an X for 10.5) as the stop button in a bezeled button in the toolbar, while it uses NSImageNameStopProgressFreestandingTemplate (X in a circle for 10.5) in the downloads window where it appears inline with a progress indicator.
The standard images are mostly provided as a convenience. Use them if they fit your need, but if they don't then custom art is okay. For example, you may find that you want a different stroke weight for a larger or smaller image with an "add" or "remove" button. It'd be great if AppKit could accommodate that, but use your own art if it's better.
Standard Images: -[NSButtonCell setImageScaling:] and -[NSSegmentedCell setImageScaling:forSegment:]
One major caveat with the standard images is that their size is not guaranteed to stay the same between releases. Also, since they're supposed to be used in multiple contexts, you may find that the original size of an image is inappropriate for your use.
To help with this, NSButton and NSButtonCell have gained an imageScaling property, and NSSegmentedControl and NSSegmentedCell have gained -imageScalingForSegment: and -setImageScaling:forSegment: methods.
These behave similarly to the imageScaling property of NSImageCell. A button can now be configured to always draw its image scaled such that it fits in the available space. This will ensure that even if an image gets larger, it will still fit in your button. This is something of a backup fail safe, since many images have narrow lines that will not look good scaled, but it does provide a level of safety that would otherwise be a pain. All new buttons and segmented controls created in interface builder will have NSImageScaleProportionallyDown set by default.
Image scaling has no bearing on -sizeToFit behavior.
We also changed the values available in the NSImageScaling enumeration, because we needed one value that wasn't available, and two of the existing values were not well named.
enum {
NSImageScaleProportionallyDown = 0, // Scale image down if it is too large for destination. Preserve aspect ratio.
NSImageScaleAxesIndependently, // Scale each dimension to exactly fit destination. Do not preserve aspect ratio.
NSImageScaleNone, // Do not scale.
NSImageScaleProportionallyUpOrDown // Scale image to maximum possible dimensions while (1) staying within destination area
// and (2) preserving aspect ratio
};
typedef NSUInteger NSImageScaling;
NSImage layout metadata
NSImage now supports an alignmentRect property, which provides metadata that a client may use to help determine layout. The bottom of the rect gives the baseline of the image. The other edges give similar information in the other directions.
- (NSRect)alignmentRect;
- (void)setAlignmentRect:(NSRect)rect;
The alignmentRect of an image has no effect on methods such as drawInRect:fromRect:operation:Fraction: or drawAtPoint:fromRect:operation:fraction:. Rather, an interested client may use the alignmentRect to improve layout where applicable.
A 20x20 image of a phone icon with a glow might specify an alignmentRect of {{2,2},{16,16}} that excludes the glow. NSButtonCell will take advantage of the alignmentRect to place the image in the same visual location as an 16x16 phone icon without the glow. A 5x5 star that should render high when aligned with text might specify a rect of {{0,-7},{5,12}}.
The default alignmentRect of an image is {{0,0},imageSize}. The rect is adjusted when setSize: is called.
Multipart image drawing
NSCell.h contains two new functions for use with custom cell drawing, though they may be useful elsewhere as well.
NSDrawThreePartImage is used to draw a button of fixed height and variable width, or vice versa. It takes two end caps and a center image to be tiled.
NSDrawNinePartImage is used to draw something like a box that can be drawn at any height or width. It takes four corner images, four images to tile along the edges, and an image to tile in both directions for a center fill.
It's a good idea to use these functions for your custom drawing. AppKit can take care to get resolution independence issues right, and there are some subtle issues.
NSCell Automatic Expansion ToolTip Frame
NSCell has some new API to support expansion tool tips in certain controls:
- (NSRect)expansionFrameWithFrame:(NSRect)cellFrame inView:(NSView *)view;
- (void)drawWithExpansionFrame:(NSRect)cellFrame inView:(NSView *)view;
An expansion tool tip allows one to see truncated text in a special floating window that is similar but different from a normal ToolTip. Currently, NSTableView, NSOutlineView and NSBrowser display expansion tool tips when the text is truncated. If you have a cell that will be displayed in one of these controls, it is recommended that you implement the above methods to properly support the expansion tool tip feature. By default, the methods are properly implemented in NSTextFieldCell and NSBrowserCell. NSCell will always return NSZeroRect, and prevent the expansion from happening. For an example of how to implement it, see the SimpleBrowser demo app.
Clients of NSTableView, NSOutlineView and NSBrowser can prevent the cell expansion from happening on a particular row/column by using the new delegate methods declared in the appropriate header files.
NSCell Marked Text Support
Changing the content of NSCell subclasses no longer forces confirming the marked text (also known as the inline hole) unless the new content is actually different from the original.
There is a new NSCell API telling its field editor to post text change notifications. NSTextFieldCell also provides -setWantsNotificationForMarkedText:
- (BOOL)wantsNotificationForMarkedText;
The bug introduced in Tiger that NSCell's various sizing methods such as -cellSizeForBounds: ignoring bounds sizes smaller than 0.0 is fixed.
NSTextFieldCell
-[NSTextFieldCell accessibilityPerformAction:] now tries to perform NSAccessibilityConfirmAction for cells with no associated action. It uses -currentEditor and -selectedCell to determine if the current field editor is bound to self.
A new property, allowedInputSourceLocales, controls the text input sources enabled for the NSTextView instance. The property can be accessed by the following accessor methods. There is a meta locale identifier, NSAllRomanInputSourcesLocaleIdentifier, available for specifying input sources that are limited for Roman script editing.
- (NSArray *)allowedInputSourceLocales;
- (void)setAllowedInputSourceLocales:(NSArray *)localeIdentifiers;
NSTextFieldCell no longer disables itself temporarily in -trackMouse:inRect:ofView:untilMouseUp:. It is the application's responsibility now to properly set up the action invocation configuration via -sendActionOn: method.
In order to prevent inconsistent rendering, background color rendering is disabled for rounded-bezel NSTextFieldCell instances.
Autorelease pool in -[NSCell trackMouse:inRect:ofView:untilMouseUp:]
For applications linked on or after Leopard, NSCell uses an internal autorelease pool while looping over events in -trackMouse:inRect:ofView:untilMouseUp:. This should have no effect on an app that already practices correct memory management, except that memory usage will not temporarily climb while a user, say, scrubs an NSSlider back and forth. However, an application that isn't always careful about memory management may have newly expressed bugs. If pressing a button causes an NSTableView delegate to receive a final autorelease, and the programmer is not careful to call [tableView setDelegate:nil] to clear the non-retained back reference, the app could crash if the tableview tries to message its datasource before the end of the runloop cycle.
Archive reading for very old cells
Prior to Leopard, NSCell had residual support for reading very old un-keyed archive data saved prior to Mac OS X 10.0. This support has been removed. An attempt to read such an NSCell will now throw an exception. By the way, in case you missed it, you should really be using keyed archiving. The unkeyed archiver does not save out any object properties added in the last several OS releases. If we did, those archives would not be readable on earlier versions of the OS.
NSBitmapImageRep - Creating with a Custom Color Profile
It is now possible to create an NSBitmapImageRep with an arbitrary ICC color profile. The specified profile will be used when the image is drawn, and will also be saved with the image when it is encoded to a bitmapped image data format such as TIFF.
To do this for an NSBitmapImageRep that is created from scratch using one of the "-initWithBitmapDataPlanes:..." initializer methods, client code should pass NSCalibratedRGBColorSpace to the initializer, and then provide the ICC profile data as an NSData object using the -setProperty:withValue: method and the NSImageColorSyncProfileData property key:
NSBitmapImageRep *bitmapImageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:planes
/* ...more parameters... */
colorSpaceName:NSCalibratedRGBColorSpace
/* ...more parameters... */
];
[bitmapImageRep setProperty:NSImageColorSyncProfileData withValue:iccProfileData];
One can also set a new replacement color profile for an NSBitmapImageRep that is initialized from an existing TIFF, PNG, etc. file using the same "-setProperty:withValue:" technique.
An ICC profile can be obtained as NSData from an NSColorSpace instance using the "-ICCProfileData" accessor method:
NSColorSpace *colorSpace = [NSColorSpace sRGBColorSpace];
NSData *iccProfileData = [colorSpace ICCProfileData];
It can also be obtained by initializing an NSData instance with the contents of an ".icc" profile file:
NSData *iccProfileData = [NSData dataWithContentsOfFile:@"/System/Library/ColorSync/Profiles/sRGB Profile.icc"];
NSBitmapImageRep - Creating from a CIImage
AppKit now includes an initializer method for creating an NSBitmapImageRep from a Core Image CIImage object:
- (id)initWithCIImage:(CIImage *)ciImage;
The CIImage is required to be of finite extent (if not, -initWithCIImage: raises an exception). The -initWithCIImage: method produces an NSBitmapImageRep whose pixel dimensions equal the incoming CIImage's extent.
Note that if the given CIImage is the result of a Core Image filter operation whose evaluation has been deferred, invoking -initWithCIImage: will cause the image recipe to be evaluated (rendered) so that the resultant image pixels can be obtained, so this method may be expected to take some time to execute and return, depending on the complexity of the filter chain and the availability of Core Image-accelerating graphics hardware on the host system.
NSBitmapImageRep - Creating from a CGImage, Getting a CGImage
AppKit also includes a new initializer method for creating an NSBitmapImageRep from a Quartz CGImage:
- (id)initWithCGImage:(CGImageRef)cgImage;
An NSBitmapImageRep that is created in this way retains the given CGImage as its primary underlying representation, reflecting the CGImage's properties as its own and using the CGImage to draw when asked to draw.
Since the CGImageRef is simply retained by the NSBitmapImageRep, its resident image data is referenced instead of being copied. If the NSBitmapImageRep is asked for its pixel data (via a -bitmapData or -getBitmapDataPlanes: message), it will oblige by unpacking a copy of the CGImage's content to an internal buffer. The resultant pixel data should be considered read-only. Changing it through the returned pointer(s) will not cause the CGImage to be re-created.
Regardless of how it was created, an NSBitmapImageRep can now be asked to return an autoreleased CGImage representation of itself:
- (CGImageRef)CGImage;
Using this method may cause the CGImage to be created, if the NSBitmapImageRep does not already have a CGImage representation of itself. Once created, the CGImage is owned by the NSBitmapImageRep, which is responsible for releasing it when the NSBitmapImageRep itself is deallocated.
NSBitmapImageRep - Fallback Background Color for JPEG output
NSBitmapImageRep.h declares an additional NSBitmapImageRep attribute, available on Leopard, that can be used to specify a fallback background color to use when encoding an image to a data/file format that doesn't support alpha channels:
NSString* NSImageFallbackBackgroundColor AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; // JPEG output (NSColor)
This allows clients to specify the background color to use for the image data that's written out, in the event that the NSBitmapImageRep being encoded has an alpha channel. It corresponds to the kCGImageDestinationBackgroundColor property defined by the ImageIO framework.
New NSBox properties
NSBox now supports generic colored box drawing via four new properties:
- (CGFloat)borderWidth;
- (void)setBorderWidth:(CGFloat)borderWidth;
- (CGFloat)cornerRadius;
- (void)setCornerRadius:(CGFloat)cornerRadius;
- (NSColor *)borderColor;
- (void)setBorderColor:(NSColor *)borderColor;
- (NSColor *)fillColor;
- (void)setFillColor:(NSColor *)fillColor;
These properties only apply to boxes of type NSBoxCustom. The word 'custom' refers to these boxes having nothing to do with the human interface guidelines for Mac OS X. As such, the style should be used sparingly. It may be useful for simple custom drawing, or as a placard of some sort, but should not be used as a general control grouping box.
This box style may be useful in conjunction with the new Leopard class NSCollectionView. NSCollectionView supports single and multiple selection, but doesn't draw anything to indicate selection itself. That responsibility is delegated to the views it contains. A custom box can be used to draw a simple selection highlight.
One problem with a using NSBox to draw the selection highlight in NSCollectionView is that one needs to be able to toggle drawing on and off corresponding to the selected property of NSCollectionViewItem. To support this case, we've added one more property to NSBox:
- (BOOL)isTransparent;
- (void)setTransparent:(BOOL)flag;
Bind the transparent property to the selected property of NSCollectionViewItem. The semantics of NSBox's transparent property of are the same as the semantics of NSButton's transparent property.
NSGradient
Instances of the new NSGradient class support the creation of a color gradient with multiple color stops, and drawing itself as a linear or or radial gradient. A gradient defines a transition between colors at locations from 0.0 to 1.0. The starting color is considered to be at location 0.0, the ending color at 1.0. Additional color stops can be located at positions between 0.0 and 1.0. The color gradient is able to draw itself in a variety of ways, as a linear gradient and as a radial gradient.
The designated initializer takes an NSArray of colors for the gradient and a C array containing CGFloats with values between 0.0 and 1.0 to indicate the locations of the colors within the gradient. In addition, the color space of the gradient is provided. All provided colors will converted to this color space. If no color stop is located at 0.0 or 1.0, the color of the nearest color stop will be drawn to the appropriate end of the gradient.
- (id)initWithColors:(NSArray *)colorArray atLocations:(CGFloat *)locations colorSpace:(NSColorSpace *)colorSpace;
Three additional initializers handle common cases. The first takes a start and end color to create a two-color gradient. The second takes an array of colors, and will space the provided colors at equal intervals from 0.0 to 1.0. The third takes a nil-terminated variable length list of color/location pairs. The generic RGB color space will be used for gradients created with these init methods.
- (id)initWithStartingColor:(NSColor *)color endingColor:(NSColor *)endingColor;
- (id)initWithColors:(NSArray *)colorArray;
- (id)initWithColorsAndLocations:(NSColor *)firstColor, ...;
Once a color gradient is defined, it can be used to draw both linear and radial gradients.
Drawing methods allow for the drawing of linear and radial gradients within a rectangle and path. Given a rectangle or path, the convenience method will calculate appropriate locations and do the appropriate clipping.
For linear gradients, the angle of the gradient within the rect or path can be specified.
- (void)drawInRect:(NSRect)rect angle:(CGFloat)angle;
- (void)drawInBezierPath:(NSBezierPath *)path angle:(CGFloat)angle;
For radial gradients, a relative center position can be specified, using NSZeroPoint specifies a radial gradient centered within the rectangle or path bounding rect. The radial gradient drawn by these methods always draws from an inner point to an outer circle, with the inner point at the center of the outer circle.
The relative center position allows a developer to proportionally adjust the center of the radial gradient with respect to the rectangle or path bounding rect. The relative center position maps the point -(1.0, -1.0) to the origin of the rectangle and the point (1.0, 1.0) to the maximum x and y values of the rectangle. The center of the bounding rect maps to a relative center position of (0, 0).
- (void)drawInRect:(NSRect)rect relativeCenterPosition:(NSPoint)relativeCenterPosition;
- (void)drawInBezierPath:(NSBezierPath *)path relativeCenterPosition:(NSPoint)relativeCenterPosition;
These drawing methods provide an easy way to draw gradient fills. Developers who wish other drawing behavior can use the primitive drawing methods described below, and create additional convenience methods as categories which calculate where the primitive methods should draw.
Two primitive drawing methods map closely to the drawing of Quartz shadings. Note that, just as with Quartz shadings, these primitive drawing methods perform no clipping before drawing.
Each primitive drawing method takes an options: argument. Two options are defined:
NSGradientDrawsBeforeStartingLocation
NSGradientDrawsAfterEndingLocation
These options control whether drawing extends before and after the gradient start and end locations. These options are only present for the primitive drawing methods, since the rect and path-centric drawing methods ensure that the entire rect or path is filled by the gradient. These drawing primitive methods draw a linear gradient and a radial gradient, respectively.
- (void)drawFromPoint:(NSPoint)startingPoint toPoint:(NSPoint)endingPoint options:(NSGradientDrawingOptions)options;
- (void)drawFromCenter:(NSPoint)startCenter radius:(CGFloat)startRadius
toCenter:(NSPoint)endCenter radius:(CGFloat)endRadius options:(NSGradientDrawingOptions)options;
A number of methods are provided primarily to allow for the creation of controls that edit/create gradients. These methods return the number of color stops, access the color and location of each stop, return the color space of the gradient, and determine the interpolated color at a particular location between 0.0 and 1.0 in the color gradient.
- (NSInteger)numberOfColorStops;
- (void)getColor:(NSColor **)color location:(CGFloat *)location atIndex:(NSInteger)index;
- (NSColorSpace *)colorSpace;
Overriding this method to provide different color values will not affect the underlying calculation of the color gradient, and will not affect how the color gradient is drawn.
- (NSColor *)interpolatedColorAtLocation:(CGFloat)location;
NSTableView/NSOutlineView
NSTableView and NSOutlineView now properly handle alpha backgroundColors. In addition, you can set the color to [NSColor clearColor] to see through to the area behind you (ie: make it transparent), but note that you must also set drawsBackground to NO on the containing NSScrollView, such as: [[tableView enclosingScrollView] setDrawsBackground:NO].
The documentation correctly states that one can programatically select multiple rows even if allowsMultipleSelection was set to NO, but this was actually not allowed. On Leopard linked applications, this is fixed, and one can call [table selectRowIndexes:indexes byExtendingSelection:YES] or [table selectRow:row byExtendingSelection:YES] and correctly select multiple rows, even if allowsMultpleSelection is set to NO. The documentation correctly states that one can progmatically deselect all rows regardless of whether an empty selection is allowed, but this was actually not allowed. This is also fixed for Leopard linked applications.
Tables now support inter-cell navigation as follows:
- Tabbing forward to a table focuses the entire table.
- Hitting Space will attempt to 'performClick:' on a NSButtonCell in the selected row, if there is only one instance in that row.
- Tabbing again focuses the first "focusable" (1) cell, if there is one.
- If the newly focused cell can be edited, editing will begin.
- Hitting Space calls 'performClick:' on the cell and sets the datasource value afterwards, if changed. (2)
- If a text cell is editing, hitting Enter will commit editing and focus will be returned to the tableview, and Tab/Shift-tab will commit the editing and then perform the new tab-loop behavior.
- Tabbing will only tab through a single row
- Once the last cell in a row is reached, tab will take the focus to the next focusable control.
- Back tabbing into a table will select the last focusable cell.
(1) A focusable cell is generally defined as [cell isEnabled] && [cell isSelectable] in a table column that is editable. However, NSTextFieldCells also check if the row is selectable, and tableView:shouldEditTableColumn:row: returns YES (if implemented by the delegate). NSImageCells cannot be focused. NSButtonCells only check if the [cell isEnabled].
(2) To make this work with NSPopUpButtonCell, performClickWithFrame:inView: is called.
NSTableView/NSOutlineView - Delegate Changes
The following two new Leopard delegate methods introduced at WWDC have been renamed from:
- (NSIndexSet *)tableView:(NSTableView *)tableView
selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes
byExtendingSelection:(BOOL)extend;
- (NSIndexSet *)outlineView:(NSOutlineView *)outlineView
selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes
byExtendingSelection:(BOOL)extend;
to:
- (NSIndexSet *)tableView:(NSTableView *)tableView
selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes;
- (NSIndexSet *)outlineView:(NSOutlineView *)outlineView
selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes;
In releases prior to Mac OS 10.5, for the following delegate method:
- (void)tableView:(NSTableView*)tableView didDragTableColumn:(NSTableColumn *)column;
the tableColumn parameter would incorrectly be the NSTableColumn that existed at the dragged column's original index. For applications linked on or after Leopard, the tableColumn will correctly be the column that was dragged.
NSOutlineView requires all of its items to be pointer unique, however, between a reload items that have the same hash value and are considered [NSObject isEqual], will preserve the expanded state for those items, even if they are not pointer equal.
Prior to Leopard, NSTableView and NSOutlineView would not clear out the drag pasteboard before calling:
- (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard;
or
- (BOOL)tableView:(NSTableView *)tableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard;
In Leopard, it will now clear out the pasteboard. You should declare the types you support for drag and drop with:
NSMutableArray *types = [[pboard types] mutableCopy];
// Add our custom type and leave the ones NSOutlineView supports in the list.
[types addObject:MyType];
// Make ourselves the owner. For any types we don't handle we can forward to NSOutlineView/NSTableView.
[pboard declareTypes:types owner:self];
The new delegate method:
- (BOOL)tableView:(NSTableView *)tableView
shouldTrackCell:(NSCell *)cell
forTableColumn:(NSTableColumn *)column
row:(NSInteger)row;
allows a cell to be tracked, even if the row isn't selectable or selected. This allows you make a table view that will not allow any rows to be selected, but still allow the user to interact with the cells, as seen below (using outline view as an example):
- (BOOL)outlineView:(NSOutlineView *)ov shouldSelectItem:(id)item {
return NO;
}
- (BOOL)outlineView:(NSOutlineView *)ov shouldTrackCell:(NSCell *)cell forTableColumn:(NSTableColumn *)column item:(id)item {
return YES;
}
Another example is to not allow check box button cells to change the selection, but still allow them to be clicked on and tracked. [NSApp currentEvent] will always be correct when this method is called, and you may use it to perform additional hit testing of the current mouse location. See the DragNDropOutlineView demo application for an example of how to do this.
In outlineView:shouldSelectItem: and tableView:shouldSelectRow: you can now access clickedRow and clickedColumn to see what row and column was clicked on, if the method is being sent in response to an initial click event. At that point, [NSApp currentEvent] will also be valid.
For Leopard (and higher) linked applications, the following drag and drop delegate method will get called only when the dragOperation or drop location changes:
- (NSDragOperation)outlineView:(NSOutlineView *)outlineView
validateDrop:(id <NSDraggingInfo>)info
proposedItem:(id)item
proposedChildIndex:(NSInteger)index;
Prior to Leopard, it would get called constantly, which is not needed.
NSTableView/NSOutlineView - Type to Select
NSTableView and NSOutlineView now support type to select (also known as incremental searching). See the header for the new delegate methods.
NSTableView/NSOutlineView - Disabled state
For applications linked on Leopard, NSTableView and NSOutlineView now properly supports [NSControl isEnabled] and [NSControl setEnabled:(BOOL)flag].
A disabled NSTableView will:
- Refuse first responder status
- Not do any tracking in mouseDown
- Not allow editing of any columns
- Disable selection of rows and columns
- Disable draggging of rows and columns
- Not allow reordering of rows or columns
- Call [NSCell setEnabled:NO] for each cell that is being drawn
- Additionally, NSTextFieldCell's will be drawn with a disabled text color
- Draw the header view with a disabled text color
Setting an NSTableView/NSOutlineView to be enabled or disabled will walk all of the NSTableColumns enable or disable the dataCell and headerCell of that NSTableColumn. You can override the enabled/disabled state of cells in the willDisplayCell delegate method.
NSTableView/NSOutlineView - Cell Hit Testing, Drag and Drop, and Cell Editing
NSTableView now uses the new NSCell hit testing API to perform certain actions. Custom NSCell subclasses in applications that link on or after Leopard should properly implement -hitTestForEvent:inRect:ofView:; see NSCell.h for more information.
NSTableView performs hit testing in the cells to do the following actions:
- Drag and Drop: NSTableView calls hitTestForEvent:inRect:ofView in canDragRowsWithIndexes:atPoint. If the hit cell returns NSCellHitTrackableArea, the particular row will be tracked instead of dragged.
- Cell Editing: When NSTableView recieves a mouse down, single-click editing of text (like Finder) will happen if there is only one row selected, and the cell returns NSCellHitEditableTextArea.
See the DragNDropOutlineView demo application for an example of how to properly implement the NSCell methods.
NSTableView/NSOutlineView - Single click to edit
NSTableView and NSOutlineView now behave like Finder and allow a single click to put it into edit mode. This is done by calling -hitTestForEvent:inRect:ofView: and checking if the cell returns NSCellHitEditableTextArea. This allows you to set a doubleAction and perform some different task when the doubleAction is invoked (for instance, Finder opens files on a double click, and edits via a single click). If the doubleAction is not set, editing is still allowed via a double click.
NSTableView/NSOutlineView - Contextual menu support
NSTableView and NSOutlineView now have better contextual menu support. Please see the DragNDropOutlineView demo application for an example of how to properly do contextual menus with a TableView.
The key thing to note is that clickedRow and clickedColumn will now both be valid when a contextual menu is popped up. In addition, one can dynamically set the popup menu for a particular cell/column in the delegate method willDisplayCell:. NSTableView handles this by properly overriding menuForEvent:, setting the clickedRow/clickedColumn, and calling [NSCell menuForEvent:inRect:ofView] to find the correct menu. If no menu was returned, the menu for the NSTableView will be used. If one right clicks on a selection of items, all the items are highlighted. One should check to see if clickedRow is one of the selected rows, and if it is then do the menu operation on all the selected rows. The clickedRow and clickedColumn properties will still be valid when the action is sent from the NSMenuItem.
NSTableView/NSOutlineView - rowHeight
For applications linked on or after Leopard, the default row height for NSTableView's created with [[NSTableView alloc] init] will be set correctly for the initial font used by the default cell inside an NSTableColumn. NSTableView's created in Interface Builder on Tiger and greater already have the rowHeight correctly set for the particular font selected.
NSTableView/NSOutlineView - Full Width cells and group row look
NSTableView and NSOutlineView now have new delegate API to create the "group row" look. See NSTableView.h/NSOutlineView.h and the DragNDropOutlineView demonstration app for an example of how to use the new delegate methods.
tableView:dataCellForTableColumn:row: allows you to easily return a custom cell for any particular row, without having to subclass NSTableColumn. In addition, you can return a cell that will span the entire width of a table view (a "full width" cell).
If you return YES from tableView:isGroupRow: then the "group row" style will be drawn for that row. The "group row" style is dependent on the selectionHighlightStyle, and will draw differently for different styles.
NSTableView/NSOutlineView - Column rects
Since table columns now can be hidden (see below), the columnsInRect: method is being deprecated in favor of one that can return a set of column indexes rather than just a range:
- (NSIndexSet *)columnIndexesInRect:(NSRect)rect;
NSTableView /NSOutlineView - selectionHighlightStyle:
There is a new property called selectionHighlightStyle with the main purpose of allowing the "source list" highlighting style to be automatically adopted with little or no work.
If the style is set to NSTableViewSelectionHighlightStyleSourceList, then it will draw selected items with a "source list" highlighting style. Additionally, setting NSTableViewSelectionHighlightStyleSourceList on an NSOutlineView will also change the following properties to match the HI guidelines: The backgroundColor, indentationPerLevel, rowHeight and intercellSpacing. If you would like to change any of these properties, you must do so after calling [outlineView setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList]. Additionally, combining "group rows" with the "source list" highlighting style will produce the standard section header look by implementing -outlineView:isGroupItem: and returning YES for the section headers. To not show a disclosure triangle, override -frameOfOutlineCellAtRow: and return an empty rect. You will also want to not allow it to be collapsed by returning NO from the delegate method -outlineView:shouldCollapseItem:. When the disclosure triangle is not shown for a section header, the item will automatically be unindented. When using the "source list" highlighting style, it is recommended that you draw your cells with 16x16px icons.
One additional note: selected items in "source lists" should be bold, and NSTableView will automatically convert an NSString value inside an NSTextFieldCell to be bold; however, this may conflict with formatters with the cell that expect an NSString instead of an NSAttributedString. If editing a cell makes the title draw blank, be sure to examine any formatters attached to the cell.
NSTableColumn
NSTableColumns now have:
- (void)setHidden:(BOOL)hidden;
- (BOOL)isHidden;
Columns which are hidden still exist in the the -[NSTableView tableColumns] array and -[NSTableView numberOfColumns] includes columns which are hidden. Hidden columns are not included in the indexes returned by the new columnIndexesInRect: method.
The state is saved out with the other autosave information if autosaveTableColumns is set to YES.
You can also now set the tooltip for the column header:
- (void)setHeaderToolTip:(NSString *)string;
- (NSString *)headerToolTip;
NSSavePanel/NSOpenPanel
NSSavePanel and NSOpenPanel now support drag and drop reordering and insertion of items in the sidebar. In addition, you can drag and drop any of the files into the sidebar or Finder.
NSSavePanel and NSOpenPanel now support an "icon view" mode. The icon selection allows the user to change icon size and label location.
For a selection of files, cmd-i will show the Finder info panel and cmd-r will reveal the location in Finder.
NSSavePanel and NSOpenPanel can once again show hidden files by setting a user default. For instance you can use 'defaults write -g AppleShowAllFiles 1' to display hidden files in the panels.
In NSSavePanel, Ctrl-Tab will now quickly tab you to the file list from the name editing text. Ctrl-Shift-Tab will take you back to where you were.
NSSavePanel and NSOpenPanel now have advanced search options. Click the plus button located to the right of the search location buttons to customize the search. Searches can now be saved to the sidebar for use in all applications, or just your application. You can also edit existing searches and resave them.
NSOpenPanel now allows you to browse your iLife and related media. The appropriate media items in the sidebar are displayed based on the types passed to the open panel.
NSPathControl, NSPathCell, NSPathComponentCell
AppKit now has a new control that allows you to easily represent a path, be it a file system path, or a virtual path.
NSPathControl supports three styles:
- NSPathStyleStandard, the same style seen in the NSOpenPanel/NSSavePanel/Finder when a search is done and a file is selected.
- NSPathStyleNavigationBar (aka "breadcrumb"), which is similar to what iTunes has in the iTunes Music Store.
- NSPathStylePopUp, a style that only shows the last path component and uses a popup to display the full path and allow the user to choose a new path.
The primary interaction with the control is done via [NSPathControl setURL:url], which is a wrapper around [NSPathCell setURL:url]. The control automatically supports drag and drop, which can be further customized via delegate methods. The cell's properties can easily be modfied via accessor methods. The NSPathCell also works well in an NSTableView/NSOutlineView, however, the automatic animation will not work when the style is set to NSPathStyleStandard or NSPathStyleNavigationBar. Please refer to the corresponding header files for more information.
NSPathCell (Updated since WWDC 2007)
When NSPathCell has the style set to NSPathStylePopUp, if it contains 0 pathComponentCells, it will use the placeholderString for the pop up button. Previously, it would not use a pop up button, and would simply draw the string.
When NSPathCell has the style set to NSPathStylePopUp, if the cell has a border (ie: [cell setBordered:YES]), it will draw the "round bezel" border around the pop up button. Previously, it would not ever draw a border.
NSDictionaryController
There is a new controller class: NSDictionaryController. It can be used to edit dictionary contents as key/value pairs in table views, for example. The controller allows to configure "included keys" (keys always visible in the displayed array of key-value pairs, whether present in the content dictionary or not) and "excluded keys" (keys always hidden in the displayed array of key-value pairs). To support localization, dictionary controllers allow to set a localized key dictionary, which can specify a table of display labels for each key (and will typically be read from a string table).
NSDictionaryController offers bindings for the content dictionary as well as the included keys, excluded keys, and localized key dictionary (which means that the object that provides the content dictionary can also provide the included, excluded, and localized keys at the same time, potentially dynamically computed based on the content dictionary).
NSTreeController
NSTreeController now provides better access to the displayed content objects in the form of NSTreeNode objects. NSTreeNode is designed to represent an individual node in a tree data structure. Each NSTreeNode instance wraps a single representedObject and maintains a reference to its parent and child nodes. NSTreeNodes also know where they are relative to the tree's root node. This location information represented by an NSIndexPath and is returned by NSTreeNode's indexPath method.
In Tiger, NSTreeController's arrangedObjects method returned an opaque proxy that developers weren't supposed to use directly. In Leopard, the arrangedObjects method returns a proxy that responds to the NSTreeNode methods
- (NSArray *)childNodes;
and
- (NSTreeNode *)descendantNodeAtIndexPath:(NSIndexPath *)indexPath;
With these two methods, it is now possible to navigate the tree controller's arranged tree.
By using NSTreeNodes to wrap represented objects, an NSOutlineView bound to an NSTreeController can display the same represented object in multiple rows.
When bound to an NSTreeController, NSOutlineView's row items will be NSTreeNodes. This applies to your outline view delegate or datasource - the items passed to your delegate/datasource methods are NSTreeNode instances. The same is true for NSBrowsers bound to an NSTreeController and the browser delegate methods. You can use the node's location (indexPath) in the arranged tree being displayed, or the underlying model object for the node (representedObject) in order to make decisions in delegate code.
Leveraging NSTreeNode, NSTreeController also exposes two new methods for moving nodes from one place to another.
- (void)moveNode:(NSTreeNode *)node toIndexPath:(NSIndexPath *)indexPath;
- (void)moveNodes:(NSArray *)nodes toIndexPath:(NSIndexPath *)startingIndexPath;
When a node is moved from one location to another, its represented objects in the new and old parent will be updated using Key Value Coding and the tree controller's childrenKeyPath. You are encouraged to use these methods to modify relationships in the tree, rather than modify the represented objects directly. This will ensure better selection handling and improve outline view's ability to maintain item "expanded" state.
NSTreeNodes can be used outside of an NSTreeController to maintain an ad hoc tree of objects. When created outside of a tree controller, the tree nodes do not have a concept of a childrenKeyPath and so don't automatically update relationships of their model objects.
NSTreeController selectedObjects accessor not accurate when not deleting objects through NSTreeController remove: (New since WWDC 2007 Seed)
There is a known bug in NSTreeController's selectedObjects accessor that means that under certain circumstances, the contents of selectedObjects does not accurately reflect the controller selection. This can happen when an object is deleted by mutating the relationship of one of the treeController's model objects instead of using the deleteObjectAtArrangedIndexPath: method of the remove: IBAction method. If this bug affects you, a potential workaround is to replace use of code like
[treeController selectedObjects]
with code like
[[treeController selectedNodes] valueForKey:@"representedObject"]
This will produce an array that accurately represents the treeController's selected objects, even if objects are deleted via manipulating model objects' relationship containers.
NSArrayController
NSArrayController now has API to automatically re-sort/filter content arrays when the objects in it change.
- (void)setAutomaticallyRearrangesObjects:(BOOL)flag; // default: NO
- (BOOL)automaticallyRearrangesObjects;
- (NSArray *)automaticRearragementKeyPaths;
This method returns the array of key paths that trigger automatic rearranging from the sort descriptors and filter predicates; subclasses may override this method to customize the default behavior (for example if additional arrangement criteria are used in custom implementations of -rearrangeObjects).
- (void)didChangeArrangementCriteria;
This method is invoked by the controller itself when any criteria for arranging objects change (sort descriptors or filter predicates) to reset the key paths for automatic rearranging; subclasses should invoke this method if additional arrangement criteria are used in custom implementations of -rearrangeObjects and those criteria change.
NSObjectController setUsesLazyFetching:
NSObjectController and its subclasses, when in entity mode, can now fetch "lazily." With the behaviour enabled, the controller will try to fetch only a small amount of data from available persistent stores. This can provide a huge improvement in memory use when dealing with a lot of data on disk but just a subset of that data needs to be in memory.
When set to use lazy fetching, a controller will fetch objects in batches - default batch size is 96. You can change the default batch size for your application by setting a value for the the user default "com.apple.CocoaBindings.LazyFetchBatchSize". If you have table views bound to an array controller set to use lazy fetching, the size of the controller's batch size will grow as the table views' visible row count grows.
Add, Insert, and Remove operations on a controller that uses lazy fetching behave similarly to the same operations on a regular controller. The difference is that it is faster to sort an arraycontroller using lazy fetching if
- all of the keys in the sortDescriptors array are modeled, non transient properties
- all of the selectors in the sortDescriptors array are compare: or caseInsensitiveCompare:
- there are no changes in the controller's managed object context
KeyValueObserving notifications for IBOutlets during Nib loading
Prior to 10.5, when an outlet instance variable was set on an object during Nib loading, no corresponding KVO notification would be sent. This meant that registered KVO observers and bindings wouldn't be notified that the observed ivar changed. This could lead to bad behaviour or displaying stale data.
For applications linked on or after 10.5, observers will get KVO notifications when the IBOutlet instance variable is set during Nib loading. Observers should expect to get KVO notifications for an observed object before the observed object's awakeFromNib method is called.
NSAccessibility
Cocoa/Carbon Integration
In Leopard, Cocoa accessibility information is now correctly reported for Cocoa windows used in a Carbon application, as well as Cocoa views embedded into Carbon windows via the new Carbon HICocoaView.
Toolbar Accessibility
Image-based toolbar items have always appeared in the accessibility hierarchy as a single AXButton, with the label of the toolbar item serving as the AXTitle of the button. In Leopard, certain configurations of view-based toolbar items will also be represented automatically in the accessibility hierarchy in this manner:
- The item view is an NSButton that reports itself to accessibility as an AXButton.
- The item view is an NSSegmentedControl with a single segment.
- A toolbar item group has an NSSegmentedControl as its view, and has one labeled subitem per segment.
For view-based toolbar items that do not fit into these special cases, the label of the toolbar item will be automatically attached to the item's view as an AXTitleUIElement in Icon & Text display mode, and as the view's AXDescription in Icon Only mode.
New Accessibility Constants
NSAccessibilityGridRole is a role that should be used for user interfaces like thumbnails and media browsers that present a grid of items. The children of a grid are ordered. A grid is like an accessibility list with the addition of three new attributes: NSAccessibilityRowCountAttribute and NSAccessibilityColumnCountAttribute which return the number of rows and columns in the grid, respectively and NSAccessibilityOrderedByRowAttribute which returns a boolean number value indicating whether the children are ordered row major, or column major.
NSAccessibilityTimelineSubrole is a subrole of NSAccessibilitySliderRole and should be used for media timelines, such as QuickTime movie or audio and video playback and editing controls. A timeline can have multiple value indicators as children to represent different markers on the timeline - for instance the current time, an in point, and an out point for video editing. If multiple value indicators are present, each should have an accessibility description.
The optional attribute NSAccessibilityValueDescriptionAttribute should be used when the value of an element does not provide enough information for assistive applications. For instance, the sliders in the Energy Saver preference pane represent values that include "1 minute", "1 hour", "2 hours 20 minutes", and "never." The numeric value of the slider does not convey this information, so the optional value description attribute should be used. The string values of the attribute should be localized, lower case, and as brief as possible to convey the information.
The optional attribute NSAccessibilitySelectedTextRangesAttribute returns an array of ranges of selected text. NSTextView supports selection of multiple ranges of text as well as this new optional attribute. The existing NSAccessibilitySelectedTextRangeAttribute still returns the first selected text range.
NSAccessibilityDisclosureTriangleRole is a new accessibility role for disclosure triangles.
NSColor
Asking for colorWithAlphaComponent: on NSDeviceRGBColorspace and NSDeviceWhiteColorSpace colors would return results in their calibrated counterparts. This has now been fixed.
NSColorSpace
NSColorSpace now has methods to create from and return a CGColorSpace. Note that the method to return a CGColorSpace might return NULL if the NSColorSpace cannot be represented as a CGColorSpace. In addition, creating an NSColorSpace from a CGColorSpace does not guarantee future pointer identity of the CGColorSpace.
NSColorSpace also has methods to return sRGB and Adobe1998 color spaces.
A CMProfileRef leak was fixed in -[NSColorSpace initWithICCProfileData:].
NSColorPanel
NSColorPickingCustom protocol now enables specifying a tooltip for the toolbar button:
- (NSString *)buttonToolTip;
and setting the minimum size for your picker:
- (NSSize)minContentSize;
NSColorPanel will not allow resizing smaller than this size. By default, you will not have to do anything if you properly setup the autosizing attributes in IB for your view.
Services
We now have a number of NSErrors for reporting services errors. Like other NSErrors in NSCocoaErrorDomain, these errors come ready with user-presentable error messages, and for the most part they will automatically be bubbled up to the user via the Cocoa error presentation machinery, so no developer action is needed. The individual error codes can be found in AppKitErrors.h.
The NSUpdateDynamicServices() function is functional in Leopard. When NSUpdateDynamicServices() is called, applications should see updated services immediately, without needing to be quit and relaunched. Services are also updated at login.
NSScroller
To help improve API uniformity, NSScroller's -setFloatValue:knobProportion: method is being deprecated in favor of using the separate -setDoubleValue: and -setKnobProportion: accessors that have been added in Leopard. To maintain binary compatibility, AppKit will continue to invoke overrides of -setFloatValue:knobProportion:. Code that targets Mac OS 10.5 and later should use -setDoubleValue: and -setKnobProportion: instead, and eliminate any overrides of -setFloatValue:knobProportion:. Code that needs to remain compatible with Mac OS 10.4 and earlier should continue to use -setFloatValue:knobProportion:.
NSUserInterfaceValidations
The interface declarations for NSApplication, NSButton, NSDocument, NSDocumentController, NSMatrix, NSMovieView, NSTableView, NSTextField, and NSWindow now correctly show their conformance to the NSUserInterfaceValidations protocol.
NSTabView
Prior to Leopard, an NSTabViewItem added to multiple tab views would produce inconsistencies. For applications built on or after Leopard, tab view items are automatically removed from the old tab view when added to a new one.
NSLevelIndicator behavior changes
Prior to Mac OS X 10.5, NSLevelIndicatorCell ignored its editable flag; all cells were effectively editable. In 10.5 we pay attention to the flag for apps linked on Leopard and later. The default value of -[NSLevelIndicator isEditable] is and has been NO, so it is necessary to explicitly set the flag for cells that should be editable.
NSRulerView
In the past, the method -moveRulerlineFromLocation:toLocation: immediately handled drawing for temporary lines on the ruler which could cause performance degradation when temporary lines were drawn in conjunction with automatic beam sync. Now, the temporary lines are drawn during -drawRect:.
Usage of -moveRulerlineFromLocation:toLocation: remains unchanged with a few caveats. Each new line location is drawn no more than once. There is however now no guarantee that every new line location will be drawn - if a particular location is set to be drawn and then set to be erased before -drawRect: occurs, that location is never drawn. In addition, any subclass that uses the default -moveRulerlineFromLocation:toLocation: but implements -drawRect: will have to override -moveRulerlineFromLocation:toLocation: to handle ruler line drawing in their own -drawRect: method.
NSBrowser
NSBrowser now supports drag and drop. Please see the AppKit example application "SimpleBrowser" for an example of how to use the API and read the comments in NSBrowser.h. The API is very similar to the drag and drop API for NSTableView.
NSTokenField
NSTokenField has been rewritten and numerous bugs were fixed. Tokens now show the pulldown menu icon regardless of selection state. Applications can set the NSTokenAttachmentUsesDynamicPulldownIcon preference setting to preserve the pre-Leopard behavior.
NSGraphicsContext
NSGraphicsContext now has color rendering intent API:
- (NSColorRenderingIntent)colorRenderingIntent;
- (void)setColorRenderingIntent:(NSColorRenderingIntent)renderingIntent;
NSBezierPath
There are now convenience methods for creating rounded rectangular paths:
+ (NSBezierPath *)bezierPathWithRoundedRect:(NSRect)rect xRadius:(CGFloat)xRadius yRadius:(CGFloat)yRadius;
- (void)appendBezierPathWithRoundedRect:(NSRect)rect xRadius:(CGFloat)xRadius yRadius:(CGFloat)yRadius;
-containsPoint: method now follows the winding rule returned from -windingRule.
NSProgressIndicator
NSProgressIndicatorSpinningStyle and determinate progress indicators now render pie-style determinate indicator found in Mail and Xcode. Spinning-style progress indicators now properly use larger images for NSRegularControlSize rendering. Applications dynamically creating progress indicators for 16x16 image now need to explicitly set the control size to NSSmallControlSize.
The threaded animation setting for newly created indicators is on by default now regardless of the style.
NSWorkspace
NSWorkspace method selectFile: inFileViewerRootedAtPath: no longer follows symlinks. It will now show the symlink in the Finder instead of what it points at. If you wish to show the linked file, use -[NSString stringByResolvingSymlinksInPath] to resolve any symlinks before calling the NSWorkspace method.
The setIcon: forFile: options: method on NSWorkspace will set 512x512 icons, unless NSExclude10_4ElementsIconCreationOption is set in options.
The NSWorkspace method getFileSystemForPath: isRemovable: isWritable: isUnmountable: description: type: now returns meaningful values in description: and type:. (Prior to Leopard, these values were always nil.)
NSMovie/NSMovieView in 64-bit (New since WWDC 2007 Seed)
For 64-bit, NSMovie and NSMovieView have been deprecated in favor of QTKit's QTMovie and QTMovieView. However, because NSMovies may exist in archives that may need to be read or written by 64-bit applications, limited functionality has been provided for NSMovie in 64-bit to enable this.
NSMovies can be unarchived in 64-bit as expected, but because of the absence of NSMovieView, they are not useful on their own. To facilitate the transition to QTMovie and QTMovieView, the existing -QTMovie method on NSMovie has been modified to return a reference to a QTMovie object in 64-bit. This QTMovie instance contains the same movie data that the original NSMovie contained.
For backwards compatibility purposes, an NSMovie can be created in 64-bit using the -initWithMovie: method, which has been modified for 64-bit to accept a QTMovie instance. Archives containing these NSMovie objects are backwards compatible with NSMovies in 32-bit.
Please note that NSMovie in 64-bit does not support movies referenced by URLs. -initWithCoder: will properly read archives containing these movies, but will return nil instead of a valid NSMovie object.
TextEdit
The TextEdit application in 10.5 has been changed to use NSDocument. It thus provides many features provided or enabled by NSDocument, such as correct file tracking and autosaving. In addition it continues to be a showcase for the Cocoa text system, and highlights many of its new features. You can find more info in the README file, in /Developer/Examples/AppKit/TextEdit.
"open" tidbit
Thank you for reading this far! If you are a Terminal or command line user, be sure to check out some of the new features in "open". For instance "open -h" will now search and open header files in Xcode (or your default editor for header files).
Copyright © 2007 Apple Computer, Inc.