OS X Developer Release Notes:
Cocoa Application Framework (10.7 and Earlier)
This document contains the release notes for OS X v10.7, its updates, and earlier. Please refer to the current release notes for AppKit before referring to this document, as the more recent changes in the current release could have obsoleted some of the items discussed here.The notes below are split into the following sections:
- Notes specific to MacOS X 10.7
- Notes specific to MacOS X 10.6
- Notes specific to MacOS X 10.5
- Notes specific to MacOS X 10.4
- Notes specific to MacOS X 10.3
- Notes specific to MacOS X 10.2.5
- Notes specific to MacOS X 10.2.3
- Notes specific to MacOS X 10.2
- Notes specific to MacOS X 10.1
- Notes specific to MacOS X 10.0
- Notes specific to MacOS X Public Beta
- Notes specific to MacOS X Developer Preview 4
- Notes specific to MacOS X Developer Preview 3
- Notes specific to MacOS X Developer Preview 2
- Notes specific to MacOS X Developer Preview 1
- Notes specific to MacOS X Server
- Notes specific to MacOS X Server Developer Release 2
- Notes specific to MacOS X Server Developer Release 1
- Summary of OpenStep 4.x notes
Notes specific to MacOS X 10.7
Some of the major topics covered in this section include:- Modernized document model and Auto Save
- Asynchronous saving
- Mechanisms for file coordination and file presentation (Foundation)
- Versions
- Resume (a.k.a. state restoration)
- Full screen
- Autolayout
- New scroller look and behavior
- Multi-image dragging
- Support for resolution independence
- Managing high resolution bitmap images
- Automatic reference counting (a.k.a ARC)
- iCloud
- Apple push notifications
- Accessibility updates
- TableView/OutlineView updates
- Popover support
- Vertical text
- Find bar and incremental search
- Text inspector bar
Automatic Reference Counting
Automatic Reference Counting (ARC) is a new feature which enables the compiler to generate calls for retain, release, and autorelease. It is available with Xcode 4.2, for OS X and iOS, back to OS X v10.6 and iOS 4. However, on these earlier systems the zeroing weak reference feature of ARC is not available.On 10.7, some Cocoa classes including NSWindow, NSTextView, NSFont, and NSImage do not support zeroing weak references. Many other classes do.
Unlike garbage collection, ARC does not provide any cycle-detection or breaking features. So traditionally weak references such as delegates, outlets, or targets should remain weak by default under ARC. In most cases this can mean zeroing-weak, although in cases where the object in question is one of those that does not support zeroing weak reference (or you want to deploy on 10.6), you may have to use non-zeroing weak, which is indicated with the variable qualifier __unsafe_unretained or property attribute assign.
Note that Xcode 4.2 defaults to ARC when creating new projects, and in the WWDC seed release generates outlet declarations that are strong (in new projects as well as when adding new outlet declarations from Interface Builder). In most cases these should be changed to weak (zeroing or not) to avoid cycles that may cause leaks.
iCloud
10.7 includes APIs to enable applications to store configuration information and documents in the iCloud. The main APIs here are in a number of Foundation classes; please refer to the Foundation release notes.Lion's Two Scroller Styles
Lion introduces new, iOS-inspired "Overlay scrollers", as well as restyled "Legacy" scrollers that provide for application compatibility, accessibility, and accommodation of user preferences. Applications should be prepared to work with either style of scroller, and for the system's preferred scroller style to change over time as the user connects/disconnects pointing devices or makes changes to Appearance, Trackpad, or Mouse scrolling preferences."Overlay" scrollers are composited atop the margins of an NSScrollView's contentView, instead of subtracting reserved space from the NSScrollView's potential content area. Scroll thumbs appear automatically during gesture-scrolling (as well as programmatically induced scrolling), and disappear after scrolling stops, giving users an uncluttered, content-focused UI. This automatic show/hide behavior and coordination of a horizontal and vertical scroller pair requires a managing NSScrollView, which provides the necessary logic and relationship to the scrollable content area. An NSScroller instance that's used on its own, without being managed by a parent NSScrollView, can be configured to use the Overlay scroller appearance, but will not have the associated animated show/hide behavior.
To avoid interfering with user-content interaction, simply mousing into the content area edges does not cause the Overlay scrollers to change from hidden to shown, since it's possible the user may intend mouse actions in those margins for the underlying content. Gesture-scrolling, or any keyboard equivalent or application-defined action that results in scrolling, is necessary to cause the scrollers to be shown.
Once the Overlay scrollers are shown, any mouse motion within the scrollable content area is sufficient to delay their hiding, which allows time to mouse into either scroller if desired. When the mouse enters an Overlay scroller, the scroller shows its track to aid the user in hitting the desired scroller part, and both scrollers remain pinned to visible until the mouse exits the scroller.
Overlay scrollers offer the familiar mouse-driven capabilities: By mousing into an Overlay scroller, users can drag the scroller knob (including [Option]+drag to achieve fine scrolling) and can click in the scroller track to page or jump (according to the user's "Click in the scroll bar to:" Appearance preference setting).
Application code can programmatically request showing of the Overlay scrollers using the NSScrollView's new -flashScrollers method. This may be desirable when changing a document view's size or swapping new content into the view, or to give the user a sense of the current position within the scrollable range at each step of an incremental search or similar operation. (Without the explicit -flashScrollers message at each find step, the scrollers would be pulsed to visible only when advancing to the next find result happens to cause programmatic scrolling.) -flashScrollers may be sent any number of times; AppKit coalesces the requests into smooth scroller show/hide behavior. An NSScrollView's vertical and horizontal Overlay scrollers always show and hide together. -flashScrollers is OK to send to an NSScrollView that's using the Legacy scroller style, and simply has no effect in that case.
Overlay scrollers are designed for use with gesture-scroll-capable devices. To provide compatibility for various circumstances, including users with Accessibility needs and systems with non-gesture-scroll pointing devices, AppKit also provides a "Legacy" scroller style. Legacy scrollers have been restyled to provide an appearance similar to Overlay scrollers, and no longer have scroll arrow buttons, but are otherwise functionally identical to 10.6 Aqua scrollers: They have the same metrics, have layout space reserved for them instead of being overlaid atop the contentView, and are always visible (except when the "autohidesScrollers" NSScrollView feature is used). NSScroller has a new "scrollerStyle" property that can be consulted to determine the style in use by a particular NSScroller instance. A matching setter method is tentatively provided, but should not in general be used except by NSScrollView's implementation.
The "Appearance" preference panel contains a new "Show scroll bars:" preference that enables users to choose between Overlay and Legacy scrollers. By default, the setting is "Automatically, based on input device", which leaves the decision up to AppKit based on the connected pointing device hardware, but users can also choose "When scrolling" to prefer Overlay scrollers, or "Always" to request Legacy scrollers.
When the setting is "Automatically…" and more than one pointing device is connected, the decision is based on the most capable pointing device that is found, disregarding a built-in trackpad if other pointing devices are present. The idea is that a user who connects a pointing device probably means to use it, but we also don't want to force fallback to the least common denominator when a touch-scroll capable external device is also present. A touch-scroll device that has its touch-scroll capability disabled in the user's Mouse/Trackpad preferences is treated the same as a non-capable device, but, again, the internal trackpad's configuration is disregarded when one or more external pointing devices is present. So, for example, if a user has a MacBook Pro with a Magic Mouse connected, the trackpad's touch-scroll capability can be disabled without causing fallback to Legacy scrollers, as long as the Magic Mouse has its touch-scroll capability enabled.
AppKit updates scrollers at runtime to reflect changes to the user's "Show scroll bars:" Appearance preference, and as pointing devices (including wireless devices) are connected and disconnected.
There are two cases where a particular set of scrollers are forced to the Legacy style, even if the user's preference is set to "Show scroll bars: When scrolling".
An NSScrollView's scrollers may be forced to the Legacy scroller style if AppKit detects the presence of an "accessory view" in the track areas of the NSScrollView. The zoom popup that appears in TextEdit's wrap-to-page mode is an example of such an accessory view. This technique of nudging scrollers aside to make room for additional small informational views or controls was useful when scrollers had space reserved for them separate from the content area, but isn't compatible with scrollers that sit atop the content area and disappear when not in use. Applications that want to make use of Overlay scrollers should seek alternatives to accessory views for presenting the desired functionality. Once the accessory views are removed or moved out of the scroller margins, the ScrollView will again be eligible to use Overlay scrollers.
Instances of an NSScroller subclass are, for compatibility, defaulted to the Legacy scroller style and behavior. NSScroller subclasses can declare themselves to be Overlay-scroller-compatible using the new +isCompatibleWithOverlayScrollers API, allowing customization relative to the Overlay scroller appearance and behavior. See the comments accompanying this method in NSScroller.h for usage and the complete set of compatibility requirements.
NSAnimationContext API Additions
NSAnimationContext now has a “timingFunction” property that’s analogous to the CATransaction API’s “animationTimingFunction”. Animations initiated through the “animator” proxy syntax, that do not have an explicitly specified timingFunction, will inherit the enclosing NSAnimationContext’s timingFunction if it is not nil (which is the NSAnimationContext default).As with the existing “duration” property, changing an NSAnimationContext’s timingFunction causes the same change in the underlying CATransaction’s animationTimingFunction. Also as with the “duration” property, you may change the timingFunction any number of times within a given NSAnimationContext -beginGrouping/-endGrouping block. Changes to the timingFunction will apply to any animations that are subsequently initiated in that NSAnimationContext grouping (until the timingFunction is possibly changed again).
NSAnimationContext has also been given a new “completionHandler” block. Once set to a non-nil value, a context’s completionHandler is guaranteed to be called (on the main thread) as soon as all animations subsequently added to the current NSAnimationContext grouping have completed (or been cancelled). This API drives the underlying CATransaction "completionBlock" property (though AppKit may assign a different, intermediary completionBlock to the current CATransaction). NSAnimationContext's completionHandler firing waits for all animations to which the handler applies, independent of whether they are evaluated by AppKit or delegated to Core Animation for evaluation in the render tree. If no animations are added before the current NSAnimationContext grouping is ended (or the completionHandler is set to a different value), the handler will be invoked immediately.
Lastly, NSAnimationContext has an accompanying new +runAnimationGroup:completionHandler: class method. Its use provides for more natural syntax, letting you specify a completion block body after the set of animation actions whose completion will trigger the completion block.
The following:
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context){
// Start some animations.
[[myView animator] setFrameSize:newViewSize];
[[myWindow animator] setFrame:newWindowFrame display:YES];
} completionHandler:^{
// This block will be invoked when all of the animations started above have completed or been cancelled.
NSLog(@"All done!");
}];is semantically equivalent to:
[NSAnimationContext beginGrouping];
[NSAnimationContext setCompletionHandler:^{
// This block will be invoked when all of the animations started below have completed or been cancelled.
NSLog(@"All done!");
}];
// Start some animations.
[[myView animator] setFrameSize:newViewSize];
[[myWindow animator] setFrame:newWindowFrame display:YES];
[NSAnimationContext endGrouping];The “NSAnimationContext *context” parameter passes the thread’s current NSAnimationContext to the block as a convenience, so that code within the block that wants to change or query properties of the current context does not have to query (possibly repeatedly) for [NSAnimationContext currentContext].
NSWindow “animationBehavior” property
In 10.7, we added automatic animation of NSWindow -orderFront:/-orderOut: operations, which can be controlled using the new “animationBehavior” NSWindow property. By default, an NSWindow’s animationBehavior is set to NSWindowAnimationBehaviorDefault, which causes AppKit to determine the style of animation to use automatically based on its inference of a window’s “type” from various window properties. A window’s animationBehavior can be set to NSWindowAnimationBehaviorNone to disable Appkit's automatic animations for the window. (This may be useful if AppKit’s animation interferes with an animation that your application implements.) Or, it can be set to one of the other non-Default NSWindowAnimationBehavior values to override AppKit's automatic inference of appropriate animation behavior based on the window's apparent type.Custom View Unarchiving and “wantsLayer”
Prior to 10.7, a Custom View being unarchived from a .nib or .xib file would always receive a -setWantsLayer: message during loading, regardless of whether the unarchived wantsLayer setting was YES or NO. Receiving this message with a parameter of NO could interfere needlessly with a wantsLayer==YES setting the view might attempt to make in its -initWithFrame: method. On 10.7, AppKit will only send -setWantsLayer: to a Custom View during unarchiving if the parameter is YES, eliminating this pitfall.Easier Focus Ring Management
A "focus ring" is an Aqua-tinted halo that indicates a control is the active first responder that will receive key events. New NSView API in 10.7 makes it much easier to specify a focus ring shape to be shown for a custom view, while providing AppKit with the information it needs to automatically ensure consistent focus ring erasure and redraw. This new automatic focus ring model also yields correct results in layer-backed mode, which previously was constrained by the inability of focus ring drawing to extend out, reduces the need for view content redraw in many cases, and even provides a useful hint to Accessibility regarding where the user's attention is likely to be focused.- (void)drawFocusRingMask NS_AVAILABLE_MAC(10_7);A custom view that wants a focus ring to be shown when it is the active first responder, and that doesn't inherit that behavior from a superclass, needs only specify a focus ring mask shape. AppKit will automatically determine when the focus ring needs to be shown and erased (as the view becomes, and ceases to be, the active first responder -- that is, the firstResponder in the application's keyWindow), and will invoke the -focusRingMaskBounds and -drawFocusRingMask methods as needed.
- (NSRect)focusRingMaskBounds NS_AVAILABLE_MAC(10_7);
- (void)noteFocusRingMaskChanged NS_AVAILABLE_MAC(10_7);
NSView's implementation of -drawFocusRingMask draws nothing, and its -focusRingMaskBounds method returns an empty rectangle. To opt into the new 10.7 focus ring drawing model, a view simply overrides the -drawFocusRingMask method to draw the shape whose outline should serve as the template for the focus ring, and -focusRingMaskBounds to return the bounding box of that shape (expressed in the view's interior ("bounds") coordinate space). For example, a simple rectangular focus ring surrounding a view's frame would be requested by simply doing:
- (void)drawFocusRingMask {
NSRectFill([self bounds]);
}
- (NSRect)focusRingMaskBounds {
return [self bounds];
}
AppKit will automatically invoke these methods when appropriate, to render the view's focus ring. (Make sure the view is eligible to become its window's firstResponder, by overriding -acceptsFirstResponder to return YES.) The focus ring is rendered using the style specified by the view's focusRingType (which must not be NSFocusRingTypeNone unless you want to suppress drawing of the focus ring). An implementation of -drawFocusRingMask can assume that it is drawing in the view's interior (bounds) coordinate space, and that the fill and stroke colors have been set to an arbitrary fully opaque color. It needs only draw the desired focus ring shape (or an image or other object whose outline defines the focus ring mask).This model provides an alternative to the previous focus ring technique, wherein a view was responsible for determining when to draw and erase its own focus ring, was required to perform its focus ring drawing explicitly as part of its -drawRect: implementation, and had to use the special -setKeyboardFocusRingNeedsDisplayInRect: method to ensure correct focus ring erasure and redraw. A view that adopts this new 10.7 focus ring API should no longer call NSSetFocusRingStyle() and perform its own focus ring drawing in -drawRect:, unless running on 10.6 or earlier, since the use of NSSetFocusRingStyle() would cause AppKit to fall back to the old focus ring drawing model for the view. Invoking -setKeyboardFocusRingNeedsDisplayInRect: is OK, but will cause more redraw than necessary on 10.7, where AppKit is able to determine the exact coverage of focus ring rendering based on the focus ring mask, and is thus able to show, hide, and move the focus ring with minimal redraw of affected window areas. In layer-backed mode, AppKit draws a focus ring that's implemented using this new 10.7 API into its own layer, which permits the focus ring to extend outside the associated view's backing layer bounds and allows the focus ring to be efficiently shown, moved, and hidden without necessitating redraw of the associated view content.
The third new method, -noteFocusRingMaskChanged, is provided for you to invoke when some state that AppKit doesn't know about (such as which of a set of non-view components of your content you consider "focused"), that affects the focus ring shape or where you'd like it to be drawn, changes. AppKit automatically invokes -noteFocusRingMaskChanged when your view receives a -setNeedsDisplayInRect: message (as happens when the view is resized or moved), and when the view's focusRingType is changed, which covers most cases. It is only necessary to invoke -noteFocusRingMaskChanged in cases where some internal state change of your own, that AppKit can't know about, affects the desired shape or position of your focus ring mask.
For the benefit of cell-based NSControls, there is a parallel set of overridable NSCell API:
- (void)drawFocusRingMaskWithFrame:(NSRect)cellFrame inView:(NSView *)controlView NS_AVAILABLE_MAC(10_7);There is no NSCell equivalent to -noteFocusRingMaskChanged. When a cell determines that its focus ring mask needs updating, in a situation where the cell’s content is not otherwise being redrawn, it can invoke [[self controlView] noteFocusRingMaskChanged].
- (NSRect)focusRingMaskBoundsForFrame:(NSRect)cellFrame inView:(NSView *)controlView NS_AVAILABLE_MAC(10_7);
New Tiling Model for Layer-Backed Views
Handling of large views poses a fundamental challenge for a layer-backed rendering model, as it implies the need to allocate commensurately large layer backing stores that consume memory and may exceed the maximum allowable texture size of the host system's graphics hardware. Some form of tiling is necessary, to enable the needed, visible portions of a view to be buffered, while potentially large regions that aren't currently visible (e.g. parts of a document view that are scrolled out of view) are free to be unbuffered until they are made visible.In 10.5 and 10.6, AppKit's tiling model was based on Core Animation's CATiledLayer, and assumed that the documentView of an NSScrollView, and only the documentView of an NSScrollView, should receive a tiled backing layer. This approach permitted common case of large layer-backed documentViews, but didn't address the potential for other views to exceed the maximum texture size, and came with asynchronous drawing behavior (fade-in of tiles as they were drawn) that in some cases didn't yield the right user experience.
On 10.7, AppKit uses an entirely new, synchronous tiling implementation that eliminates the visual tile-fade-in artifacts associated with the previous model, and that automatically tiles and untiles view backing layers as necessary based on the view's size, rather than being tied to the assumption that only an NSScrollView's documentView is likely to be large. A layer-backed view that is tiled will be called to draw via the usual -drawRect: mechanism to draw portions of its content to be cached into tiles. AppKit automatically manages the creation and population of tile layers, which reside under a "tile container layer" parent that AppKit adds as a sublayer of the view's normal backing layer.
The new tiling model is automatic and does not require the use of any new API, but responds to a set of user defaults that can be used to experimentally tune its parameters on a per-process basis. These user defaults should be considered private and subject to change in future releases, but the ability to adjust them might be of use if the standard values prove unsuitable for a particular application.
NSViewBackingLayerTileSize determines the tile size, and defaults to 512 (pixels on a side). Power-of-two values are recommended. (512 * 512 * 4bytes/pixel) = 1MB per tile. Larger values produce fewer tiles and reduce tile management overhead, but will tend to waste more space for views whose frames don't map to an integral number of tiles.
NSViewMaxNonTiledBackingLayerSize specifies the maximum size (in pixels) that a view's backing layer can be on either side before the view is forced to be tiled. The default is 2000 pixels.
NSViewMaxResidentTiles determines the maximum number of tile backing stores that can be allocated for the process. When AppKit reaches this limit, it begins discarding the contents of non-visible tiles to make room for new visible tiles. The default is 64.
NSAppKitBackingLayerTiling controls the new tiling model, and defaults to YES. It can be set to NO to revert to the 10.6 tiling behavior (use of CATiledLayer backing layers for NSScrollViews' documentViews).
NSDebugBackingLayerTiling can be set to YES to enable informative logging regarding tiling activity. The output could be useful in assessing and tuning performance, or when filing a bug report.
NSOpenGL and Resolution Independence
As part of 10.7's updated Resolution Independence architecture, individual views have a new means to declare their compatibility with high-resolution framebuffers. Since OpenGL is innately a pixel-oriented API and much existing OpenGL code does not function correctly when "surprised" with a high-resolution backing store, OpenGL views are run in scaled mode by default, and need to specifically opt into high-resolution backing.The accessors for this new property are declared in a category on NSView in NSOpenGLView.h, to emphasize that their relevance is confined to OpenGL rendering, but the property is applicable to any view that renders using an associated NSOpenGLContext.
@interface NSView (NSOpenGLSurfaceResolution)The "wantsBestResolutionOpenGLSurface" property specifies whether a given view instance wants, and is capable of correctly handling, an OpenGL backing surface (framebuffer) with resolution greater than 1 pixel per point. This property is relevant only for views to which an NSOpenGLContext is bound (including, but not limited to, NSOpenGLViews); its value does not affect the behavior of other views, including CALayer-backed views (which may choose to render at a higher surface resolution independent of this property's value. For compatibility, wantsBestResolutionOpenGLSurface defaults to NO, providing a 1 pixel per point framebuffer regardless of the backing scale factor for the display the view occupies. (When the backing scale factor is > 1.0, the rendered surface contents are scaled up to the appropriate apparent size.) Setting this property to YES for a given view gives AppKit permission to allocate a higher-resolution framebuffer when appropriate for the backing scale factor and target display. AppKit may vary the surface resolution when the display mode is changed or the view is moved to a different display, but with this property set to YES it is capable of allocating a surface of greater than 1 pixel per point for the view.
- (BOOL)wantsBestResolutionOpenGLSurface NS_AVAILABLE_MAC(10_7);
- (void)setWantsBestResolutionOpenGLSurface:(BOOL)flag NS_AVAILABLE_MAC(10_7);
@end
To function correctly with wantsBestResolutionOpenGLSurface set to YES, a view must take care to perform correct conversions between view units and pixel units when needed. For example: The common practice of passing the width and height of [self bounds] to glViewport() will yield incorrect results (partial instead of complete coverage of the render surface) at backing scale factors other than 1.0, since the parameters to glViewport() must be expressed in pixels. Instead, use the dimensions of [self convertRectToBacking:[self bounds]], which are in appropriate (pixel) units.
The "wantsBestResolutionOpenGLSurface" property is archived (keyed archiving required).
For testing purposes only, the effect of this property can be overridden globally for all views in a process, using the "NSSurfaceResolution" user default. If NSSurfaceResolution is set to "Device", all views that have surfaces (including not only OpenGL surfaces, but layer tree render surfaces as well) will be opted into using the best resolution surface for the primary display the view is presented on. This can be used to quickly assess whether an apps view's are ready for non-1x surfaces. If NSSurfaceResolution is set to "1x", all views that have surfaces will be opted into using 1x (1 pixel per point) surfaces, independent of the display or backing scale factor. If NSSurfaceResolution is set to any other value, or no value is present for it, then wantsBestResolutionOpenGLSurface will be consulted as described above for views that perform NSOpenGL rendering, and AppKit will separately determine the appropriate resolution for other surfaces, as also described above.
NSView now passes unhandled -rightMouseDown: events up the responder chain
Prior to 10.7, NSView did not pass unhandled -rightMouseDown: events up the responder chain. On 10.7, NSView passes -rightMouseDown: up the responder chain, if AppKit doesn’t find an associated context menu to display for the view. To avoid binary compatibility issues, this new behavior is enabled only for applications linked on 10.7 or later.Deprecation of Pixel Buffers (PBuffers)
Pixel Buffers (PBuffers) have been deprecated in 10.7. Modern clients should use Frame Buffer Objects (FBOs) instead of NSOpenGLPixelBuffer and associated methods. See GL_EXT_framebuffer_object.Deprecation of NSOpenGLContext -setOffScreen:width:height:rowbytes:
NSOpenGLContext’s -setOffScreen:width:height:rowbytes: API should be considered deprecated as of 10.7. As noted in NSOpenGL.h, this API forces use of the software rasterizer, which is much slower than GPU rendering. It is generally much better nowadays to use a normal pixel format with either an off-screen window or an FBO (Frame Buffer Object), and then call glReadPixels() to read the rendered result back to CPU memory (if that's where it is needed).Changes for Resolution Independence
Significant changes have been made to the underlying graphics model for resolution independence, and the behavior of Cocoa applications running under high resolution operation has been updated. Scaling of user interface content is now limited to an integral 2x scale factor, and is keyed off of per display settings rather than a global scale factor. Pixel-accurate high resolution bitmap artwork now only needs to be provided at a 2x size multiple, similar to the operation of iOS on a Retina display.The underlying device pixel geometry of both window backing stores and screen frame buffers has been virtualized and abstracted behind new point-based coordinate spaces for NSWindow and NSScreen. The relationship between pixels and points is now driven by attributes of the display mode any given display is operating in. When a display is configured to run in one of the new "HiDPI" modes, the corresponding NSScreen object and any windows created on that display are automatically scaled with a 2:1 pixel per point backing ratio.
The implication of this change is that under high resolution operation, rectangles, sizes and positions are all unchanged from low resolution operation, and are now universally assumed to be in points. Previously, event coordinates and both the window base coordinate system and the screen global coordinate system were presumed to be in device pixels, and were in an "unscaled" space compared to view coordinates. This is no longer the case: window frame coordinates, event coordinates and view coordinates converted to window space are all expressed in points and do not change between low and high resolution operation. The relevant scaling and mapping to the display is performed automatically by the Quartz Window Manager and by the NSGraphicsContext attached to any given window.
Coordinate Space Conversion
The consequence of window and screen device virtualization is significantly enhanced application compatibility under high resolution operation, and under most circumstances, apps will not need to be aware of the underlying pixel geometry of their windows. However, circumstances can arise where applications may wish to perform pixel-accurate drawing operations to optimize or align their content under high resolution operation. To address these needs, a complete set of new coordinate space conversion methods have been introduced to go back and forth between virtual point geometry and a pixel aligned coordinate system matching the characteristics of the backing store for that object.All of the methods below are defined on NSView, NSWindow and NSScreen.
/* New methods for converting to and from backing store pixels */
- (NSPoint)convertPointToBacking:(NSPoint)aPointIn order to provide a conceptually consistent mapping between window and screen coordinates in the face of screen device virtualization, two new methods have been introduced on NSWindow. Under high resolution operation, these methods now simply offset the given rectangle based on the window's position in screen space. No scaling transform is necessary since both coordinate systems are virtual and aligned in point space.
- (NSPoint)convertPointFromBacking:(NSPoint)aPoint
- (NSSize)convertSizeToBacking:(NSSize)aSize
- (NSSize)convertSizeFromBacking:(NSSize)aSize
- (NSRect)convertRectToBacking:(NSRect)aRect
- (NSRect)convertRectFromBacking:(NSRect)aRect
- (NSRect)convertRectToScreen:(NSRect)aRect
- (NSRect)convertRectFromScreen:(NSRect)aRect
Deprecated Coordinate Conversion Methods
As a result of the virtualization of window and screen coordinates, the concept of the 'base' coordinate system has been deprecated, along with methods referring to that coordinate space. The following methods have now been superseded by the convertToBacking methods detailed above./* NSView deprecated methods */
- (NSPoint)convertPointToBase:(NSPoint)aPoint
- (NSPoint)convertPointFromBase:(NSPoint)aPoint
- (NSSize)convertSizeToBase:(NSSize)aSize
- (NSSize)convertSizeFromBase:(NSSize)aSize
- (NSRect)convertRectToBase:(NSRect)aRect
- (NSRect)convertRectFromBase:(NSRect)aRect
/* NSWindow deprecated methods */
- (NSPoint)convertBaseToScreen:(NSPoint)aPoint
- (NSPoint)convertScreenToBase:(NSPoint)aPoint
Compatibility Notes for High Resolution Operation
It is important to note that in order to maximize backwards compatibility, the deprecated NSView and NSWindow methods will retain their historic compatible and documented behaviors.When running at @2x, the behavior of these methods will still remain consistent with their previously documented semantics, but there will be an unavoidable break in compatibility for code which feeds pixel-based positions (e.g. resulting from -[NSView convertPointToBase:]) into -[NSWindow convertScreenFromBase:].
Application software calling -convertBaseTo/FromScreen: incorrectly will need to adopt the new routines and/or ensure that they refrain from using NSView's convertTo/FromBase routines when providing input positions, and instead use -[NSView convertPoint:To/FromView:nil] instead. (Note: This is the way everyone did things anyway prior to 10.5 when -[NSView convertTo/FromBase:] was introduced)
Backing Aligned Drawing under High Resolution Operation
One of the most common reasons for making drawing code aware of its underlying backing pixel geometry is to perform rounding to ensure pixel aligned drawing or layout. Although NSView's -centerScanRect: has been updated to work correctly under high resolution operation, some scenarios call for tighter control over the rounding behavior of the alignment operation on each edge of a rectangle. We have introduced a new convenience method to satisfy this need, built on top of the services provided by Foundation's NSIntegralRectWithOptions() API.This method is available on NSView, NSWindow and NSScreen.
- (NSRect)backingAlignedRect:(NSRect)aRect options:(NSAlignmentOptions)optionsThis method accepts rectangles in local (virtual) coordinates and ensure that the result is aligned on backing store pixel boundaries, subject to specific rounding hints given in the options argument. Foundation already provides NSIntegralRect(), which takes a rectangle and pushes each edge outward to the nearest integral boundary. The new method is a slightly more flexible version that gives you control over how the rect is massaged into being integral, after the input rectangle is converted to the appropriate backing space.
Determining Scale Factor
The move to virtualization of screen space and event coordinates fortunately means that developers remain isolated from the hardware details of the displays they are presenting their content on.There are some scenarios, however, where an app that is resolution-aware may want to reason on its own about the display environment it is running in, and make decisions or computations that are not effectively covered by existing API. These circumstances require an efficient way to determine the effective scale factor of the current display environment.
A new public method will be introduced that returns the effective scale relationship between virtual and physical coordinates for a given screen in it's current mode. This factor can be thought of as the extra multiple of device pixels which the system has determined need to be used when representing user content on the given display.
/* NSScreen and NSWindow methods */
- (CGFloat)backingScaleFactorThis method is expected to return 2.0 for high resolution scaled display modes, and 1.0 for all other cases.
An identical API is needed on NSWindow to report the scale factor relationship of its backing store pixels to virtual window coordinates, since that relationship is independent of its containing screen. This method is critically useful for contexts where a window exists, but the graphics context may not be setup yet or may be ambiguous. As an example, consider an off screen window. The AppKit will ensure that this method will return a 'reasonable' answer depending on the available display environment.
It is important to note that this number returned by this method does not represent anything concrete, such as pixel density or physical size, since it can vary based on the configured display mode. For example, the display may be in a mirrored configuration that is still high resolution scaled, resulting in pixel geometry that may not match the native resolution of the display device.
For almost all common cases, developers should avoid keying off of or using the backingScaleFactor as an input to layout or drawing calculations. Developers should avail themselves of the backing coordinate space conversion methods instead, as the resulting code will more likely work consistently and correctly under both low and high resolution operation.
Window edge resizing
In 10.7, windows may be resized from all edges and corners, and the resize indicator has been removed. This affects all Cocoa apps, and no new API is associated with this behavior. The existing live resize support functions continue to function for all resize directions, and the best practices for maximum live resize performance are unchanged. The setShowsResizeIndicator: method is ignored and the NSWindow method showsResizeIndicator always returns NO.The cursor also changes to indicate when a click will result in a resize operation, and in which direction. The available resize directions are determined from the minSize and maxSize of a window, and of course whether NSResizableWindowMask is set. Previously it was possible to limit resizing with the NSWindow delegate method windowWillResize:toSize:, but this approach will produce misleading cursors, so prefer to set a minSize and maxSize in addition to (or instead of) this delegate method.
NSWorkspace's NotificationCenter and Blocks
In SnowLeopard, the blocks-based API was not reliable for adding observers to the special notifications vended by NSWorkspace's notification center (such as NSWorkspaceDidTerminateApplicationNotification). Specifically, at least one observer would have to use the target - selector based API for the notification to be successfully received by the blocks-based API.In 10.7, this has been resolved, so that NSWorkspace's notification center works with the blocks-based API in all cases.
NSWorkspace openURLs: return value
In 10.7, the NSWorkspace method - (BOOL)openURLs: withAppBundleIdentifier: options: additionalEventParamDescriptor: launchIdentifiers: will now return NO and not open anything, if the bundle identifier does not refer to an existing app. In SnowLeopard and earlier, it would return YES, ignore the bundle ID, and open the URLs with the default apps. If the bundle identifier is nil or empty, it will use the default application for the URLs (which is the same behavior on SnowLeopard and earlier).To determine if a bundle identifier is valid, you can use the absolutePathForAppBundleWithIdentifier: method.
NSWorkspace setDesktopImageURL: now works with folders
In 10.7, you may pass a URL to a folder to the NSWorkspace desktop image API, and the desktop will cycle through the images in the folder (non-recursively). Similarly, the desktopImageURLForScreen: API may return a URL to a folder.Self Service
Before 10.7, if an application invoked its own Service and the Service has return types, the app would deadlock until the timeout (or the user hits escape). In 10.7, an application can invoke its own Service without deadlock.Restorable State
In 10.7, AppKit supports a way for applications to restore their state when relaunched. See the methods in NSWindowRestoration.h for more information.Ignoring Existing Restorable State
When the user default ApplePersistenceIgnoreState is defined, existing restorable state and Untitled documents are ignored. New restorable state and Untitled document autosaves are redirected to a temporary directory, whose path will be logged to the console. This user default is intended for automated tests that want to start with a clean environment, and for debugging.Toolbar Versioning
In 10.7, when apps add items to their default toolbar (perhaps in new versions), users with customized toolbars will have the new items automatically added.Prior to 10.7, if an application added a new item to a toolbar's default item list, that new item would not appear for users with customized toolbars. This is because the NSToolbar simply stored the list of items in the customized toolbar, so there was no way to distinguish between an item that was removed by the user, and an item that was not available when the app was last run.In 10.7, NSToolbar records both the customized list of items and the default items. When run on a system with one or more new items in the default array, it will insert the new item into the customized toolbar. NSToolbar makes a best-effort attempt to add it at a location that reflects its position in the default array, but it will always be added somewhere.
Furthermore, if the customized toolbar is identical to the default toolbar, the customizations are no longer recorded at all, so that all changes to the default array are reflected.
These changes are automatically applied when the app is run on 10.7 (so the user must have launched the previous version of the app at least once on 10.7). No app adoption is necessary to take advantage of these enhancements.
NSWorkspace recycleURLs Changes for Volumes Without Trash
In SnowLeopard, if you use use recycleURLs: on a volume that does not support trash, such as an AFP mount, the files would not be moved and the result would be a "file not found" error. In 10.7, this method has changed to prompt the user to either delete the files or to cancel. Canceling results in NSUserCanceledError, while deleting results in the error obtained from attempting to delete the file, or no error if the file is successfully deleted.Text and Search Fields in Toolbars
In 10.7, search fields and text fields draw slightly differently when they are within toolbars, compared to within the window's content view. The toolbar variant requires one additional point, so text and search fields in toolbars will have their height automatically increased to at least 23 points. Furthermore, if an NSToolbarItem has a search field as its view, that search field automatically has its minimum and maximum size adjusted to the system-specified standard values (currently 140 and 240 points).Toolbar Contextual Menu Changes
In 10.7, NSToolbar no longer allows users to control the visibility priority of toolbar items, although users can still add and remove items. Furthermore, the Remove Item menu item has itself been removed, except when running with Voice Over.Toolbar Separator and Customize items removed
In 10.7, the Customize Toolbar item and the Separator item (with the vertical dots) have been removed from toolbars and customization palettes, and their item identifiers are ignored.Right to Left in -[NSMenu popUpMenuPositioningItem:atLocation:inView:]
In 10.7, the NSMenu method popUpMenuPositioningItem:atLocation:inView: will position the menu to the right of the passed-in point, when running in a right to left context. See the description of the method in the NSMenu.h header for more information.NSResizableWindowMask and window title bars
Prior to 10.7, passing NSResizableWindowMask to NSWindow init methods would create the window with a titlebar, as if you had passed NSTitledWindowMask. In 10.7, this is no longer true: passing NSResizableWindowMask without NSTitledWindowMask will create a resizable window with no titlebar. For compatibility, this change only affects applications compiled on 10.7 or later, and only affects windows loaded from nibs that were saved on 10.7.For maximum compatibility, you should of course specify NSTitledWindowMask whenever you want a window to have a titlebar.
Untitled windows at launch
As part of the restorable windows feature, the application delegate may not be asked to create an Untitled window at launch in some circumstances. This was found to cause crashes in certain apps, so these apps will maintain 10.6 behavior of more often opening Untitled windows. When these apps are recompiled on 10.7, they will acquire the 10.7 behavior. For maximum compatibility, do not depend on being asked to create an Untitled window at launch.Rounded Popup Button Widths
In 10.7, the blue indicator region of rounded popup buttons has been removed, and so the button's content (such as its title) is allowed to consume more space. Be aware when designing on 10.7 that a rounded popup button that does not clip on 10.7 may clip when run on earlier versions.Modernized Document Model
Mac OS 10.7 introduces a modernized document model in which:• The user does not have to manually manage multiple copies of their document files to be able to retrieve old versions that they deemed interesting at some point in the past.
• The user is not bothered by alerts asking what to do with unsaved document changes when closing documents or terminating applications. (See other sections of this release note for changes to when applications are terminated.)
• The user does not have to know and take care to save document changes before causing the document’s file to be read by another application. For example, if the user drags a document’s icon from the Finder and drops it in a Mail message editing window to make an attachment then the attachment will always include all of the changes the user had made to the document up to that point. In other words, from the user's point of view a document as presented in an application window is simply the same thing as the document file presented by the Finder, and vice versa.
The next several sections describe changes to NSDocument that you have to know about to make your application conform to the modernized document model.
NSDocument Autosaving in Place
Mac OS 10.4 introduced to NSDocument the “autosaving” of documents to protect against the user losing work due to application crashes, kernel panics, and power failures. You can easily create an NSDocument-based application that periodically saves the current contents of a document to a file next to the actual document file in the file system and having a name like “My Great Document (Autosaved).” Autosaved content files for documents that have never been saved and so don’t yet have document files go in ~/Library/Autosave Information. NSDocument takes care of triggering periodic autosaving, choosing where autosaved document contents file go, invoking your NSDocument subclass’ code for document file writing, cleaning up autosaved document contents files when they are redundant, and reopening autosaved documents when the application is relaunched after an apparent malfunction.Autosaving still serves the purpose of protecting against malfunction but is now also an important part of NSDocument’s implementation of Mac OS 10.7’s modernized document model. A new kind of autosaving, autosaving in place, has been added. Autosaving in place is different from the old kind of autosaving in that it overwrites the actual document file instead of writing a separate document contents file next to it in the file system. The old kind of autosaving, now called autosaving elsewhere, is still supported for backward binary compatibility and also for when the document has never been saved, so there is no document file, and autosaving requires writing to some other file. You enable autosaving in place, and a wide variety of new behaviors that follow from it, for NSDocument subclasses in your application by overriding a new class method and returning YES from your override:
+ (BOOL)autosavesInPlace;So that your NSDocument subclass' overrides of -save… and write… methods can distinguish between the two kinds of autosaving the NSAutosaveOperation enumerator that was published in Mac OS 10.4 has been renamed and a new one has been added:
NSAutosaveOperation = 3,
NSAutosaveElsewhereOperation = 3,
NSAutosaveInPlaceOperation = 4In Mac OS 10.4 through Mac OS 10.6 “autosaving” was a misnomer because it was not synonymous with “automatic saving,” which would be the writing of a document’s current documents to the document’s file and the updating of NSDocument attributes that has to be done when that happens. In Mac OS 10.7 NSDocument's new autosaving in place really is automatic saving.
Autosaving in place can sometimes result in the user changing document files that they did not really mean to change. To protect against that, NSDocument does autosaving safety checking when the user changes the document. Examples are checking for documents that are old (does the user really want to edit last year's tax return?) and checking for documents that are in folders where the user typically does not edit documents (does the user really want to edit something they just downloaded?). You can override a new method to customize this checking:
- (BOOL)checkAutosavingSafetyAndReturnError:(NSError **)outError;See the comments in <AppKit/NSDocument.h> for details.
NSDocument Autosaving Changes
In Mac OS 10.7 the manner in which NSDocument triggers periodic autosaving for crash protection is more complicated than simply setting a timer when the user changes the document. There is no way to customize this behavior other than using the existing -setAutosavingDelay: and -autosavingDelay methods in NSDocumentController but there is a new NSDocument method you can override to implement your own behavior:- (void)scheduleAutosaving;This method is invoked by -updateChangeCount: and -updateChangeCountWithToken:forSaveOperation:.
Turning on autosaving in place in your NSDocument-based application changes what NSDocument does when the user closes the document and there are unsaved changes. Instead of presenting an alert asking the user what to do with the changes, NSDocument simply autosaves the document before it is closed. That autosaving is not done by invoking the existing -autosaveDocumentWithDelegate:didAutosaveSelector:contextInfo: method. Instead it is done by invoking a new method:
- (void)autosaveWithImplicitCancellability:(BOOL)autosavingIsImplicitlyCancellableSee the comments in <AppKit/NSDocument.h> for details.
completionHandler:(void (^)(NSError *errorOrNil))completionHandler;
Things to Watch For When Enabling NSDocument Autosaving in Place
Enabling autosaving in place in your application may require other changes to your NSDocument subclass. Here are some things to watch for:• Turning on asynchronous saving
Because autosaving happens periodically, at unpredictable times while the user is editing the document, user interface delays during saving are less acceptable than they were. When you turn on autosaving in place you should strongly consider also turning on asynchronous saving. See the next section.
• Invoking and overriding deprecated methods
Over the years many NSDocument methods have been deprecated. Notably, in Mac OS 10.4 new methods that take NSURLs and return NSErrors replaced methods that do not. You should update your application to stop invoking and overriding the replaced methods. They are listed at the bottom of <AppKit/NSDocument.h>.
• Overriding -revertToContentsOfURL:ofType:error:
When you enable autosaving in place you also enable version preserving. (Unless you also override +preservesVersions to turn version preserving off.) With autosaving in place enabled it's more important than ever to invoke super when you override -revertToContentsOfURL:ofType:error:, because it's NSDocument's default implementation of that method that updates the document's state to reflect what happens during reverting to an old version. If you don't, NSDocument might present the user with alerts about the document having been changed by another application when that is not the case.
Your override of -revertToContentsOfURL:ofType:error: may now be passed a URL that is not the same as the document's current file URL, and a file type that is not the same as the document's current file type.
• Overriding the various -write… methods
During duplicating and reverting NSDocument may cause the current version of the document, the one the user is looking at at that moment, to be preserved. Doing this sometimes requires that the document's current contents be written to disk. NSDocument does not invoke any of the -save… methods in that case. Instead, it invokes -writeSafelyToURL:ofType:forSaveOperation:error:, passing it a URL to a file that will become the preserved version, the file type that [self autosavingFileType] returns, and NSAutosaveElsewhereOperation. Your NSDocument subclass must write the entire contents of the document to that file. You will probably not notice this if you are not overriding -writeSafelyToURL:ofType:forSaveOperation:error:, you are correctly overriding one of the simple writing methods, which are -writeToURL:ofType:error:, -fileWrapperOfType:error:, and -dataOfType:error:, and if -autosavingFileType always returns an appropriate value.
Simple duplicating may cause the same kind of writing to be done. In that case the file that is written will be used as the already-autosaved contents of the new document.
• Overriding -keepBackupFile
You should not override -keepBackupFile in such a way that it returns YES when +autosavesInPlace returns YES.
• Overriding -autosaveDocumentWithDelegate:didAutosaveSelector:contextInfo:
When autosaving in place is enabled not all autosaving is done by an invocation of -autosaveDocumentWithDelegate:didAutosaveSelector:contextInfo:, so it is not a good method to override to customize all kinds of autosaving. Consider overriding -saveToURL:ofType:forSaveOperation:completionHandler: and checking the passed-in save operation type instead.
• Overriding -validateUserInterfaceItem: and -validateMenuItem:
Enabling autosaving in place adds a popup menu to the title bar of document windows. The popup menu has actions that must be validated by the NSDocument instance. If you override -validateUserInterfaceItem: or -validateMenuItem: you should make sure your override invokes super when the action is not one that your subclass recognizes.
• Updating of the File menu
In applications that have enabled autosaving in place, the Save As… and Save All items in the File menu are hidden, and a Duplicate menu item is added.
• The meaning of NSDocumentController's -setAutosavingDelay: and -autosavingDelay methods
In applications that have enabled autosaving in place, as opposed to the old style of autosaving that was introduced in Mac OS 10.4, passing a value of -[NSDocumentController setAutosavingDelay:] to 0.0 does not turn off autosaving.
NSDocument Asynchronous Saving
Mac OS 10.7 introduces to NSDocument the notion of asynchronous saving, in which the writing to a file being saved is done on a background thread so the application's user interface remains responsive even if that writing is slow. Saving is now done by a new method that does not have to signal success or failure before it returns:- (void)saveToURL:(NSURL *)url-saveToURL:ofType:forSaveOperation:error:, the old, synchronous, variant of this method, is deprecated. If your application is overriding that method to customize saving you should override this new method instead. If your application must continue to run on versions of OS X older than 10.7 you can leave your override of the old method there. If you do that then NSDocument will invoke the old method on Mac OS 10.6 and older and the new one on Mac OS 10.7 and newer.
ofType:(NSString *)typeName
forSaveOperation:(NSSaveOperationType)saveOperation
completionHandler:(void (^)(NSError *errorOrNil))completionHandler;
Whether or not saving is asynchronous is controlled by a new method that you can override:
- (BOOL)canAsynchronouslyWriteToURL:(NSURL *)urlThis method is invoked during saving. If your override of it returns YES then NSDocument will create a separate writing thread and invoke -writeSafelyToURL:ofType:forSaveOperation:error: on it, but the main thread will remain blocked until something on the writing thread invokes another new method:
ofType:(NSString *)typeName
forSaveOperation:(NSSaveOperationType)saveOperation;
- (void)unblockUserInteraction;When this method is invoked the main thread will become unblocked, the application will resume dequeueing user interface events, and the user will be able to continue viewing and editing their document, even if the actual writing of the file takes some time. The right moment to invoke this method is when an immutable "snapshot" of the document's contents has been taken so that the writing of the snapshot's contents can continue safely on the writing thread even when the user is editing the document on the main thread. In many applications taking that snapshot will be as simple as creating an NSFileWrapper, so the default implementation of -writeToURL:ofType:error: invokes - unblockUserInteraction after it has invoked -fileWrapperOfType:error: but before writing that NSFileWrapper to a file. If your NSDocument subclass simply overrides -fileWrapperOfType:error: or -dataOfType:error: to implement writing and your override never takes too long to return then you don't have to worry about invoking -unblockUserInteraction.
There is an error in the comment for this method in <AppKit/NSDocument.h>. The default implementation of -fileWrapperOfType:error: does not invoke -unblockUserInteraction. The default implementation of -writeToURL:ofType:error:, on the other hand, does.
In some applications taking a snapshot quickly enough won't be possible all of the time regardless of whether it's an NSFileWrapper or something else. One way to unblock user interaction quickly in that case is to not take a snapshot at all and instead trigger copy-on-write behavior ("write" in this case refers to the mutation of the document's contents in memory, not the writing to a file). In a copy-on-write model objects that are to be mutated to reflect the user's edits are first copied if writing to a file is happening right then so that the writing thread can continue its work without risk of writing a file whose contents are internally inconsistent. While writing continues the values that are displayed in the user interface must be gotten from the copies if there are any, and when the writing is done the copies must be folded back into the document model's regular object graph.
In a substantial application multithreaded copy-on-write is difficult to implement. In an effort to strike a more realistic balance between responsiveness and ease of implementation NSDocument's asynchronous saving machinery also supports a "stop-copying-on-write" model, in which your NSDocument subclass can attempt to take the snapshot mentioned above but often cancel that snapshotting, and actually the entire saving operation, if it takes so long that the user's continued editing of the document requires the mutation of the objects being snapshotted. Implementing this correctly requires implementing a thread-safe way to cancel snapshotting and also a way to sense when the snapshotting must be cancelled. Hopefully this will still be easier for application developers than copy-on-write. To find out when that sort of snapshotting cancellation is appropriate for a particular save operation you can invoke a new method:
- (BOOL)autosavingIsImplicitlyCancellable;This method returns YES, for example, when periodic autosaving is being done for crash protection and therefore nothing bad would happen if that autosaving were to be cancelled by your stop-copying-on-write mechanism. It returns NO when autosaving should be allowed to continue even it is going to take a noticeable amount of time, when the document is being closed for example.
When saving is asynchronous and the user is allowed to edit a document while it is being saved NSDocument's old technique of updating the changedness of the document after successful saving by invoking -updateChangeCount and passing it NSChangeCleared or NSChangeAutosaved is not sufficient. Information must be recorded about the state of the document at the beginning of the save operation and used at the end. NSDocument now does that, using two new methods:
- (id)changeCountTokenForSaveOperation:(NSSaveOperationType)saveOperation;See the comments in <AppKit/NSDocument.h> for details about all of the methods published in this section.
- (void)updateChangeCountWithToken:(id)changeCount
forSaveOperation:(NSSaveOperationType)saveOperation;
NSDocument UI and File Access Serialization
With asynchronous saving it is possible for the user to instigate a user interface action that might present modal UI, a sheet for example, when asynchronous saving is about to fail and present an error alert sheet of its own, which would not work. This problem is prevented by the use of a set of new methods:- (void)performActivityWithSynchronousWaiting:(BOOL)waitSynchronouslyAs a general rule you should use these methods, if your NSDocument subclass supports asynchronous saving, around the performance of user interface actions that might present modal UI, like sheets, including error alerts.
usingBlock:(void (^)(void (^activityCompletionHandler)(void)))block
- (void)continueActivityUsingBlock:(void (^)(void))block;
For any instance of NSDocument there are values in memory that only make sense in the context of the document file's current state. Problems arise when one thread gets or sets these values while a different thread is changing the file's current state. These problems are prevented by the use of a different set of new methods:
- (void)performSynchronousFileAccessUsingBlock:(void (^)(void))block;You should use these methods, if your NSDocument subclass supports asynchronous saving, in code that makes decisions based on values that are changed by the act of saving a document, and in code that changes those values.
- (void)performAsynchronousFileAccessUsingBlock:(void (^)(void (^completionHandler)(void)))block;
-performActivityWithSynchronousWaiting:usingBlock: and -performSynchronousFileAccessUsingBlock: can block the main thread. Sometimes that blocking must be interrupted. You can do that, using another new method:
- (void)continueAsynchronousWorkOnMainThreadUsingBlock:(void (^)(void))block;See the comments in <AppKit/NSDocument.h> for details about all of the methods published in this section.
NSDocument Duplicating
Part of Mac OS 10.7's new document model is a Duplicate item in the File menu that replaces the existing Save As… item, so turning on autosaving in place in any NSDocument subclass in your application does two more things:• Makes NSDocumentController add a Duplicate menu item to your application's File menu.
• Makes instances of that NSDocument subclass in the responder chain hide the Save As… item in the File menu.
The handling of the user's choosing the Duplicate item is done by three new NSDocument methods:
- (IBAction)duplicateDocument:(id)sender;-duplicateAndReturnError: invokes a new NSDocumentController method to actually create the new document:
- (void)duplicateDocumentWithDelegate:(id)delegate
didDuplicateSelector:(SEL)didDuplicateSelector
contextInfo:(void *)contextInfo;
- (NSDocument *)duplicateAndReturnError:(NSError **)outError;
- (NSDocument *)duplicateDocumentWithContentsOfURL:(NSURL *)urlBoth of these methods work reliably only when only sent to instances of NSDocument in which autosaving in place is enabled.
copying:(BOOL)duplicateByCopying
displayName:(NSString *)displayNameOrNil
error:(NSError **)outError;
When NSDocumentController creates a new duplicate document it sets its name by invoking another new NSDocument method:
- (void)setDisplayName:(NSString *)displayNameOrNil;If your NSDocument subclass previously implemented this method, you may need to accommodate for NSDocument's implementation and use of this method when you eventually link on Mac OS 10.7. See the comments in <AppKit/NSDocument.h> and <AppKit/NSDocumentController.h> for details.
NSDocument Error Handling Change
The default implementations of some NSDocument methods return recoverable NSErrors. Recovery from some kinds of errors uses resources, like temporary files, that cannot be cleaned up before the NSError is returned. If such an NSError is presented to the user the resource will be cleaned up automatically, regardless of what recovery option the user chooses, but in some rare cases it is not appropriate to present an NSError to the user. In those cases you can trigger the necessary resource cleanup by invoking a new method:- (void)willNotPresentError:(NSError *)error;See the comment in <AppKit/NSDocument.h> for details.
File Type Fixing in -[NSDocument saveToURL:ofType:forSaveOperation:completionHandler:]
The new -saveToURL:ofType:forSaveOperation:completionHandler: method has a feature that the old -saveToURL:ofType:forSaveOperation:error: method does not. When the passed-in URL has a file name extension that is not valid for the passed-in type, -saveToURL:ofType:forSaveOperation:completionHandler: internally creates a new URL whose file name extension is valid, and uses that instead for the save operation. It passes that "fixed" URL when it invokes -writeSafelyToURL:ofType:forSaveOperation:error:. If the writing succeeds, it deletes the file located by the original URL. It uses -[NSFileCoordinator itemAtURL:didMoveToURL:] properly when it does this.This is useful in applications whose documents' types can be changed by user action. For example, in Mac OS 10.7, TextEdit takes advantage of this to more easily convert .rtf files to .rtfd when the user adds attachments to documents.
-saveToURL:ofType:forSaveOperation:error: does not do this. Also, -saveToURL:ofType:forSaveOperation:completionHandler: does not do this when it's invoking -saveToURL:ofType:forSaveOperation:error: for backward binary compatibility.
NSDocumentController Asynchronous Opening and Reopening
Mac OS 10.6 introduced to NSDocumentController the notion of concurrent document opening but no corresponding method was published. In Mac OS 10.7 opening is now done by a new method that does not have to signal success or failure before it returns:- (void)openDocumentWithContentsOfURL:(NSURL *)urlReopening, which is what happens during application launching when AppKit's new Restorable State machinery restores a document that was open when the application was last terminated, is now also done by a new method that does not have to signal success or failure before it returns:
display:(BOOL)displayDocument
completionHandler:(void (^)(NSDocument *document, BOOL documentWasAlreadyOpen, NSError *error))completionHandler;
- (void)reopenDocumentForURL:(NSURL *)urlOrNil-openDocumentWithContentsOfURL:display:error: and -reopenDocumentForURL:withContentsOfURL:error:, the old, synchronous, variants of these methods, are deprecated. If your application is overriding one of those methods to customize open or reopening you should override the new method instead. If your application must continue to run on versions of OS X older than 10.7 you can leave your override of the old method there. If you do that then NSDocument will invoke the old method on Mac OS 10.6 and older and the new one on Mac OS 10.7 and newer.
withContentsOfURL:(NSURL *)contentsURL
display:(BOOL)displayDocument
completionHandler:(void (^)(NSDocument *document, BOOL documentWasAlreadyOpen, NSError *error))completionHandler;
See the comments in <AppKit/NSDocumentController.h> for details.
Move of NSFileWrapper from the AppKit Framework to the Foundation Framework
In Mac OS 10.7 the NSFileWrapper class has been moved from the AppKit framework to the Foundation framework. An NSFileWrapper(NSExtensions) category, declared in <AppKit/NSFileWrapperExtensions.h> remains behind. It includes the -setIcon: and -icon methods. The NSFileWrapper class, including all other methods, is now declared in <Foundation/NSFileWrapper.h>.NSResponder
- (id)supplementalTargetForAction:(SEL)action sender:(id)sender;This method is used in the process of finding a target for an action method for both nil target action routing and user interface validation (menus, toolbar items). It allows you to add non NSResponder objects to the action responder chain. Some example uses of this API are: allowing a control's delegate or a plugin to respond to actions.
If this NSResponder instance does not itself respondsToSelector:action, then supplementalTargetForAction:sender: is called. This method should return an object which responds to the action; if this responder does not have a supplemental object that does that, the implementation of this method should call super's supplementalTargetForAction:sender:. NSResponder's implementation returns nil. A basic implementation example:
- (id)supplementalTargetForAction:(SEL)action sender:(id)sender {
return [_foo respondsToSelector:action] ? _foo : [super supplementalTargetForAction:action sender:sender];
}
Returning an object that does not respond to the action is an error. Generally, this method is called by NSApplication and NSApplication raises an exception in this case.NSPrintOperation
If the print sheet is unresponsive or sluggish due to the time is takes you to fully render a page, you can check this method in drawRect and other printing methods such as beginDocument and knowsPageRage: to determine if the print operation prefers speed over fidelity. Most applications render each page fast enough and do not need to call this method. Only use this API after establishing that best quality rendering does indeed make the user interface unresponsive.enum {
// Render at the best quality you can regardless of how slow that may be
NSPrintRenderingQualityBest,
// Sacrifice the least possible amount of rendering quality for speed
// to maintain a responsive user interface
NSPrintRenderingQualityResponsive
};
typedef NSInteger NSPrintRenderingQuality;
@implementation NSPrintOperation ...This is an example use of this API:
- (NSPrintRenderingQuality)preferredRenderingQuality;
- (void)drawRect:(NSRect)rect {
NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
if (![currentContext isDrawingToScreen]) {
NSPrintOperation *printOperation = [NSPrintOperation currentOperation]
if ([printOperation preferredRenderingQuality] == NSPrintRenderingQualityResponsive) {
// Render with the best possible quality such that the user interface remains responsive
} else {
// Printing, do a full render
}
}
}
ScrollWheel Events
There are new NSEvent methods to access the scroll wheel event delta that allow for more precise delta values from Magic Mice and trackpads. -deltaX and -deltaY should no longer be used when the event is type NSScrollWheel.A generic scroll wheel issues rather coarse (line) scroll deltas. That is, the amount that you actually scroll is the line height multiplied by the scroll delta. Some Apple mice and trackpads provide much more precise delta. Instead of multiplying by the line height, you use the scroll delta value as is. The new -scrollingDeltaX/Y methods always return the most precise delta provided by the input device. You can determine precision of the -scrollingDelta with the new -hasPreciseScrollingDeltas method. When -hasPreciseScrollingDeltas returns NO, multiply the returned value by line or row height. When -hasPreciseScrollingDeltas returns YES, scroll by the returned value (in points).
- (BOOL)hasPreciseScrollingDeltas;Touch capable Apple mice and trackpads know when the user physically starts and stops scrolling. AppKit publishes this state using the new method below.
- (CGFloat)scrollingDeltaX;
- (CGFloat)scrollingDeltaY;
enum {
NSEventPhaseNone = 0, // event not associated with a phase.
NSEventPhaseBegan = 0x1 << 0,
NSEventPhaseStationary = 0x1 << 1,
NSEventPhaseChanged = 0x1 << 2,
NSEventPhaseEnded = 0x1 << 3,
NSEventPhaseCancelled = 0x1 << 4,
};
typedef NSUInteger NSEventPhase;
- (NSEventPhase)phase;Scroll wheel events issued from non-touch scrolling devices that cannot distinguish when the user starts and ends a scroll gesture have a phase of NSEventPhaseNone. For touch scrolling devices, the first scroll wheel event in the gesture sequence has a phase of NSEventPhaseBegan. Subsequent scroll in the sequence have a phase of NSEventPhaseChanged. When the user physically ends the gesture, the final scroll wheel event has a phase of NSEventPhaseEnded and a scrollingDelta of 0,0.
Some Apple mice and trackpads also issue momentum scroll wheel events. That is, the hardware continues to issue scroll wheel events even though the user is no longer physically scrolling. AppKit routes these scroll wheel events to the view the cursor was over when they started. For custom cells, this may not be enough. Using the new methods below, you can now determine when momentum scrolling starts, continues and stops and further route the events appropriately.
- (NSEventPhase)momentumPhase;For normal (non momentum) scroll wheel events, the momentumPhase is NSEventPhaseNone. When the hardware switches from user performed scrolls events to momentum scroll wheel events, the momentumPhase is set to NSEventPhaseBegan. Subsequent scroll wheel events from that device have a momentumPhase of NSEventPhaseChanged until the momentum decays to 0, or the user stops the momentum scrolling. The final momentum scroll wheel event has a momentumPhase of NSEventPhaseEnded. All momentum scroll wheel events have a phase of NSEventPhaseNone.
Scroll and Gesture Direction
There is a new user preference to "Move content in the direction of finger movement." To accomplish this, deltaX/Y and scrollingDeltaX/Y are automatically inverted for NSEventScrollWheel events according to the user's preferences. The direction of swipes match the direction of scrolling and, as such, swipe deltas are inverted. However, for some uses of NSEventScrollWheel and NSEventTypeSwipe events, the behavior should not respect the user preference. This method allows you to determine when the event has been inverted and compensate by multiplying -1 if needed.- (BOOL)isDirectionInvertedFromDevice;
Multi-image dragging
There are new classes and methods to handle dragging. Instead of dragging a single image, you now drag individual images for each item that is in the drag. You can even specify multiple images for each item. These images are then animated into various formations as the user drags. Additionally, the destination of a drop may participate in the drag presentation by setting the images for each item when the user is over the destination.NSDraggingSource and NSDraggingDestinations are now formal protocols. The following new classes have been introduced. NSDraggingSession, NSDraggingItem, and NSDraggingImageComponent. When a drag is started, an NSDraggingSession is created. The current image based dragging methods are deprecated in favor of new NSDraggingSession based methods (See NSView.h and NSDragging.h). The NSDraggingInfo protocol is expanded with new methods that allow modifications to the drag items. The new NSView method to begin a drag takes an array of NSDraggingItems (pasteboardItems, frames and image providers) and creates the appropriate pasteboard for the drag. The current drag methods on NSWindow and NSView are informally deprecated in favor of this new method.
A basic drag starts by calling -beginDraggingSessionWithItems:event:source: on a view. This new method returns right away and the actual drag occurs later. The caller can take the returned NSDraggingSession and continue to modify its properties such as -animatesToStartingPositionsOnCancelOrFail. When the drag actually starts, the source is sent a -draggingSession:willBeginAtPoint: message followed by multiple -draggingSession:movedToPoint: messages as the user drags. Once the drag is ended or cancelled, the source receives a -draggingSession:endedAtPoint:operation: and the drag is complete.
The destination of a drag works much the same way as before. The draggingEntered:, draggingUpdated:, etc... methods are called at the same times. The sender for these messages still conforms to <NSDraggingInfo>. NSDraggingInfo has some new methods that allow the destination to enumerate the dragging items changing their properties such as frame and image.
When the DragManager determines it is a good time for the drag to change (either formation change and / or image change), the destination is sent an -updateDraggingItemsForDrag: message, if implemented. Waiting for a call to -updateDraggingItemsForDrag: is the preferred way to change the drag image at the right time.
Fluid Swipe Tracking - API
The following API allows for tracking gesture scroll wheel events as a fluid swipe gesture. Similar to iOS, NSScrollView will bounce once if needed, then optionally pass on the gesture scroll wheel events so your controller can use this API to track the scroll gesture as a fluid swipe gesture.ScrollWheel NSEvents now respond to the -phase message. There are 3 types of scrolls: 1) Gesture scrolls - these begin with NSEventPhaseBegan, have a number of NSEventPhaseChanged events and terminate when the user lifts their fingers with an NSEventPhaseEnded. 2) Momentum scrolls - these have a phase of NSEventPhase none, but they have a momentumPhase of NSEventPhaseBegan/NSEventPhaseChanged/NSEventPhaseEnded. 3) Legacy scrolls - these scroll wheel events have a phase of NSEventPhaseNone and a momentumPhase of NSEventPhaseNone. There is no way to determine when the user starts, nor stops, performing legacy scrolls.
NSScrollView processes all gesture scroll wheel events and does not pass them up the responder chain. Often, tracking a swipe is done higher in the responder chain, for example at the WindowController level. To achieve an iOS style "bounce when not at the edge, otherwise swipe" behavior, you need to inform NSScrollView that it should forward scroll wheel messages when appropriate. Instead of manually setting a property on NSScrollView, your NSResponder can implement the following method and return YES.
- (BOOL)wantsScrollEventsForSwipeTrackingOnAxis:(NSEventGestureAxis)axis;When the appropriate controller receives a -scrollWheel: message with a non NSEventNone phase, you can call the following message on that event instance to track the swipe or scroll to both user completion of the event, and animation completion.
enum {
NSEventSwipeTrackingLockDirection = 0x1 << 0,
NSEventSwipeTrackingClampGestureAmount = 0x1 << 1
};
typedef NSUInteger NSEventSwipeTrackingOptions;
@interface NSEvent ...
- (void)trackSwipeEventWithOptions:(NSEventSwipeTrackingOptions)optionsBelow is a pseudo code example of swiping a collection of pictures like the iOS Photo app.
dampenAmountThresholdMin:(CGFloat)minDampenThreshold
max:(CGFloat)maxDampenThreshold
usingHandler:(void (^)(CGFloat gestureAmount, NSEventPhase phase, BOOL isComplete, BOOL *stop))handler;
...
- (BOOL)wantsScrollEventsForSwipeTrackingOnAxis:(NSEventGestureAxis)axis {
return (axis == NSEventGestureAxisHorizontal) ? YES : NO;
}
- (void)scrollWheel:(NSEvent *)event {
// NSScrollView is instructed to only forward horizontal scroll gesture events (see code above). However, depending
// on where your controller is in the responder chain, it may receive other scrollWheel events that we don't want
// to track as a fluid swipe because the event wasn't routed though an NSScrollView first.
if ([event phase] == NSEventPhaseNone) return; // Not a gesture scroll event.
if (fabsf([event scrollingDeltaX]) <= fabsf([event scrollingDeltaY])) return; // Not horizontal
// If the user has disabled tracking scrolls as fluid swipes in system preferences, we should respect that.
// NSScrollView will do this check for us, however, depending on where your controller is in the responder chain,
// it may scrollWheel events that are not filtered by an NSScrollView.
if (![NSEvent isSwipeTrackingFromScrollEventsEnabled]) return;
if (_swipeAnimationCanceled && *_swipeAnimationCanceled == NO) {
// A swipe animation is still in gestureAmount. Just kill it.
*_swipeAnimationCanceled = YES;
_swipeAnimationCanceled = NULL;
}
CGFloat numPhotosToLeft = // calc num of photos we can move to the left and negate
CGFloat numPhotosToRight = // calc num of photos we can move to the right
__block BOOL animationCancelled = NO;
[event trackSwipeEventWithOptions:0 dampenAmountThresholdMin:numPhotosToLeft max:numPhotosToRight
usingHandler:^(CGFloat gestureAmount, NSEventPhase phase, BOOL isComplete, BOOL *stop) {
if (animationCancelled) {
*stop = YES;
// Tear down animation overlay
return;
}
if (phase == NSEventPhaseBegan) {
// Setup animation overlay layers
}
// Update animation overlay to match gestureAmount
if (phase == NSEventPhaseEnded) {
// The user has completed the gesture successfully.
// This handler will continue to get called with updated gestureAmount
// to animate to completion, but just in case we need
// to cancel the animation (due to user swiping again) setup the
// controller / view to point to the new content / index / whatever
} else if (phase == NSEventPhaseCancelled) {
// The user has completed the gesture un-successfully.
// This handler will continue to get called with updated gestureAmount
// But we don't need to change the underlying controller / view settings.
}
if (isComplete) {
// Animation has completed and gestureAmount is either -1, 0, or 1.
// This handler block will be released upon return from this iteration.
// Note: we already updated our view to use the new (or same) content
// above. So no need to do that here. Just...
// Tear down animation overlay here
self->_swipeAnimationCanceled = NULL;
}
}];
// We keep a pointer to a BOOL __block variable so that we can cancel our
// block handler at any time. Note: You must assign the BOOL pointer to your
// ivar after block creation and copy!
self->_swipeAnimationCanceled = &animationCancelled;
}
Full Screen
In Lion, we have added support for a fullscreen experience that allows users to run windows in fullscreen mode, where a new space is dynamically created for each window in fullscreen mode. This means for example that you can have a Safari window on the desktop, and another Safari window in a fullscreen space.NSWindow Full Screen API
An application must specify if a window can enter fullscreen. Note that existing space-related window collection behaviors continue to have meaning in fullscreen spaces as well. For example, a window with NSWindowCollectionBehaviorTransient will be automatically shown on the fullscreen space if it was visible on the desktop space when you entered fullscreen mode. A window with NSWindowCollectionBehaviorMoveToActiveSpace will be shown in the fullscreen space if the user performs an action to show it, even if it was previously shown on the desktop space. A window with NSWindowCollectionBehaviorManaged stays attached to the space on which it was first shown, as long as it is not closed and reopened.Windows belonging to other applications will not be allowed on a fullscreen space, except as follows: Windows belonging to a UIElement application are allowed on a fullscreen space. Non-activating panels owned by other applications are also allowed on a fullscreen space, but only if their window collection behavior includes NSWindowCollectionBehaviorFullScreenAuxiliary. Depending on the nature of your window, you may want to include NSWindowCollectionBehaviorCanJoinAllSpaces in the window collection behavior in order to have qualifying windows appear on fullscreen spaces as well as desktop spaces.
enum {
// a window with this collection behavior will have a fullscreen button in the upper right of its titlebar
NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7,
// windows with this collection behavior can be shown on the same space as the fullscreen window
NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8
};
A window can be taken into or out of fullscreen using -toggleFullScreen:. If an application supports fullscreen, it should add an "Enter Full Screen" menu item to the View menu. The menu item is now available through Xcode 4. You can also add the item programmatically, with toggleFullScreen: as the action, nil as the target, and cmd-ctrl-f as the key equivalent. AppKit will automatically update the menu item title as part of its menu item validation.
- (void)toggleFullScreen:(id)sender;The fullscreen window button is exposed as a standard window button.
enum {
NSWindowCloseButton,
NSWindowMiniaturizeButton,
NSWindowZoomButton,
NSWindowToolbarButton,
NSWindowDocumentIconButton,
NSWindowFullScreenButton
};
typedef NSUInteger NSWindowButton;
A fullscreen window does not draw its titlebar, and may have special handling for its toolbar. A styleMask is set to indicate that a window has fullscreen appearance.enum {
NSFullScreenWindowMask = 1 << 14
};
Notifications are sent for fullscreen changes:NSString *NSWindowWillEnterFullScreenNotification;The window also invokes new delegate methods as appropriate.
NSString *NSWindowDidEnterFullScreenNotification;
NSString *NSWindowWillExitFullScreenNotification;
NSString *NSWindowDidExitFullScreenNotification;
NSWindow Delegate Full Screen API
NSWindow invokes the following delegate methods as a window enters and exits fullscreen mode; these give you an opportunity to reconfigure the window layout and perform other operations if necessary.- (void)windowWillEnterFullScreen:(NSNotification *)notification;The default frame of a fullscreen window fills the entire screen (including menuBar and dock areas). The proposedSize will be a calculated contentSize, leaving space for the toolbar if present. The window delegate can implement this method to provide a different content size for the window. The window positioning will be implementation defined (probably centered) if it does not fill the entire screen.
- (void)windowDidEnterFullScreen:(NSNotification *)notification;
- (void)windowWillExitFullScreen:(NSNotification *)notification;
- (void)windowDidExitFullScreen:(NSNotification *)notification;
- (NSSize)window:(NSWindow *)window willUseFullScreenContentSize:(NSSize)proposedSize;The default presentationMode for fullscreen (NSApplicationPresentationFullScreen| NSApplicationPresentationAutoHideDock|NSApplicationPresentationAutoHideMenuBar). A delegate can provide a different set of options to take effect in the space containing this fullscreen window.
- (NSApplicationPresentationOptions)window:(NSWindow *)windowThe default animation between a window and its fullscreen representation is a crossfade. With knowledge of the layout of a window before and after it enters fullscreen, an application can do a much better job on the animation. The following API allows a window delegate to customize the animation by providing a custom window or windows containing layers or other effects. In order to manage windows on spaces, we need the window delegate to provide a list of windows involved in the animation, in desired z-order, and including the receiver itself if the receiver should stay visible on the fullscreen space. If an application does not do a custom animation, this method can be unimplemented or can return nil. window:startCustomAnimationToEnterFullScreenWithDuration: will be called only if customWindowsToEnterFullScreen: returns non-nil.
willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions;
- (NSArray *)customWindowsToEnterFullScreen:(NSWindow *)window;
The system then starts its animation into fullscreen, including transitioning to a new space. The method below is called to start the window fullscreen animation immediately, and perform the animation with the given duration to be in sync with the system animation. This method is called only if customWindowsToEnterFullScreen: returned non-nil.
- (void)window:(NSWindow *)window startCustomAnimationToEnterFullScreenWithDuration:(NSTimeInterval)duration;In some cases, the transition to enter fullscreen will fail, due to being in the midst of handling some other animation or user gesture. We will attempt to minimize these cases, but believe there is a need for failure handling. This method indicates that there was an error, and the application should clean up any work it may have done to prepare to enter fullscreen. This message will be sent whether or not the delegate indicated a custom animation by returning non-nil from customWindowsToEnterFullScreen:. Note though that the window will be taken out of the fullscreen space and returned to the desktop even in the failure case, so it is especially important to return any custom animation to the non-fullscreen state in case of error. This is not the case when a failure is encountered while entering fullscreen. In the case of failure to enter, the window is left on the desktop space.
- (void)windowDidFailToEnterFullScreen:(NSWindow *)window;The following API allows a window delegate to customize the animation when the window is about to exit fullscreen. In order to manage windows on spaces, we need the window delegate to provide a list of windows involved in the animation, in desired z-order and including the receiver if the receiver should be visible during the animation. If an application does not do a custom animation, this method can be unimplemented or can return nil. window:startCustomAnimationToExitFullScreenWithDuration: will be called only if customWindowsToExitFullScreen: returns non-nil.
- (NSArray *)customWindowsToExitFullScreen:(NSWindow *)window;
The system then starts its animation out of fullscreen, including transitioning back to the desktop space. The method below is called to start the window animation immediately, and perform the animation with the given duration to be in sync with the system animation. This method is called only if customWindowsToExitFullScreen: returned non-nil.
- (void)window:(NSWindow *)window startCustomAnimationToExitFullScreenWithDuration:(NSTimeInterval)duration;In some cases, the transition to exit fullscreen will fail, due to being in the midst of handling some other animation or user gesture. We will attempt to minimize these cases, but believe there is a need for failure handling. This method indicates that there was an error, and the application should clean up any work it may have done to prepare to exit fullscreen. This message will be sent whether or not the delegate indicated a custom animation by returning non-nil from customWindowsToExitFullScreen:.
- (void)windowDidFailToExitFullScreen:(NSWindow *)window;
NSApplication Full Screen API
There is a fullscreen presentation option, NSApplicationPresentationFullScreen. This presentation option is set when in a fullscreen space.A window delegate may also specify that the window toolbar be removed from the window in fullscreen and auto-shown with the menubar by including NSApplicationPresentationAutoHideToolbar in the presentationOptions returned from -window:willUseFullScreenPresentationOptions:.
enum {
NSApplicationPresentationFullScreen = (1 << 10),
NSApplicationPresentationAutoHideToolbar = (1 << 11),
};
typedef NSUInteger NSApplicationPresentationOptions;
Multiple Monitors and Full Screen
We have made some simplifying assumptions for Lion Full Screen behavior on multiple monitors. Multiple monitors are treated as a single unit by Spaces, and therefore are also treated as a single unit in Full Screen. This means that all monitors will be dedicated to windows belonging to the full screen application, and there can be only one primary full screen window visible at a time. A secondary monitor is useful for inspector windows.Secondly, because the menu bar is located on the main monitor, the primary full screen window will be located on the main monitor as well. This allows the menu bar, floating toolbar, and full screen window to maintain their interrelationship on the same monitor.
Find Bar Support with NSTextFinder
Mac OS 10.7 includes a new class called NSTextFinder that provides a standard Safari-like find bar interface. NSTextFinder also provides incremental search functionality.NSTextFinder serves as a controller class for the standard Cocoa find bar. NSTextFinder interacts heavily with its 'client', which is an object that provides access to the content being searched and provides visual feedback for a search operation by conforming to the NSTextFinderClient protocol, which will be explained later.
All menu items related to finding (Find…, Find Next, Find Previous, Use Selection for Find, etc.) will have the same action, performTextFinderAction:, which gets sent down the responder chain in the usual way. (Note: Before Mac OS 10.7, the default action for these menu items was performFindPanelAction: which you should update to this new action.) A responder of performTextFinderAction: is responsible for creating and owning an instance of NSTextFinder. Before any other messages are sent to the NSTextFinder, you should set its 'client' property to an object which implements the NSTextFinderClient protocol.
Each user interface item used for finding has a different tag, which corresponds to the appropriate value in the NSTextFinderAction enum (see below). Upon receipt of the performTextFinderAction: message, the responder should call the following on its NSTextFinder instance:
- (void)performAction:(NSTextFinderAction)action;This method will determine the desired action from the tag and make various callbacks to its client to perform that action. These callbacks are defined in the NSTextFinderClient protocol.
Validation occurs by a similar pattern. The responder should implement validateUserInterfaceItem: and, when the item's action is performTextFinderAction:, it should pass the item's tag to the following method and return the result:
- (BOOL)validateAction:(NSTextFinderAction)action;This method will also make various callbacks to its clients to determine what the appropriate response should be. These callbacks are also defined in the NSTextFinderClient protocol.
The NSTextFinderAction enum used in the above methods is listed below. The values from this enum are duplicates from the old NSFindPanelAction enum, with the exception of the NSTextFinderActionHideFindInterface value. The NSFindPanelAction values will be mapped to NSTextFinderAction values. Their existing numeric values will be preserved by this mapping.
enum {
NSTextFinderActionShowFindInterface = 1,
NSTextFinderActionNextMatch = 2,
NSTextFinderActionPreviousMatch = 3,
NSTextFinderActionReplaceAll = 4,
NSTextFinderActionReplace = 5,
NSTextFinderActionReplaceAndFind = 6,
NSTextFinderActionSetSearchString = 7,
NSTextFinderActionReplaceAllInSelection = 8,
NSTextFinderActionSelectAll = 9,
NSTextFinderActionSelectAllInSelection = 10,
NSTextFinderActionHideFindInterface = 11,
NSTextFinderActionShowReplaceInterface = 12,
NSTextFinderActionHideReplaceInterface = 13
};
typedef NSInteger NSTextFinderAction;
When an action is performed, NSTextFinder will ask its client for the string it is supposed to search. There are two ways the client can provide this string. It can either implement the -string method, and simply return an NSString, or it can implement the following methods:
- (NSString *)stringAtIndex:(NSUInteger)characterIndex effectiveRange:(NSRangePointer)outRange endsWithSearchBoundary:(BOOL *)outFlag;These methods make it easier to use NSTextFinder with data that cannot be easily or efficiently flattened into a single NSString. In the first method, the client should return the string which contains the character at the given index in a conceptual concatenation of all strings that are to be searched. For example, if a client had the strings "The quick", " brown fox" , "jumped over the lazy", and "dog", and NSTextFinder requests the string at index 18, then the client should return " brown fox", because the 18th character of all the strings were concatenated together would be the 'x' in "fox". Additionally, the client should return, by reference through the outRange parameter, the effectiveRange of the string that is returned. In the above example, the range of " brown fox" is {9, 10}, because, in the imagined concatenation, the substring starts at index 9 and is 10 characters long.
- (NSUInteger)stringLength;
In some cases, it is not desirable for a match to be found which overlaps multiple strings returned by the above method. For example, suppose a client has a list of names like "John Doe", and "Jane Doe", etc. Normally, if the string is concatenated together like so: "John DoeJane Doe", then a search for "DoeJane" would succeed. To prevent this typically undesirable behavior from happening, the client should return YES, by reference through the outFlag parameter, when returning each individual string. This indicates that there is a boundary between the current string and the next string that should not be crossed when searching for a match.
One of the two approaches described above must be implemented by the client, or else the NSTextFinder will not be able to function. If both approaches (so all three methods) are implemented, stringAtIndex:effectiveRange:endsWithSearchBoundary: will be used by default.
Below are some properties reported by the client for use in NSTextFinder's -validateAction: method. If any of these properties is not implemented, a value of YES will be assumed. Returning NO from any of these methods will disable the actions that require them. For more information about which actions require these properties, consult the NSTextFinder.h header.
@property (getter=isSelectable, readonly) BOOL selectable;
@property (readonly) BOOL allowsMultipleSelection;
@property (getter=isEditable, readonly) BOOL editable;
When an action is sent via -performAction:, NSTextFinder may need additional information from the client to perform that action, or it may require the client to perform some parts of the action on its behalf. The following methods and properties provide the hooks NSTextFinder needs for each of the actions it supports. If the client does not implement one of these methods or properties, then the action that requires it will be disabled.
@property (readonly) NSRange firstSelectedRange;This property is required for the NextMatch, PreviousMatch, Replace, ReplaceAndFind, and SetSearchString actions. The client just needs to return its first selected range, or {index, 0} to indicate the location of the insertion point if there is no selection.
@property (retain) NSArray *selectedRanges;This property is required for the ReplaceAllInSelection, SelectAll, and SelectAllInSelection actions. The array should contain NSRanges wrapped in NSValues.
- (void)scrollRangeToVisible:(NSRange)range;This method is used by many actions, but is not strictly required by any. This method instructs the client to scroll its view to make the given range visible.
- (BOOL)shouldReplaceCharactersInRanges:(NSArray *)ranges withStrings:(NSArray *)strings;These methods are used by the ShowReplaceInterface, Replace, ReplaceAll(InSelection) and ReplaceAndFind actions. NSTextFinder does not have the ability to directly make changes to the client’s content, so the client is responsible for performing replace operations in these methods. Before a replace operation is performed, NSTextFinder calls the first method to determine if a replacement should take place. If it returns NO, then the given range will not be replaced. If the method returns YES, or is not implemented, then NSTextFinder will call the second method, instructing the client to carry out the replacement. Finally, the third method will be called, if implemented, to indicate that the replacement was completed. For ReplaceAll actions, these methods may be called multiple times, starting from the end of the string and moving toward the beginning, in order to preserve the indexes of the matches which precede the current one.
- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)string;
- (void)didReplaceCharacters;
In order to display the find bar, a "container" for the find bar must be specified. A "container" is an object that conforms to the NSTextFinderBarContainer protocol, described below. You can specify a find bar container using the following property on NSTextFinder:
@property (assign) IBOutlet id <NSTextFinderBarContainer> findBarContainer;The NSTextFinderBarContainer contains the following methods for displaying and updating the find bar:
@protocol NSTextFinderBarContainer <NSObject>
@required
@property (retain) NSView *findBarView;
@property (getter=isFindBarVisible) BOOL findBarVisible;
- (void)findBarViewDidChangeHeight;
[...]
@endWhen a new NSTextFinder is created and instructed to display the find bar, it will create an NSView for the find bar and assign that to the container via the findBarView property. From that point on, the container owns that view and should release it when the container is deallocated. The container should make the find bar visible when the findBarVisible property is set to YES. The container should implement the findBarViewDidChangeHeight method so it can reposition the find bar when its height changes, usually in response to user action.
Important: The container is free to modify the width of the find bar view, but it should never modify its height directly.
Find bar support in AppKit
In Mac OS 10.7, NSScrollView now conforms to NSTextFinderBarContainer in order to support the find bar for any document view searched by NSTextFinder. The find bar can be positioned either above or below the document view by assigning one of the values of the NSScrollViewFindBarPosition enum to the findBarPosition property.NSTextView also now supports the find bar. The find bar can be enabled or disabled on a text view with the following methods:
- (BOOL)usesFindBar;Only one of usesFindBar and usesFindPanel can be YES at a time. If one value is set to YES, the other will be set to NO.
- (void)setUsesFindBar:(BOOL)flag;
NSTextFinder Search Feedback
Since Mac OS 10.5, NSTextView has used an animated find indicator to give feedback to the user about a successful find action. The find indicator could be activated manually on an NSTextView via the -showFindIndicatorForRange: method.To provide this functionality for NSTextFinder clients in Mac OS 10.7, NSTextFinder will show the find indicator at the appropriate time automatically when performing a find. However, NSTextFinder needs to know where to show the indicator and it needs something to draw the text contents of the find indicator. During incremental search, NSTextFinder needs also to know the locations of all the visible matches so it can highlight them as well. The following additions to the NSTextFinderClient protocol provide these capabilities.
@protocol NSTextFinderClient
[...]
@optional
/* This method is used when displaying feedback about find operations to the user.
The client should return the view the contains the character at the given index.
It should also return by reference the entire range of the string displayed by
the view.
*/
- (NSView *)contentViewAtIndex:(NSUInteger)index effectiveCharacterRange:(NSRangePointer)outRange;
/* NSTextFinder uses this method to determine the location to display the find
indicator. The client should convert the given content range to an array of
rectangles in the contentView's coordinate system and return that array. The
given range is guaranteed not to overlap multiple views.
*/
- (NSArray *)rectsForCharacterRange:(NSRange)range;
@endThe first method is called to determine what view the content is displayed in, over which the find indicator will be displayed. Since content may potentially be spread over multiple views (like NSLayoutManager, which supports multiple NSTextViews), the method asks for the view at a given index, and the full range of the string that is contained by that view.
In the second method, the client should determine and return the rectangles in which the content with the given range is displayed in its contentView. The given range is guaranteed not to span multiple content views. The returned rectangles tell NSTextFinder where the matched range is found, so it can show the find indicator there.
NSTextFinder and NSView will handle the find indicator correctly when the contentView is resized, moved, or removed from the view hierarchy. If your content view's scrolling is done by an NSScrollView, the find indicator will also be handled for you during scrolling. Beyond these cases, there may be some circumstances where the find indicator should be immediately cancelled and hidden, such as when the view's content or selection is changed without the knowledge of the NSTextFinder. The find indicator can be cancelled with the cancelFindIndicator method on NSTextFinder listed below. If your document is not scrolled by NSScrollView, then you should set the findIndicatorNeedsUpdate property to YES.
NSTextFinder is responsible for drawing the yellow find indicator background bezel, but the view must provide the contents. NSTextFinder will setup a drawing context and cause the contentView to draw into it. There are two ways this can happen.
NSTextFinder will invoke drawCharactersInRange:forContentView: on its client, if the method is implemented. The client should then draw the requested characters (or ask the containing view the characters, which is provided as a convenience) at the origin of the current context. If the requested character range partially intersects a glyph range, the client may draw the entire glyph, if necessary for performance.
If drawCharactersInRange:ForContentView: is not implemented, then NSTextFinder will use the normal NSView drawing mechanisms. The contentView does not need take any additional action to make this happen.
You should draw the contents of a find indicator slightly differently than normal to make sure it is readable on the background of the find indicator. For example, instead of drawing the text in its original color, it should be black. You can find out when find indicator drawing is occurring by checking the drawingFindIndicator property.
Incremental Search Support with NSTextFinder
A common feature used in conjunction with the find bar is incremental search. This feature allows users to find matches immediately as they are typing. In Mac OS 10.7, NSTextFinder provides this feature for its clients with minimal additional API.Incremental searching can be enabled by setting the incrementalSearchingEnabled property on NSTextFinder to YES. This property alone is sufficient to cause the NSTextFinder to start searching incrementally.
For improved responsiveness, when a document is sufficiently long, NSTextFinder will search the document in the background. To ensure thread-safety, a client using incremental search must call noteClientStringWillChange on its NSTextFinder before any changes are made to the string provided to the NSTextFinder.
During incremental search, all visible matches are highlighted. NSTextFinder provides two alternatives for highlighting. If your client implements the read-only visibleCharacterRanges property, then by default a gray overlay will appear over your find bar container’s contentView with transparent "holes" cut out for each match. This contentView should be a superview of all contentViews reported by the NSTextFinderClient. NSScrollView already implements this property, so you only need to implement this property when using a different container class. Finally, this mode also requires the same two NSTextFinderClient methods that are used to display the find indicator: contentViewAtIndex:effectiveCharacterRange: and rectsForCharacterRange:. However, the implementation of these methods is identical for both purposes, so no additional work is required to support incremental search.
To disable the overlay, you should set the incrementalSearchingShouldDimContentView property to NO. If you do, you can still show matches from incremental search by using the NSTextFinder’s incrementalMatchRanges property. This property is an array of ranges specifying all incremental matches found so far. The array is updated on the main thread while the incremental search is performed on a background thread. You may use Key Value Observing to receive notifications when the contents of this array changes. If you use NSKeyValueObservingOptionNew and NSKeyValueObservingOptionOld, the dictionary passed to the KVO notification method will provide the indexes and ranges that are added or removed.
You may use the incrementalMatchRanges array to display the matches however you choose, but if you choose to highlight them in your view, it is recommended that you use the drawIncrementalMatchHighlightInRect: method to draw the standard highlight.
Incremental Search Support in NSTextView
In Mac OS 10.7, NSTextView provides incremental search support. It is disabled by default, but it can be enabled by setting the incrementalSearchingEnabled property to YES. Also, since incremental searching requires the find bar, usesFindBar must be set to YES for incremental searching to be occur.NSCollectionView Drag and Drop Click-and-hold Delay
When NSCollectionView gained drag and drop support in Mac OS 10.6, a click-and-hold delay was required to differentiate drag and drop from drag selection. This is still the case in Mac OS 10.7 for collection views that support multiple selection. However, since drag selection is not important for single-selection collection views a click-and-hold delay is no longer required to start a drag and drop operation in applications linked on or after Mac OS 10.7.NSCollectionView Multi-image dragging
NSCollectionView now supports multi-image dragging.To support multi-image dragging for a dragging source, all you need to do is implement -collectionView:pasteboardWriterForItemAtIndex:. NSCollectionViewItem is responsible for creating dragging image components. You can customize its default behavior by overriding -draggingImageComponents. You can customize dragging session settings like draggingFormation or animatesToDraggingFormationOnBeginDrag by implementing -collectionView:draggingSession:willBeginAtPoint:forItemsAtIndexes:. You can also be informed when dragging ends by implementing -collectionView:draggingSession:endedAtPoint:dragOperation:.
More work is required to support multi-image dragging for dragging destinations. First of all, you need to make sure your acceptDrop: method uses the new item-based NSPasteboard APIs. Some time after a multi-image drag enters your collection view, -collectionView:updateDraggingItemsForDrag: is called on the delegate. Within that method you should enumerate the dragging info's dragging items and do the following:
1. Map the NSDraggingItem's item to a representedObject for your collection.
2. Create a new NSCollectionViewItem with the represented object. (The collection view item is typically created by copying the itemPrototype. Also, you may create a single collection view item and reuse that for each NSDraggingItem.)
3. Determine the target frame size for the item and set the NSDraggingItem's frame size.
4. Update the NSDraggingItem's image components provider using the frame from (3) and NSCollectionViewItem's -draggingImageComponents (or other custom code for generating the image components).
NSCollectionView's subview layout is dependent on the number of items contained in the collection. The method -frameForItemAtIndex: is based on the number of items currently contained in the collection. Since drag and drop operations typically change the number of items in the collection, you will likely need to use the new -frameForItemAtIndex:withNumberOfItems: method to compute the proper frame sizes.
To make the dragged images animate to their final destinations, you can set the draggingInfo's animatesToDestination property in your delegate's implementation of validateDrop:. If you do, you should enumerate the dragging info's dragging items in acceptDrop: and update them with their proper frames. Be sure to update your collection view's or array controller's content synchronously in acceptDrop: to get the proper animations.
NSDocument Autosaving In Place – Unintentional Edits and Versions Browser
NSDocument's "Autosaving In Place" eliminates the largely unnecessary user task of manually saving documents. However, when saving happens without user knowledge, it becomes easier for unintentional edits to get saved to disk. To help avoid this problem, when an NSDocument subclass adopts autosaving in place by returning YES from +autosavesInPlace, it will use various heuristics to determine when a user may have opened a document with the intention to read, but not edit it. When such a file is opened, NSDocument will warn the user when an edit is made to the document, offering them the choice of canceling the change, creating a new document with the change, or allowing editing, if possible. A document that is preventing edits will display "Locked" in the right-hand corner of the title bar. If the user intends to edit the document, he can avoid the alert by clicking on the "Locked" text and allowing editing by choosing "Unlock" in the pop-up menu. A document that has been changed since it was last opened and is therefore being actively autosaved in place will show "Edited" in the titlebar instead of "Locked".Sometimes a user may choose to edit a file, then decide to revert those changes later. While a user is working on an autosaved in place document, NSDocument will, at various times, preserve the current contents of the document to disk. The user can access these preserved versions by selecting "Revert to Saved" from the File menu, or by selecting "Browse All Versions…" from the menu available in the right-hand corner of the document window's title bar. This brings the user into an interface that displays both the current document and a "stack" of previous versions. If the user chooses to restore a previous version, the current document contents are preserved on disk, if necessary, and the file's contents are replaced with those of the selected version. By holding the Option key, the user can also choose to restore a copy of a previous version, which will not affect the current document contents. Since old document versions are live windows, the user can also select and copy contents from them and paste them into the current document. Since the version browser interface takes control of the entire screen, you can set the NSDocumentRevisionsDebugMode user default to YES for easier debugging of your application in this interface.
Mac OS 10.7 includes several APIs to allow your application to customize its behavior in the version browser. First of all, the window that is used in the version browser is determined by the -windowForSheet API. You may also wish to simplify application's interface when the user is viewing old document versions. You can detect when to do this by checking the -isInViewingMode method on NSDocument. This value is set to YES before your the interface is loaded for an old document version. If you wish to change the interface of the current document when it enters or exits the version browser you can use the NSWindowDelegate methods, -windowWillEnterVersionBrowser: and -windowDidExitVersionBrowser:, or their NSNotification equivalents.
In order to fit two document windows side-by-side, the version browser may attempt to resize and/or scale the windows. When a window is scaled down the user can click on it to bring it back to full size. You can control the size of your document window in the version browser by implementing the -window:willResizeForVersionBrowserWithMaxPreferredSize:maxAllowedSize: method. The returned size will be used to determine the resulting size of the document windows in the version browser. You may return any size from this method, but that size is constrained to the "maximum allowed size" to ensure the version browsing controls are still accessible. If the window is larger than this size and cannot be resized, an exception will be thrown. The "maximum preferred size" parameter specifies the largest size a window can be without requiring scaling. If this method is not implemented, the version browser will use -window:willUseStandardFrame: to determine the resulting window frame size.
NSDocument File Dependence
When an NSDocument's file disappears from the file system the document can usually continue to function properly and even allow the user to re-save it if the entire file has been loaded into memory. When this happens with a file that has not been completely loaded, then unexpected errors may occur. NSDocument has a new method in Mac OS 10.7 called -isEntireFileLoaded, which is intended to provide explicit knowledge about a document's dependence on its file. NSDocument may use this information to do things like prevent volume ejection or warn the user when a partially loaded file disappears from the file system.NSDocument's base implementation of this method returns YES, providing the same default behavior as before Mac OS 10.7. If you override -readFromURL:ofType:error: or -readFromFileWrapper:ofType:error: and read only a portion of the file or file wrapper, you should override -isEntireFileLoaded and return NO. If at some point the entire file is completely loaded, then you can start returning YES. Similarly, if portions of the file are removed from memory, you should start returning NO again.
NSSplitView Restorable State
In Mac OS 10.7, NSSplitView will participate in automatic state restoration by persisting its subview's frames, and restoring them when the app launches. If necessary, this process will invoke the same NSSplitViewDelegate methods used for constraining divider positions to ensure proper layout. For compatibility reasons, an NSSplitView will not participate in Mac OS 10.7's automatic state restoration unless it is assigned an autosave name. You can set the autosave name in Interface Builder, or in code using the -setAutosaveName: method.Changes for @2x images
10.7 contains some changes to accommodate the iOS naming convention for high resolution images. If your bundle contains files named foo.png, foo@2x.png, and foo.pdf then [NSImage imageNamed:@"foo"] will return an NSImage backed by all three files, provided your app is linked against the 10.7 SDK.This accomplishes something similar to how iOS handles hi-res images, but the mechanics are a little bit different. On iOS it's known at device startup whether @2x images are appropriate. On OS X, the machine might go to sleep and wake up with a different screen attached. On iOS [UIImage imageNamed:@"foo"] loads foo.png if you are running on an iPhone 3Gs, and it loads foo@2x.png if you are running on an iPhone 4. On the mac, [NSImage imageNamed:@"foo"] unconditionally returns an NSImage object that contains three NSImageRep objects, one for each of foo.png, foo@2x.png, and foo.pdf. Each time the NSImage is drawn, it selects the representation best for the drawing context.
That takes care of +[NSImage imageNamed:], but +imageNamed: only finds resources in the main app bundle, not in frameworks or plugins. Framework authors typically do something like this:
[[NSImage alloc] initByReferencingURL:[pluginBundle URLForImageResource:@"foo"]]…which can only pick up one of foo.png, foo@2x.png, and foo.pdf since the URL is explicit. To provide a way forward, we add a one step method to get an image:
@interface NSBundle(NSBundleImageExtension)This can return an NSImage backed by multiple files, and it can also automatically call setTemplate: when appropriate according to the template image naming convention used in +imageNamed:.
- (NSImage *)imageForResource:(NSString *)name NS_AVAILABLE_MAC(10_7);
@end
Despite NSImage having this multifile support, we don't really expect you to use it much. If you have resources foo.png and foo@2x.png in your project resources, Xcode is going to roll them together into a multi-representation foo.tiff that contains both during build. NSImage has always had support for reading TIFF files with multiple image representations.
Performance note: NSImage has laziness in instantiating images. [NSImage imageNamed:] does no file IO at all, provided bundle contents are already cached. When you draw an image, we read the header of each backing file in order to see how large each is and select the best one given the drawing context. However, we avoid decoding the image data for a representation that is never selected. If you let Xcode assemble your images into a single TIFF, there's only one file whose header we have to read.
Bug in deprecated -[NSImage composite…] methods fixed conditional on linking with 10.7 SDK
We don't mention every bug fix in the release notes, but we do try to mention every time we do something that has behavior conditional on the SDK you link with. I say this to explain why this rather complicated section is here. If you don't use the deprecated -composite… methods, you can skip to the next section.For 10.6, we rewrote the internals of NSImage, and fixed a lot of bugs and strangenesses. As part of that, we deprecated all the NSImage methods that start with composite, because they have surprising semantics as compared to the methods that start with draw. Since the -composite… methods were being deprecated, we didn't try to change their behavior and correct bugs in places where the fixes might mess up apps depending on the bugs.
However, the -composite… methods are used in really a lot of extant code, despite being deprecated. And one bug that we were aware of messes up a lot of applications when running at HiDPI, even if they're okay at 1.0x.
So, we fixed the bug, conditional on the app either being run at HiDPI or on it being linked against the 10.7 SDK or greater.
The bug is that -[NSImage compositeToPoint:fromRect:operation:fraction:] interpreted the fromRect in the coordinates of the NSImageRep selected to draw with instead of in the coordinates of the NSImage.
The symptom is that instead of a whole image at the correct size, you see just a corner, blown up, or you see the image too small. If you hit this, switch to one of the NSImage methods that start with -draw… (and also fix the fromRect, since it's probably wrong).
Cocoa Autolayout
10.7 introduces a new system for layout, reworking the springs and struts model. So big it needs its own release note!NSApplication and Apple Push Notifications
NSApplication can register to receive push notifications similar to UIApplication on iOS. Currently, only badge notifications are supported on OS X for non-running applications. Running applications can interpret data from the push server as desired.To register to receive push notifications, use:
- (void)registerForRemoteNotificationTypes:(NSRemoteNotificationType)types;If successful, the application’s delegate will receive a callback message with an opaque data blob representing the token used to communicate with the Apple Push Notification server (read the documentation on Push Notifications for more details on that interaction). If the application is not open when a push notification is received, the notification information is delivered in the userInfo dictionary for the NSApplicationDidFinishLaunchingNotification notification using the key NSApplicationLaunchRemoteNotificationKey. If the application is open when a push notification is received, the delegate gets a application:didReceiveRemoteNotification: message.
Button Appearance Changes
As part of an ongoing refresh of Aqua in OS X Lion, some buttons look different and may not look the same in every context in your application. Specifically, the “Round Textured” button is not appropriate in any context other than directly on the background of a textured window. If you are using this kind of button in a table view or other context, please consider changing it to a “Round Rect” button.NSPasteboard data-reading methods
The documented behavior of NSPasteboard has historically required that -types or -availableTypeFromArray: be sent to a pasteboard before invoking the data-reading methods -dataForType:, -propertyListForType:, and -stringForType:. Beginning in the 10.6 (the previous release of OS X), calling these data-reading methods perform an implicit check for the requested type before attempting to read the data, making it no longer strictly required to call -types or -availableTypeFromArray: before calling the data-reading methods.NSPasteboard UTI and Scrap Manager/Drag Manager Interoperability
As applications move towards using UTIs with NSPasteboard, as opposed to pboard types, an application may still need to interact with existing applications which use four character OS types with the legacy Scrap Manager and Drag Manager.To interoperate with these legacy types, use the functions declared in UTTypes.h to generate the appropriate UTI string for a given OSType. The code below generates the appropriate UTI from a fictional OS type 'test':
CFStringRef flavorString = UTCreateStringForOSType('test');
CFStringRef conformsToType = (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_5) ? NULL : kUTTypeData;
CFStringRef utiForConvertedScrapFlavor = UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, flavorString, conformsToType);
// Use the utiForConvertedScrapFlavor
CFRelease(flavorString);Using the generated UTI to read and write pasteboard data allows your application to interoperate with legacy Scrap Manager and Drag Manager clients.
CFRelease(utiForConvertedScrapFlavor);
Two things to note about the code sample:
1. The AppKit version check is required because of a change in behavior between 10.5 and 10.6. All UTIs used on the pasteboard should conform to kUTTypeData, but in 10.5 UTIs generated internally by NSPasteboard did not. This was addressed in 10.6. The version check ensures that the UTI string generated in your code will match the UTI that will appear on the pasteboard for that release. If your application will ship only on 10.6 and later, you can remove the version check and use kUTTypeData as the last argument to UTTypeCreatePreferredIdentifierForTag().
2. Always generate the UTI dynamically from the OSType. Do not store or cache the UTI string between application launches. This will ensure your pasteboard code continues to work, even if the developer of the legacy application releases a new version at a later date that formally maps the legacy OSType to a declared UTI string, or if the algorithm for generating dynamic UTIs changes in a future OS release.
Additional NSPasteboard Exceptions Added
NSPasteboard and NSPasteboardItem have methods to set NSData, NSString, and property list values. Classes that implement the NSPasteboardWriting protocol also provide property list values. Prior to 10.7, setting or providing an object of the wrong class, or invalid property lists, would fail silently. For applications linked on or after 10.7, this programming error now causes an exception to be raised.Accessibility
When an accessibility client sets the AXSelectedRows and AXSelectedColumns of NSTableView, the behavior now correctly mirrors user selection of rows and columns, including invoking appropriate delegate methods such as -tableView:tableViewselectionIndexesForProposedSelection:.NSScroller no longer reports accessibility children when its subelements are not visible.
NSSecureTextField now reports its caps lock and num lock indicators as AXImage accessibility children, if they are visible.
NSRulerView now posts NSAccessibilityUnitsChangedNotification when its measurement units change.
Accessibility of View-Based NSTableView and NSOutlineView
OS X v10.7 adds support for using NSViews instead of NSCells to define the contents of table and outline views. View based table and outline views automatically provide a richer and more complete representation to accessibility. This is because complex table interfaces can now be represented as a composition of standard AppKit views and controls, such as text fields and image views, instead of custom cell subclasses. This allows a view-based table or outline to take advantage of the built-in accessibility of AppKit views and controls, removing the need for custom accessibility code to support complex cell subclasses.View-based table views are reported to accessibility as using AXCells as the children of its rows. (Despite the similar terminology, an AXCell has no relationship to an NSCell). This means that changing an existing table view or outline view to be view-based will require AppleScript-based or AX-based user interface automation scripts to be updated to handle the new structure.
Accessibility and table cell views
The NSTableCellView class has a built-in 'textField' outlet and property. You should connect this outlet to the main text field in the cell. This text field will be automatically reported as the title UI element of the cell. Connecting this outlet greatly enhances the usability of the table for VoiceOver users, especially when the cell contains multiple pieces of information, such as additional subordinate pieces of information.If you use a table cell view which is not a subclass of NSTableCellView, adding a 'textField' outlet and property is strongly recommended. AppKit treats 'textField' as a special key for any table cell view, and reports the value of that property as the title UI element of the table cell.
Changes to behavior of NSAccessibilityPostNotification()
In 10.7 NSAccessibilityPostNotification() checks for accessibility client observers differently depending on the notification. See NSAccessibility.h for a detailed description of the behavior changes.NSPopover Accessibility
Popovers displayed using the new NSPopover class report themselves to accessibility clients using the new NSAccessibilityPopoverRole constant in NSAccessibility.h.The accessibility parent of a popover is the user interface element that the popover popped from. Because an NSView subclass can have accessibility subelements, AppKit uses a heuristic to determine the correct accessibility parent for a popover, treating that element as the popover's parent, and temporarily making the popover a child of that element while the popover is visible.
If you need to override AppKit's default assignment of the popover's accessibility parent, override -accessibilityAttributeValue: in an NSPopover subclass to return a different object that implements NSAccessibility.
Accessibility and Automatic Termination
For All AppKit Applications:After an accessibility client, such as VoiceOver, has made an accessibility request to an application, automatic termination will be disabled for that application to ensure that accessibility clients can continue to access the same application process. Because accessibility clients such as GUI Scripting are also used for automated testing during application development, setting the user default NSDisableAutomaticTerminationAfterAccessibilityRequest to NO will override the default behavior so that these automated tests can be run without automatically disabling automatic termination.
For Assistive Applications Only:
If your application is an accessibility client (i.e. those that use the AX APIs found in the HIServices framework to interact with other applications), you should consider how your application uses the AX APIs when planning to enable Automatic Termination in your application. For example, if your application needs to respond to accessibility notifications, even when it is hidden, you should disable automatic termination. On the other hand, if your application displays information about the UI element under the mouse, but does not make accessibility requests when hidden, the usage of AX APIs would not prevent you from enabling automatic termination. See the documentation for more details on Automatic Termination itself.
Accessibility and Overlay Scrollers
An overlay scroller reports the AXHidden attribute to indicate if it is currently hidden or visible. This attribute is settable. An assistive application can set this value to 'NO' to make the scroller visible temporarily. Note that 'NO' is the only valid value, visible scrollers cannot be forced to be hidden with a value of 'YES'.Press-and-Hold for Certain Keys
A new system-wide behavior has been implemented for cases in which the user presses and holds down certain keys while editing text. Instead of repeating the key, this new behavior provides a mechanism for entering alternative (usually accented) forms. UI appears near the text showing these alternative forms, which can be selected by clicking, by arrowing, or by pressing a digit corresponding to the index of the desired form. Keys other than letter and digit keys continue to repeat when pressed and held.This new behavior generally should be active only in cases in which text is actually being edited, but there are a few cases in which it may need to be turned off explicitly. There is a default provided for this, ApplePressAndHoldEnabled, which may be set to NO to turn off the new press-and-hold behavior and re-enable key repeat for all keys. This may be set for a specific application, to control the behavior for that application only, or globally, to control the behavior for all applications.
Additional API for Autocorrection
There is new API on NSSpellChecker for supporting autocorrection. First, there is now a convenience method that allows separate access to correction results, which otherwise had been available only via the unified text checking methods checkString:range:types:options:inSpellDocumentWithTag:orthography:wordCount: and requestCheckingOfString:range:types:options:inSpellDocumentWithTag:completionHandler:.- (NSString *)correctionForWordRange:(NSRange)rangeSecond, there is now API to allow clients to report the user's response to a proposed correction, so that the spell checker can learn from this and adjust future correction behavior accordingly. This is used by views such as NSTextView and WebView, and third parties implementing their own text editing views that support autocorrection can use it as well. The tag, language, word, and correction should match those from the original correction result, so that the spellchecker can match them. This implies that in order to record responses properly, clients must store the original word and original correction at least from the point at which the user accepts it until the user edits or reverts it.
inString:(NSString *)string
language:(NSString *)language
inSpellDocumentWithTag:(NSInteger)tag;
enum {
NSCorrectionResponseNone, // No response was received from the user
NSCorrectionResponseAccepted, // The user accepted the correction
NSCorrectionResponseRejected, // The user rejected the correction
NSCorrectionResponseIgnored, // The user continued in such a way as to ignore the correction
NSCorrectionResponseEdited, // After the correction was accepted, the user edited the
// corrected word (to something other than its original form)
NSCorrectionResponseReverted // After the correction was accepted, the user reverted the
// correction back to the original word
};
typedef NSInteger NSCorrectionResponse;
- (void)recordResponse:(NSCorrectionResponse)responseThird, there is now API to allow clients to bring up the new UI for autocorrection. This is also used by views such as NSTextView and WebView, and third parties implementing their own text editing views that support autocorrection can use it as well. This user interface is used to indicate a correction intended to be made, allowing the user to accept or reject it; or once a correction has been made, to indicate the original form, allowing the user to revert back to it; or to display multiple alternatives from which the user may choose one if desired. The primaryString is the first string displayed, a correction or reversion according to the type of indicator; the alternativeStrings should be additional alternatives, if available. Only one indicator at a time may be displayed for a given view, and the only thing a client may do with the indicator after displaying it is to dismiss it. When an indicator is dismissed, whether by user action or by the view, the completion block will be called, with the acceptedString argument being either the replacement string accepted by the user, or nil if the user has not accepted a replacement.
toCorrection:(NSString *)correction
forWord:(NSString *)word
language:(NSString *)language
inSpellDocumentWithTag:(NSInteger)tag;
enum {
NSCorrectionIndicatorTypeDefault = 0, // The default indicator shows a proposed correction
NSCorrectionIndicatorTypeReversion, // Used to offer to revert to the original form after a correction has been made
NSCorrectionIndicatorTypeGuesses // Shows multiple alternatives from which the user may choose
};
typedef NSInteger NSCorrectionIndicatorType;
- (void)showCorrectionIndicatorOfType:(NSCorrectionIndicatorType)typeFinally, there is additional API to support the new global user preference settings for automatic text replacement and spelling correction. NSTextView now by default will keep track of and follow these settings automatically, but applications using NSTextView can override that by programmatically using existing NSTextView methods such as -setAutomaticTextReplacementEnabled: and -setAutomaticSpellingCorrectionEnabled: to control an individual text view's settings. The new API is primarily for non-text view clients who wish to keep track of the settings for themselves, using the NSSpellChecker class methods to determine their values, and optionally also notifications to determine when the settings have changed.
primaryString:(NSString *)primaryString
alternativeStrings:(NSArray *)alternativeStrings
forStringInRect:(NSRect)rectOfTypedString
view:(NSView *)view
completionHandler:(void (^)(NSString *acceptedString))completionBlock;
- (void)dismissCorrectionIndicatorForView:(NSView *)view;
+ (BOOL)isAutomaticTextReplacementEnabled;
+ (BOOL)isAutomaticSpellingCorrectionEnabled;
NSString * const NSSpellCheckerDidChangeAutomaticSpellingCorrectionNotification;
NSString * const NSSpellCheckerDidChangeAutomaticTextReplacementNotification;
Regular Expression Text Checking
There is an additional key, NSTextCheckingRegularExpressionsKey, to be used in the options dictionary with the unified text checking methods checkString:range:types:options:inSpellDocumentWithTag:orthography:wordCount: and requestCheckingOfString:range:types:options:inSpellDocumentWithTag:completionHandler:. This key allows clients to specify a set of regular expressions to be searched for during text checking. Results will be reported using NSTextCheckingResults of type NSTextCheckingTypeRegularExpression. There are no default actions provided in response to these results, but clients may in this way detect the occurrence of particular regular expressions in text, in somewhat the same way that Data Detectors currently detects addresses, phone numbers, etc., and take any desired action in response.NSTableView / NSOutlineView General Updates
Tables/Outlines setup as a source list (identified by the selectionHighlightStyle of NSTableViewSelectionHighlightStyleSourceList) on 10.7 and higher will now only allow a drag to begin from "content" in the cells. This is determined by using the NSCell methods hitTestForEvent:inRect:ofView: and returning NSCellHitContentArea.Since Leopard, NSTableView has supported full width cells (which span all columns) by returning a cell from tableView:dataCellForTableColumn:row: when a 'nil' tableColumn is passed in. Up until Lion, NSTableView would incorrectly pass a non-nil value to the tableColumn parameter when the user right clicked on a row. This has been fixed for Lion linked applications, but prior applications will need to work around this by returning the proper cell even though a column was passed to the tableView:dataCellForTableColumn:row: method.
In 10.7 and higher tables that support variable row heights now have the row height values returned from -tableView:heightOfRow: cached by the NSTableView/NSOutlineView until noteHeightOfRowsWithIndexesChanged: (or reloadData) is called. Applications targeting 10.6 or lower need to ensure the implementation always returns the same value for the same row (as it may be called multiple times for the same row) until noteHeightOfRowsWithIndexesChanged: / reloadData is called.
Allowing column selection ( [tableView setAllowsColumnSelection:YES]) combined with the "none" selection highlighting style (NSTableViewSelectionHighlightStyleNone) would previously cause a crash when selecting a row after selecting a column. This has been fixed for applications that link on 10.7 or higher. Applications targeting 10.6 or lower should turn off column selection with [tableView setAllowsColumnSelection:NO].
Calling [tableView setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList] will have the side effect of changing the backgroundColor to the standard "source list" color, and setting the draggingDestinationStyle to be NSTableViewDraggingDestinationFeedbackStyleSourceList. This allows one to easily setup a "source list" style NSTableView or NSOutlineView. For applications linked less on an OS lower than 10.7, calling setSelectionHighlightStyle to anything else would change the backgroundColor to [NSColor controlBackgroundColor] and draggingDestinationStyle to NSTableViewDraggingDestinationFeedbackStyleRegular. 10.7 now correctly only does this change if the previous style was a source list.
NSTableView now has a new rowSizeStyle property. This is mainly for sidebars/source lists to have a consistent layout defined by the Human Interface Guidelines. Setting the rowSizeStyle to NSTableViewRowSizeStyleCustom causes the table to behave as it always has. For any other value, the table will automatically change the -rowHeight based on various options; for instance, group rows may have a smaller row height than non-group rows. When the rowSizeStyle is not NSTableViewRowSizeStyleCustom, the -rowHeight will return the standard row height for non-group rows. Variable row heights can still be used; it is recommended to return the -rowHeight when a default height is needed, but group rows will require a custom specific row height to be returned (i.e.: 20). View Based TableViews should use NSTableCellView -- it will automatically layout the textField and imageView based on the current rowSizeStyle, and apply appropriate "sidebar / source list" attributes to the textField's stringValue and imageView's image.
NSTableView / NSOutlineView - View Based TableView and Animations
NSTableView and NSOutlineView in Mac OS 10.7 now support the use of NSViews instead of NSCells. Search for "View Based" in NSTableView.h, NSOutlineView.h for more information, and see: NSTableRowView.h and NSTableCellView.h.NSTableView and NSOutlineView now have methods to insert/remove/move rows and items, optionally with an animation. These methods work for both the "Cell Based" and "View Based" NSTableView/NSOutlineView, however, the "Cell Based" version must always call -beginUpdates before doing any insert/remove/move calls, and then end with an -endUpdates. This allows the NSTableView to capture the current state to perform animations. You must not make changes to your model until after -beginUpdates has called, and you must update your model before -endUpdates is called.
NSTextFields inside a View Based TableView may have the -backgroundColor automatically changed when editing. Specifically, if an NSTextField is not bordered (isBordered==NO && isBezeled==NO), and does not draw a background (drawsBackground==NO), then NSTableView will automatically make the -backgroundColor be white, and set to be drawn, when it is editing (ie: when it is the firstResponder). This helps provide a complete Table View experience for the end user.
The NSTableRowView is responsible for drawing the bottom line of a horizontal grid if the gridStyleMask has NSTableViewSolidHorizontalGridLineMask set. This will happen automatically. However, the TableView draws any grid lines below the last row with drawGridInClipRect:. Subclassing drawGridInClipRect: is still acceptable, but horizontal grid lines should only be drawn past the last row. Vertical grid lines are currently not customizable.
Note that the View Based TableView does not support column selection.
The View Based NSTableView supports animating selection changes. This can be done using the animator proxy and calling -selectRowIndexes:byExtendingSelection:, such as: [[tableView animator] selectRowIndexes:indexes byExtendingSelection:NO];
NSTableView now implements -cancelOperation: to allow escape to automatically cancel text editing. This is accomplished by calling -abortEditing on the NSControl that is showing the current field editor. This action can be customized by overriding -cancelOperation: on NSTableView or overriding -abortEditing on the control that exists in the View Based TableView.
To disable animations, create an animation group around the code you want them to be disabled for and set the duration to 0. IE:
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0];
...
[NSAnimationContext endGrouping];
Syncing Animations with NSTableView
The following NSTableView methods can animate:- (void)insertRowsAtIndexes:(NSIndexSet *)indexes withAnimation:(NSTableViewAnimationOptions)animationOptions;There may be a need to sync animations that happen inside the view, or with other things outside of the Table. This can be done by surrounding the calls with a specific NSAnimationContext grouping that already has a specific duration set. See the TableViewPlayground demo app for an example. In addition, if the duration is 0, then no animation will happen.
- (void)removeRowsAtIndexes:(NSIndexSet *)indexes withAnimation:(NSTableViewAnimationOptions)animationOptions;
- (void)moveRowAtIndex:(NSInteger)oldIndex toIndex:(NSInteger)newIndex;
- (void)noteHeightOfRowsWithIndexesChanged:(NSIndexSet *)indexSet; // View-based tableview only
NSTableView / NSOutlineView Multi-image Dragging
NSTableView and NSOutlineView now support multi-image dragging. NSOutlineView methods mirror the NSTableView ones, but use 'item' instead or 'row. There are two components to dragging; being a dragging source and a dragging destination.For a dragging source, the dataSource should implement tableView:pasteboardWriterForRow: and return an object that implements NSPasteboardWriting. That is all that is required to be a source. However, dataSource hooks are also provided for when the dragging session begins (tableView:draggingSession:willBeginAtPoint:forRowIndexes:) and ends (tableView:draggingSession:endedAtPoint:operation:). To control the drag image, the NSCell method draggingImageComponentsWithFrame:inView: is called on the clicked cell. For View Based TableViews, NSTableCellView's draggingImageComponents should be used, and can be overridden to customize the drag image.
The default implementation of NSCell's draggingImageComponentsWithFrame:inView: will generate an image from the cell and return two components: one for NSDraggingImageComponentLabelKey and another for NSDraggingImageComponentIconKey. This is done by capturing the portion from titleRectForBounds: and imageRectForBounds: respectively.
NOTE: NSCell currently has an issue where it will return an empty array from draggingImageComponentsWithFrame:inView: if the cell does not have an image portion. To work around this, subclass NSCell and override draggingImageComponentsWithFrame:inView: and generate your own NSDraggingImageComponents in the returned array.
To be a dragging destination, more work is required than to be a source. Implement tableView:updateDraggingItemsForDrag: to provide a drag image representation for the source content that was dragged over the tableView. Generally, this involves using a template NSCell and calling the new NSCell method draggingImageComponentsWithFrame:inView:. For View Based TableViews, NSTableCellView's draggingImageComponents should be used. tableView:validateDrop:proposedRow:proposedDropOperation: should be implemented as normal. In tableView:acceptDrop:row:dropOperation one should enumerate the NSDraggingInfo's classes and create new model objects. The table should be told of the changes by doing an insertRowsAtIndexes:withAnimation: using the animation option NSTableViewAnimationEffectGap to create a gap for the dragged image. The NSDraggingItem's draggingFrame should be updated to the appropriate -frameOfCellAtColumn:row:.
See the DragNDropOutlineView (cell based) and TableViewPlayGround (view based) examples for more information.
Implementing Sidebar Source Lists with NSOutlineView
The proper way to create a source list sidebar is to use NSOutlineView. The rowSizeStyle should be set to NSTableViewRowSizeStyleDefault, and all three sizes should be tested. Items which should not be expandable or collapsable by the user can have the outline cell hidden by using the delegate method outlineView:shouldShowOutlineCellForItem:. These items should be programmatically expanded. Group header items are created by using the delegate method outlineView:isGroupItem: and returning YES. To find out what rowSizeStyle the system is using, you can call [tableView setRowSizeStyle:NSTableViewRowSizeStyleDefault] and then read the current value with: [tableView effectiveRowSizeStyle]. This will return NSTableViewRowSizeStyleSmall, NSTableViewRowSizeStyleMedium or NSTableViewRowSizeStyleLarge as based on what the user has set in System Preferences. There is no direct way to know the image size for the icons. If you use NSTableCellView in a View Based NSOutlineView, then the cell will automatically be setup with the correct sizes when the rowSizeStyle is set. If this is not sufficient, then you can make the assumption for the following sizes: NSTableViewRowSizeStyleSmall = 16x16, NSTableViewRowSizeStyleMedium = 18x18, and NSTableViewRowSizeStyleLarge=32x32.For Cell Based TableViews, a default set of attributes will be applied to the cell's stringValue creating a custom attributedStringValue based on the Human Interface Guidelines. This is done before calling -willDisplayCell, so in general, it is recommended to not change the text color or font of the cell in -willDisplayCell:. In Leopard and SnowLeopard, attributes were only applied to the group header cells. For applications linked on Lion and later, custom attributes will also be applied to all other cells (specifically, a 1-pixel drop shadow). For applications that want this feature pre-Lion, they can use -willDisplayCell to customize the -attributedStringValue as desired.
For View Based TableViews, a plain NSTextField (as setup in Xcode's Interface Builder) should be returned for the header items. Or, alternatively an NSTableCellView can be used with the -textField property properly setup to a basic NSTextField. The text should be set to an uppercase string, but no attributes need to be applied; they will automatically be applied by NSOutlineView. All other rows should use NSTableCellView, which will have proper layout done based on the -rowSizeStyle set to it. In addition, the -textField property will automatically have attributes applied to it as necessary. In general, source lists should not float the group rows, and [outline setFloatsGroupRows:NO] should be called.
To add an accessory view to an NSTableCellView that resides in a sidebar source list, you will need to subclass NSTableCellView and perform layout of the additional views in -viewWillDraw. First call [super viewWillDraw] to have the standard layout of the text and image performed.
To create an unread indicator or accessory button, use an NSButton (or NSButtonCell) with a bezelStyle set to NSInlineBezelStyle. This bezel style creates a special inline look for tables. When used inside a source list, it has appropriate color attributes applied to it to appear as an unread indicator (or colored button). It will behave as a button only if the target/action is set; otherwise, it can be used as a static unread indicator. -cellSizeForBounds: properly implements the minimum size required for an unread indicator if the cell has text. If the cell has an image, it will default to a circular size.
Prior to Lion, the source list background color was a special color that varied depending on the window key state. On Lion, it is now a gradient.
Debugging NSTableView
To help debug general issues, one can have the NSTableRowView show row numbers by running the application with “-NSTableRowViewShowRows YES” as a parameter. In addition, this also works in the "Cell Based" TableView.Debugging View Based NSOutlineView Changes
To help debug errors in calls to insert/deleting/moving inside NSOutlineView, run the application with “-NSOutlineViewValidateChanges YES”. This performs some validation to ensure the inserts/deletes/moves match what the dataSource will return. Note that this should only be turned on for testing/debugging, and will cause a slowdown for all inserts/deletes/moves inside NSOutlineView. Each validation will happen after each call to insert/delete or move (it will NOT happen at the end of batched updates called inside of a beginUpdates/endUpdates block). Errors will be reported by throwing an exception.NSApplication
AppKit now has the ability to report uncaught exceptions. It is controlled by a user default: NSApplicationShowExceptions (YES/NO). The default shipping value is NO. In general, it is recommend that developers set it to YES during development to catch programming errors. Individual applications can automatically turn this on by using [[NSUserDefaults standardUserDefaults] registerDefaults: ...] to register the option on. It can be set with defaults via: 'defaults write com.yourdomain.app NSApplicationShowExceptions YES'. It can also globally be turned on by writing to the global domain.NSOpenPanel / NSSavePanel
Using the new method -beginWithCompletionHandler: in 10.6 requires you to first retain the save panel (or reference it in the completion handler block, which implicitly retains it). Otherwise, the panel will briefly show and then correctly be autoreleased. For applications linked on 10.7 and later, this problem has been fixed and applications will no longer need to explicitly retain panel.For applications linked on 10.7 and higher, URLs vended from the NSSavePanel may now contain non-file schemes (i.e.: something other than NSURLFileScheme). This will happen if they don't have a physical backing file, such as things in the SHARED section. For compatibility, certain delegate methods (such as panel:shouldEnableURL:) will only be called with non-file URLs, and all other URLs will automatically be enabled. It is recommended that the delegate always enable non-file URLs unless some other behavior is desired.
NSSavePanel and NSOpenPanel have an updated look to match Finder. This look contains four view options (includes cover flow and group options), while the classic look has only three view options and no cover flow. Carbon based applications will always use the classic option. If you have an application which crashes in the open or save panel, you can opt it out of using the new look with a user default: NSSavePanelUseFinderKit NO. Please log bugs reporting any issues encountered.
In Auto Save applications, closing a yet-unsaved document window brings up a combined alert/save panel. Since this panel includes both a "Don't Save" button and a file browser, an ambiguity is created for the keyboard shortcut cmd-d, which can stand both for "Don't Save" and "Desktop". In order to make sure users do not unintentionally lose their unsaved documents, we have chosen to go with "Desktop" for cmd-d, and have switched to using cmd-delete for "Don't Save." This new shortcut should also work in most non Auto Save applications.
NSPopover
AppKit now has a new class for presenting popovers. The header contains lots of self documentation.NSPopover Delegate Methods
When a popover is closed and the delegate should be consulted, the following sequence of events occurs. First, the -popoverShouldClose: method will be invoked on the delegate if implemented. If the delegate returns NO, the popover will not be closed. Otherwise, if the popover instance implements -popoverShouldClose:, that method will be invoked on the popover instance and if the method returns NO, the popover will not be closed.Due to a bug in NSPopover, however, implementing -popoverShouldClose: on the NSPopover instance will not behave as expected. It is recommended that for now -popoverShouldClose: should not be implemented on the popover instance.
Additionally, -popoverShouldClose: is currently incorrectly invoked on the popover delegate and instance if implemented when the popover detaches to a window. You should never return NO in this case.
NSPopover Bindings
A bug in AppKit prevents the NSPopover bindings from working in the initial release of 10.7.NSButtonCell - New Bezel Style
NSButtonCell has a new bezel style: NSInlineBezelStyle. This bezel style creates a special inline look for tables and lists, but can be used in any appropriate location. When used inside a source list table view, it will have automatic color attributes applied to it to appear as an unread indicator (or colored button). To make the button appear as a label (ie: not respond to clicks), call [buttonCell setHighlightsBy:0]. -cellSizeForBounds: properly implements the minimum size required for an unread indicator if the cell has text. If the cell has an image, it will default to a circular size.NSWindow - Child Windows Ordering Out
For applications linked on 10.7 and later, ordering out a child window will now first remove itself from its parent window. Previously, ordering out a child window would implicitly order out the parent window too.NSColor
NSColor now has two class methods to enable creating colors with sRGB or its gray counterpart color spaces. These create colors with colorSpaceName = NSCustomColorSpace and the appropriate color space (+[NSColorSpace sRGBColorSpace] or +[NSColorSpace genericGamma22GrayColorSpace]).These days the sRGB color space is a better match to the default Mac display environment than the calibrated color space. For this reason in general we recommend using sRGB color space for your images and in your colors—this can lead to better fidelity and performance.
In NSColor there is a change in the treatment of individual component accessor methods such as redComponent, etc. In 10.6 and earlier, these are defined to work only on colors in the named colorspaces NSCalibratedRGB and NSDeviceRGB. (Same for component accessors in the gray and CMYK color spaces.)
In 10.7, these component accessors work on any custom colorspace color which has the appropriate underlying colorspace model. So for example this means it's legal to send -redComponent to a color created with the new sRGB creating method.
It's important to note that this does not mean these methods will start doing any conversion; as before, they will work only in cases where no color space conversion is needed.
NSColorSpace
The initialization method initWithColorSyncProfile: now accepts ColorSyncProfileRef as well as CMProfileRef as before. Whatever was passed into this initializer will also be returned when the accessor method colorSyncProfile is invoked.If colorSyncProfile is invoked on instances not created with initWithColorSyncProfile:, then colorSyncProfile will return an instance of ColorSyncProfileRef for Lion-linked apps, and an instance of CMProfileRef for earlier apps.
Note that ColorSync APIs that take ColorSyncProfileRef also take CMProfileRefs as arguments, so the intermixing of the two types should work fine.
NSTextStorage
The text system will now use UTF-8 rather than the default C-string encoding for any documents that otherwise have no encoding tags or hints, and which look like they are valid UTF-8.Vertical Text
OS X v10.7 introduces the complete vertical text writing support through out the Cocoa Text System. There are several API additions for application developers to take advantage of this typographical enhancement.NSVerticalGlyphFormAttributeName
This NSAttributedString attribute controls the glyph appearance based on the layout orientation value specified. When non-nil, the value must be an NSNumber containing an integer value. The value 1 indicates that the run of text is to be rendered with the vertical font variant if available. The layout and rendering engine in the Cocoa Text System substitutes the NSFontAttributeName value with its vertical font returned from -verticalFont method.0 indicates forces horizontal font regardless of higher-level layout orientation settings.
NSFont
NSFont now can manage vertical variant instances if the underlying font typeface can handle the text layout orientation. With vertical font, the text matrix is rotated 90 degrees counter-clock wise. Also, vertical glyph substitution typographic features are enabled by default. There are two API for managing vertical font instances introduced.- (NSFont *)verticalFont;
- (BOOL)isVertical;
Text Layout Orientation
The orientation of lines rendered by NSTextView is determined by the text layout orientation property. NSLayoutManager accesses the orientation value for each view via NSTextContainer. It is allowed to have different orientation settings for views sharing a single layout manager. A new protocol, NSTextLayoutOrientationProvider, and its value enum type, NSTextLayoutOrientation, are declared in NSLayoutManager.h. NSTextContainer and NSTextView conform to NSTextLayoutOrientationProvider protocol.@protocol NSTextLayoutOrientationProviderIn addition to -layoutOrientation method, NSTextView implements -setLayoutOrientation: and -changeLayoutOrientation: methods.
- (NSTextLayoutOrientation)layoutOrientation;
@end
- (void)setLayoutOrientation:(NSTextLayoutOrientation)theOrientation;
- (void)changeLayoutOrientation:(id)sender;
Reading and writing text layout orientation
A new text document attribute key, NSTextLayoutSectionsAttribute, is introduced for reading and writing the text layout orientation setting for each text view. The value is an array of dictionaries. Each dictionary specifies the text layout orientation for a range of string. Two keys, NSTextLayoutSectionOrientation and NSTextLayoutSectionRange, are provided for the dictionary content.I-Beam cursor for vertical text layout orientation
When the mouse pointer enters a text area with the vertical text layout orientation, the shape should be changed to an I-beam cursor that is 90 degrees rotated. NSCursor has a new factory method for the pointer type.+ (NSCursor *)IBeamCursorForVerticalLayout;
NSRulerView value translation
NSRulerView has a built-in facility for translating its client's coordinate system location to the ruler value. With the introduction of the vertical text layout orientation that rotates the bounds of NSTextView, the current logic is ambiguous for determining the intended axis for representing the ruler value. For example, with a vertical text view, a vertical ruler wants to translate the x coordinate value. In order to disambiguate the client view intention, NSRulerView now has an explicit value translation delegate methods.- (CGFloat)rulerView:(NSRulerView *)ruler locationForPoint:(NSPoint)aPoint;
- (NSPoint)rulerView:(NSRulerView *)ruler pointForLocation:(CGFloat)aLocation;
Integrated Quick Look support for NSTextView
NSTextView in OS X v10.7 has built-in Quick Look support for attachments. There are three new NSTextView APIs for working with the Quick Look preview panel.- (IBAction)toggleQuickLookPreviewPanel:(id)sender;In the meantime, NSTextViewDelegate has a new optional delegate method, -textView:URLForContentsOfTextAttachment:atIndex:, for managing the content URL for NSTextAttachment. This delegate method can enable both Quick Look and double-click action.
- (NSArray *)quickLookPreviewableItemsInRanges:(NSArray *)ranges;
- (void)updateQuickLookPreviewPanel;
- (NSURL *)textView:(NSTextView *)view URLForContentsOfTextAttachment:(NSTextAttachment *)attachment atIndex:(NSUInteger)charIndex;
NSLayoutManager glyph rendering
NSLayoutManager in OS X v10.7 deprecated the glyph rendering primitive, -showPackedGlyphs:length:glyphRange:atPoint:font:color:printingAdjustment:, and introduced the following new method taking a CGGlyph array.- (void)showCGGlyphs:(const CGGlyph *)glyphs-[NSLayoutManager drawGlyphsForGlyphRange:atPoint:] dynamically determines which primitive to invoke based on the instance method implementation. The new primitive method is invoked regardless of the framework linkage version unless:
positions:(const NSPoint *)positions
count:(NSUInteger)glyphCount
font:(NSFont *)font
matrix:(NSAffineTransform *)textMatrix
attributes:(NSDictionary *)attributes
inContext:(NSGraphicsContext *)graphicsContext;
- the instance has an overridden implementation of the old primitive and does not have an overridden implementation of the new primitive
- the NSLayoutManagerForcesShowPackedGlyphs preference value is true
Attributed Editing with NSTokenFieldCell
NSTokenFieldCell no longer forces -allowsEditingTextAttributes to return YES.Marked underline color rendering with NSTextView
When rendering NSMarkedClauseSegmentAttributeName, NSTextView no longer forces black NSUnderlineColorAttributeName. Since NSLayoutManager uses NSForegroundColorAttributeName in absence of NSUnderlineColorAttributeName, the NSMarkedClauseSegmentAttributeName matches the base color.Placeholder string in focused NSTextField
The placeholder string is now displayed even with focused NSTextField.NSSearchField
The search menu now can be popped up with the Command-Down Arrow key combination while the field has focus.NSSecureTextField
The caps lock and num lock indicators are now rendered with a color derived from the field's text color.Enhanced Text System Formatting controls in inspector bar
In place of the current formatting controls in the ruler accessory view, we're introducing an inspector bar containing text formatting controls much like iWork applications. This new option is available through -[NSTextView usesInspectorBar].NSRulerView mouse handling
NSRulerView now routes mouse events occurring in the marker area in addition to the ruler area to its clients via -rulerView:handleMouseDown:. It now sends both NSLeftMouseDown and NSRightMouseDown. It is the client view's responsibility to determine the right behavior.NSTextTab markers in ruler view
The right mouse down events reveal a context menu for choosing text tab type in OS X Lion.NSFontCollection
We introduced a dedicated class for representing font collections, NSFontCollection, previously accessed partly through NSFontManager. NSFontCollection is a bag of NSFontDescriptor. You can publicize the font collection as a named collection and presented through UI such as the font panel & Font Book.Notes specific to MacOS X 10.6
NSApplication presentationOptions API
New “presentationOptions” API on NSApplication provides a Cocoa replacement for the existing Carbon “SystemUIMode” API (whose usage was outlined in TN2062: "Guide to Creating Kiosks on OS X"). Using this API, an application can request certain behaviors for system user interface elements (the Dock, menu bar, task switcher, etc.) that will apply when the application is the active application, and can also observe changes to these UI element behaviors that are made by other applications when they are active.The supported set of options is expressed using a new NSApplicationPresentationOptions bitmask type declared in NSApplication.h:
/* Flags that comprise an application's presentationOptions */
enum {
NSApplicationPresentationDefault = 0,
NSApplicationPresentationAutoHideDock = (1 << 0), // Dock appears when moused to
NSApplicationPresentationHideDock = (1 << 1), // Dock is entirely unavailable
NSApplicationPresentationAutoHideMenuBar = (1 << 2), // Menu Bar appears when moused to
NSApplicationPresentationHideMenuBar = (1 << 3), // Menu Bar is entirely unavailable
NSApplicationPresentationDisableAppleMenu = (1 << 4), // all Apple menu items are disabledThis type is used by the following new NSApplication API methods.
NSApplicationPresentationDisableProcessSwitching = (1 << 5), // Cmd+Tab UI is disabled
NSApplicationPresentationDisableForceQuit = (1 << 6), // Cmd+Opt+Esc panel is disabled
NSApplicationPresentationDisableSessionTermination = (1 << 7), // PowerKey panel and Restart/Shut Down/Log Out disabled
NSApplicationPresentationDisableHideApplication = (1 << 8), // Application "Hide" menu item is disabled
NSApplicationPresentationDisableMenuBarTransparency = (1 << 9) // Menu Bar's transparent appearance is disabled
};
typedef NSUInteger NSApplicationPresentationOptions;
The following gets or sets the presentationOptions that should be in effect for the system when this application is the active application. Only certain combinations of NSApplicationPresentationOptions flags are allowed, as detailed in the AppKit Release Notes and the reference documentation for -setPresentationOptions:. When given an invalid combination of option flags, -setPresentationOptions: raises an exception.
- (NSApplicationPresentationOptions)presentationOptions;Returns the set of application presentation options that are currently in effect for the system. These are the presentation options that have been put into effect by the currently active application.
- (void)setPresentationOptions:(NSApplicationPresentationOptions)newOptions;
- (NSApplicationPresentationOptions)currentSystemPresentationOptions;For most applications, the initial value of presentationOptions is NSApplicationPresentationDefault. If an application’s Info.plist specifies a value for the LSUIElement key as described in TN2062, the application’s presentationOptions is initialized to an equivalent combination of NSApplicationPresentationOptions flags instead.
Both presentationOptions and currentSystemPresentationOptions are KVO-observable. A client that observes currentSystemPresentationOptions will receive notifications when (1) the client is the active application, and makes a change itself using either -setPresentationOptions: or SetSystemUIMode(), (2) another application is active, and makes such changes of its own, and (3) making a different application active causes the active set of presentation options to change. Notifications are not sent for non-changes (e.g. when a different application becomes active, but has the same set of presentation options as the previously active application).
The presentationOptions / currentSystemPresentationOptions API interoperates with the SystemUIMode API (both reflect the same underlying state). The combination of option flags that you pass to -setPresentationOptions: must satisfy the same set of requirements that apply to the SystemUI “Mode” and “Options” combinations as described in TN2062. Specifically:
NSApplicationPresentationAutoHideDock and NSApplicationPresentationHideDock are mutually exclusive: You may specify one or the other, but not both.
Likewise, NSApplicationPresentationAutoHideMenuBar and NSApplicationPresentationHideMenuBar are mutually exclusive: You may specify one or the other, but not both.
If you specify NSApplicationPresentationHideMenuBar, it must be accompanied by NSApplicationPresentationHideDock. If you specify NSApplicationPresentationAutoHideMenuBar, it must be accompanied by either NSApplicationPresentationHideDock or NSApplicationPresentationAutoHideDock.
If you specify any of NSApplicationPresentationDisableProcessSwitching, NSApplicationPresentationDisableForceQuit, NSApplicationPresentationDisableSessionTermination, or NSApplicationPresentationDisableMenuBarTransparency, it must be accompanied by either NSApplicationPresentationHideDock or NSApplicationPresentationAutoHideDock.
When -setPresentationOptions: receives a newOptions parameter that does not conform to these requirements, it raises an NSInvalidArgumentException.
NSApplicationPresentationOptions and FullScreen Mode
On 10.6, NSView's -enterFullScreenMode:withOptions: API accepts a new option, whose value specifies the NSApplicationPresentationOptions to use while the view is operating in full-screen mode:NSString *NSFullScreenModeApplicationPresentationOptions; // NSNumber numberWithUnsignedInteger:(NSApplicationPresentationOptions flags)Note that the presence or absence of this option affects whether entering full-screen mode for a view captures displays, and whether the NSFullScreenModeSetting option is permitted, as follows:
When the options dictionary you pass to -enterFullScreenMode:withOptions: does not contain a value for NSFullScreenModeApplicationPresentationOptions, AppKit does not alter the presentation options that were previously in effect when taking the view full-screen. AppKit also captures either the destination screen, or (if you specify YES for NSFullScreenModeAllScreens) all attached screens, consistent with the behavior on 10.5. ("Capturing" a screen takes exclusive control of it, in the same sense that is provided for by CGDisplayCapture() and related Core Graphics APIs.)
When the options dictionary you pass to -enterFullScreenMode:withOptions: does contain a value for NSFullScreenModeApplicationPresentationOptions, AppKit does not capture any displays, since doing so would prevent showing of presentationOptions-controlled UI elements such as the menu bar and Dock. (Because displays are not captured in this case, and the app therefore doesn’t hold exclusive ownership of them, the NSFullScreenModeSetting option is disallowed; specifying it when NSFullScreenModeApplicationPresentationOptions is also specified will cause -enterFullScreenMode:withOptions: to raise an exception.) AppKit puts the requested NSApplicationPresentationOptions value into effect when switching the view into full-screen mode, and will restore the previously active NSApplication presentationOptions setting when the view exits from full-screen mode via -exitFullScreenModeWithOptions:. Even if you don’t wish to change the NSApplication presentationOptions setting when entering full-screen mode, you can pass [NSApp presentationOptions] for the NSFullScreenModeApplicationPresentationOptions key as a means of preventing screen capture, if desired.
NSView -enterFullScreenMode:withOptions: API Changes
On 10.5, NSView’s -enterFullScreenMode:withOptions: method would throw an exception if the receiving view wasn’t in a window, as might be the case for an offscreen view that’s created exclusively for the purpose of being presented fullscreen. This was an unintentional limitation. On 10.6, -enterFullScreenMode:withOptions: can now be sent to a view for which [view window] == nil. For applications that must also run on 10.5, a simple workaround is to place the view in an offscreen dummy window.Implications of NSWindow and NSScreen Color Space support for NSViews
Most views draw into their window’s backing store, and are therefore drawn in their window’s assigned NSColorSpace. A few kinds of views are instead drawn into a separate backing store called a “surface” that’s composited atop the window, to facilitate hardware (GPU) accelerated rendering. This includes OpenGL views, as well as other kinds of views that render via OpenGL as a detail of their implementation (currently, views that display a Core Animation CALayer tree, QuickTime QTMovieViews, Quartz Composer QCViews, and ImageKit IKImageBrowserViews, among others).For surface-based views, AppKit sets the surface’s color space to match, and track changes to, the window’s color space. Applications should therefore look to NSWindow’s -setColorSpace: and -setDisplaysWhenScreenProfileChanges: APIs to determine the color space used for surface-based rendering in a given window.
Animation Support for Integer Values
On 10.6, integer-valued properties can now be animated using the CAAnimation-based “animator” proxy API. All integer types are supported: BOOL values as well as signed and unsigned variants of char, short, int, long, and long long may be animated. As with animating any custom property, be sure to associate a suitable CAAnimation with the property name key so that messaging through the target’s animator to set a new value will actually animate. For example, an NSImageView’s “imageFrameStyle” property can be configured to animate when changed through the ImageView’s animator like so:[imageView setAnimations:[NSDictionary dictionaryWithObject:[CABasicAnimation animation] forKey:@"imageFrameStyle"]];Any new value set through the animator will then trigger animation:
[imageView setImageFrameStyle:NSImageFramePhoto];As when animating integer-typed properties of Core Animation CALayers, the animation proceeds in discrete steps.
[[imageView animator] setImageFrameStyle:NSImageFrameButton];
Animation Support for NSColor Values
The 10.5 AppKit Release Notes incorrectly stated that NSColor-valued properties could be animated. While applications that happened to link against the Quartz framework had the benefit of this capability, it was not provided as an intrinsic AppKit feature.Support for animating NSColor-valued properties has been added to AppKit for 10.6, so the code example that was presented in the 10.5 AppKit Release Notes (under the heading “NSAnimatablePropertyContainer protocol”), will now work as intended on 10.6, irrespective of whether the application links against the Quartz framework.
@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
Animation Evaluation Scheduling Changes
On 10.5, NSView and NSWindow property animations that were scheduled through the view’s/window’s “animator”, and that were evaluated by AppKit (rather than being delegated to Core Animation for threaded asynchronous evaluation), were scheduled for updating in the NSDefaultRunLoopMode.On 10.6, such animations are now evaluated in NSRunLoopCommonModes.
“animator”-initiated animations that are evaluated by AppKit include:
- all NSWindow property animations
- all NSView property animations, for views that aren’t layer-backed
- a view’s frameSize, for a layer-backed view whose layerContentsRedrawPolicy (new in 10.6) equals the default value of NSViewLayerContentsRedrawDuringViewResize
- animation of any custom property that’s added by an NSView or NSWindow subclass, or of any existing NSView property that doesn’t readily map to a corresponding CALayer property (and therefore cannot be delegated to Core Animation for evaluation)
Changes to AppKit’s CATransaction Usage
AppKit’s NSAnimationContext “groupings” serve a very similar purpose to Core Animation’s CATransactions, and in fact invoking [NSAnimationContext beginGrouping] performs a [CATransaction begin] (in addition to doing other work), and invoking [NSAnimationContext endGrouping] performs a [CATransaction commit].On 10.5, AppKit also began and committed CATransactions in the course of its own layer-tree management activities, notably during certain modal event tracking operations, and to suppress implicit animations during non-animated view backing layer property updates.
On 10.6, we’ve removed this automatic CATransaction usage to avoid interfering with a client’s own CATransaction management -- in particular, so that changes a client application makes expecting an implicit CATransaction will not be unexpectedly delayed. AppKit instead makes very sparing use of [CATransaction flush], flushing only before it initiates an explicit layer tree draw, and on each cycle through a modal event tracking loop. This change makes client application usage of [CATransaction flush] more reliable as a means to force layer tree changes to be committed to the render tree. The general philosophy now is to leave creation of explicit CATransactions for the exclusive use of client applications.
Concurrent View Drawing
To help applications with especially high drawing loads to more effectively leverage multi-core hardware, while seeking to minimize adoption requirements for view code that conforms to AppKit’s established threading conventions, 10.6 introduces a simple model for concurrent view drawing, and accompanying control API. Our findings to date have indicated that concurrent view drawing is of benefit only in some circumstances, and applications may experience a net performance decrease in cases where parallelism gains do not outweigh the additional overhead involved, so use of concurrent view drawing should be accompanied by performance investigations to measure results. Forward-looking applications may want to consider the implications of the thread safety model, so as to make design decisions that are compatible with potentially leveraging this feature in the future.While the new model’s requirements for safely concurrent -- meaning threaded -- view drawing are comparatively simple (see “Concurrent View Drawing - Thread Safety Implications” below), existing UIs cannot be assured of 100% thread safety out of the box, so we provide an API mechanism to enable clients to opt in to concurrent view drawing when desired.
1. Concurrent view drawing is enabled by default for NSWindow instances, but disabled by default for all NSView instances within those windows. (We might in a future release decide to enable canDrawConcurrently by default for instances of particular generally thread-safe classes, such as simple controls.) From this initial state, clients can enable concurrent drawing for individual views, or use disabling at the NSWindow level as a master switch that can be thrown to suppress concurrent drawing in case problems precluding its use are encountered.
The view setting is not recursive in its effect; it specifies the threadability of drawing for a given view, not for the view’s entire subtree. The view’s descendants may have their threadability configured independently.
Our recommendation is that potential clients start by seeking out a handful of the more costly custom views in their UIs (typically those that draw complex content and/or cover large areas) and try enabling concurrent drawing for those. Additional views can be marked as safe for concurrent drawing to give the view drawing system further scheduling flexibility (this may be particularly worthwhile for descendants of the primary threadable views, due to implicit back-to-front drawing order dependencies), but switching this on for even a few of a UI’s “heavier” leaf views is often sufficient to reap the bulk of the potential performance benefit, where such benefit exists.
To the NSWindow class, we’ve added a Boolean “allowsConcurrentViewDrawing” property with the following accessor methods:
/* Reports whether threading of view drawing is enabled for this window. Defaults to YES.
*/
- (BOOL)allowsConcurrentViewDrawing;
/* Sets whether threading of view drawing should be enabled for this window. Defaults to YES. When this is set to YES,To the NSView class, we’ve added a “canDrawConcurrently” property with the following accessor methods:
AppKit's view system is allowed to perform -drawRect: activity for the window's views on threads other than the main thread,
for views that have canDrawConcurrently == YES. When this is set to NO, the window's views will be drawn serially as on 10.5
and earlier, even though some of the views may have canDrawConcurrently == YES.
*/
- (void)setAllowsConcurrentViewDrawing:(BOOL)flag;
/* Reports whether AppKit may invoke the view's -drawRect: method on a background thread, where it would otherwise be invoked
on the main thread. Defaults to NO.
*/
- (BOOL)canDrawConcurrently;
/* Sets whether AppKit may invoke the view's -drawRect: method on a background thread, where it would otherwise be invoked2. The -viewWillDraw recursion that happens at the beginning of a “-display...” pass will still be executed on the main thread, and will complete serially as before. This allows the potentially view-hierarchy-modifying on-demand layout activity for which -viewWillDraw is designed to complete in a safe and sane single-threaded environment, and is not expected to pose a significant limitation because -viewWillDraw activity is meant to be kept lightweight compared to drawing.
on the main thread. Defaults to NO for most kinds of views. May be set to YES to enable threaded drawing for a particular
view instance. The view's window must also have its "allowsConcurrentViewDrawing" property set to YES (the default) for
threading of view drawing to actually take place.
*/
- (void)setCanDrawConcurrently:(BOOL)flag;
3. Any view instance that has canDrawConcurrently == YES, in a window that allowsConcurrentViewDrawing, is eligible to have its drawing automatically delegated to a background thread/operation at AppKit’s discretion. AppKit sees to it that the concurrent view drawing operations it spawns have their completion serialized as needed, such that overlapping views (both siblings and ancestor/descendant chains) are rendered with correct results.
4. Servicing of normal view tree drawing (“-displayIfNeeded” activity) will still be initiated and managed by the main thread, which will seek to load-balance view drawing work by spawning background operations for certain eligible views. Decisions about which views to spin off to background operations may be based in part on view draw measurements, among other potential factors. The presence or absence of overlapping sibling views will influence the partitioning as well. The main thread will complete remaining view tree drawing, and then block on completion of any background view drawing operations it spawned before flushing the window and returning control to the runloop.
Concurrent View Drawing - Thread Safety Implications
The simplifying implications of this last point are important: AppKit must force a synchronization point before the window flush anyway, to allow for all contributed drawing into the window to complete before the flush, but this has the added benefit that the runloop on the main thread is guaranteed to be blocked during a drawing pass, including during any background-thread drawing of views in the window. So excepting side effects wherein the act of drawing a view may modify data it shares in common with another view that may be attempting to draw simultaneously on another thread (though hopefully relatively rare, this is the reason why both view- and window-level disabling API are provided), views that reference data outside themselves largely needn’t be any more concerned about the data changing out from under them than they have been with today’s primarily main-thread-based view drawing. If view drawing is happening, then model data that the views reference can be relied on not to be changed by event-handling and other main thread activity while that’s happening. Background-thread changes to the model will need to be coordinated with views on the main thread as before, using available mechanisms such as locks, notification queues, -performSelectorOnMainThread:..., or the like.Concurrent View Drawing - Testing Features and Additional Notes
To facilitate experimenting with this feature, some AppKit user defaults have been added. These are subject to changing or going away entirely, so as with other debug user defaults AppKit provides, do not rely on them in production code.“NSEnableConcurrentViewDrawing” can be set to NO to disable concurrent view drawing entirely for a process, reverting to Leopard-and-earlier view drawing behavior regardless of the allowsConcurrentViewDrawing and canDrawConcurrently settings on individual windows and views. It defaults to YES otherwise.
“NSConcurrentViewClasses” can be set to a comma-delimited list of view class names to force canDrawConcurrently to be set to YES for all instances of those classes, allowing for preliminary testing of this feature with existing built app executables. Note that the class test used here is intentionally an exact equality test, not an isKindOfClass: test -- so including “NSButton” in the list would not cause instances of subclasses of NSButton to be enabled for concurrent drawing. To force enabling of concurrent drawing for both NSButton and NSPopUpButton instances, one would need to include both those class names in the list.
“NSShowConcurrentViewDrawing” can be set to YES, which frames every concurrently drawn view in green for visual confirmation that concurrent view drawing is operating.
“NSDebugConcurrentViewDrawing” can be set to YES to enable some basic diagnostic console log output related to concurrent view drawing.
“NSConcurrentViewDrawingMinCores” specifies the minimum number of CPU cores that must be available on the host system for AppKit to actually attempt concurrent view drawing. AppKit compares this value to [[NSProcessInfo processInfo] activeProcessorCount]. The default value is 2.
An example of applying some of these defaults in launching TextEdit.app:
/Applications/TextEdit.app/Contents/MacOS/TextEdit -NSShowConcurrentViewDrawing YES -NSConcurrentViewClasses "NSScroller,NSPopUpButton,NSButton"To assist developers in assessing typical drawing costs of various views, AppKit accumulates basic timing stats for views’ -drawRect: invocations, which are now shown as part of the -_subtreeDescription debug info. Printing a window’s view subtree description in gdb as below shows these stats appended to the line for each view, reporting a minimum, mean (average), and maximum draw time in milliseconds. Draw times of several milliseconds, and especially more than 10ms or so, typically indicate a good potential candidate view for concurrent drawing. Draw times of less than a hundredth of a millisecond show up as 0.00ms. This particular example (TextEdit with an empty document window) does not show any particularly strong candidates for concurrent view drawing.
(gdb) po [[[[[NSApplication sharedApplication] windows] objectAtIndex:0] _borderView] _subtreeDescription]To provide more meaningful data, only full redraws of a view will contribute to these timing stats. Resizing a view’s window is a good way to prompt full redraws so as
[ A O P ] h=--- v=--- NSThemeFrame 0x185650 "Untitled" f=(0,0,475,442) b=(-) TIME drawRect: min/mean/max 0.62/0.62/0.62 ms
[ AF ] h=--- v=--- _NSThemeCloseWidget 0x15e840 "Button" f=(8,422,14,16) b=(-) TIME drawRect: min/mean/max 0.04/0.05/0.06 ms
[ AF ] h=--- v=--- _NSThemeWidget 0x15fd00 "Button" f=(50,422,14,16) b=(-) TIME drawRect: min/mean/max 0.06/0.07/0.08 ms
[ AF ] h=--- v=--- _NSThemeWidget 0x15fe50 "Button" f=(29,422,14,16) b=(-) TIME drawRect: min/mean/max 0.04/0.04/0.04 ms
[ A ] h=--- v=--- NSView 0x187840 f=(0,0,475,420) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ AF O ] h=-&- v=-&- ScalingScrollView 0x13c680 f=(0,0,475,420) b=(-) TIME drawRect: min/mean/max 0.03/1.39/2.75 ms
[ AF OGP ] h=--- v=--- NSClipView 0x17c6b0 f=(0,55,460,365) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ AF OG ] h=-&- v=--- NSTextView 0x19db90 f=(0,0,460,365) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ AF O ] h=--- v=--- NSScroller 0x1a2aa0 f=(460,55,15,350) b=(-) TIME drawRect: min/mean/max 0.06/0.07/0.07 ms
[ AF O ] h=--- v=--- NSScroller 0x14c770 f=(-100,-100,479,15) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ AF O ] h=--- v=--- NSRulerView 0x15cf00 f=(0,0,475,55) b=(-) TIME drawRect: min/mean/max 1.51/1.51/1.51 ms
[ P ] h=--- v=--- NSStopTouchingMeBox 0x15a680 "Title" f=(0,0,475,24) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ ] h=--- v=--- NSView 0x1aaa70 f=(0,0,475,24) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ P ] h=--- v=--- NSBox 0x17bfc0 "Title" f=(0,-1,388,24) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ A ] h=--- v=--- NSView 0x14c700 f=(0,0,388,24) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ AF ] h=--- v=--- NSPopUpButton 0x1a9360 "Spacing" f=(194,-1,85,22) b=(-) TIME drawRect: min/mean/max 0.18/0.19/0.21 ms
[ AF ] h=--- v=--- NSPopUpButton 0x158b80 "Styles" f=(2,-1,85,22) b=(-) TIME drawRect: min/mean/max 0.08/0.10/0.11 ms
[ AF ] h=--- v=--- NSPopUpButton 0x162520 "Lists" f=(279,-1,85,22) b=(-) TIME drawRect: min/mean/max 0.08/0.09/0.10 ms
[ AF ] h=--- v=--- NSSegmentedControl 0x1807f0 f=(88,-3,105,25) b=(-) TIME drawRect: min/mean/max 0.40/0.46/0.52 ms
[ P ] h=--- v=--- NSBox 0x164830 "Title" f=(388,-1,88,24) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ ] h=--- v=--- NSView 0x1595a0 f=(0,0,88,24) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ P ] h=--- v=--- NSBox 0x188980 "Title" f=(0,0,79,20) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ A ] h=--- v=--- NSView 0x169af0 f=(0,0,79,20) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ P ] h=&-- v=--- NSBox 0x1b6370 "Title" f=(0,0,79,20) b=(-) TIME drawRect: min/mean/max 0.23/0.23/0.23 ms
[ ] h=--- v=--- NSView 0x1673c0 f=(3,3,73,14) b=(-) TIME drawRect: min/mean/max 0.00/0.00/0.00 ms
[ A ] h=--- v=--- NSTabWell 0x180330 f=(4,1,15,13) b=(-) TIME drawRect: min/mean/max 0.02/0.02/0.02 ms
[ A ] h=--- v=--- NSTabWell 0x1847b0 f=(20,1,15,13) b=(-) TIME drawRect: min/mean/max 0.02/0.02/0.02 ms
[ A ] h=--- v=--- NSTabWell 0x1b8bf0 f=(37,1,15,13) b=(-) TIME drawRect: min/mean/max 0.02/0.02/0.02 ms
[ A ] h=--- v=--- NSTabWell 0x1a7a70 f=(54,1,15,13) b=(-) TIME drawRect: min/mean/max 0.01/0.01/0.01 ms
A=autoresizesSubviews, C=canDrawConcurrently, D=needsDisplay, F=flipped, G=gstate, H=hidden (h=by ancestor),
O=opaque, P=preservesContentDuringLiveResize, S=scaled/rotated, W=wantsLayer (w=ancestor wantsLayer), #=has surface
to gather draw time stats, for views that are not specifically optimized preserve content using the preservesContentDuringLiveResize mechanism.
Simple standard AppKit NSControls that don’t use dataSources, and don’t otherwise rely on data outside their own at draw time, are likely to be safe for canDrawConcurrently enabling, but have not all been formally vetted for concurrent drawing safety yet. For now, it is recommended that applications restrict enabling of canDrawConcurrently to their own custom view classes.
Layer-backed view drawing does not yet take advantage of the ability to draw views concurrently. This is a possible future direction.
NSView -setNeedsDisplay: and -setNeedsDisplayInRect: Activity During Drawing
One OS X v10.5, sending -setNeedsDisplay: or -setNeedsDisplayInRect: to a view might not produce the requested redraw if the view’s window is in the middle of a recursive view display operation when the request arrives. On 10.6, rectangles invalidated during display are accumulated in a side region that will be serviced in a subsequent window display cycle. This new behavior is on by default for applications linked on or after 10.6. For debugging purposes, it can be disabled by running with the “NSWindowsUseDeferredNeedsDisplayRegion” user default set to NO, or forced on by running with this user default set to YES. If you see a drawing glitch or drawing performance issue that goes away when running with “-NSWindowsUseDeferredNeedsDisplayRegion NO”, please file a Radar.NSView -viewWillDraw and Offscreen Drawing
On OS X v10.5, the newly introduced -viewWillDraw method was only invoked when views were drawn into their window. On 10.6, -viewWillDraw is also invoked when either -cacheDisplayInRect:toBitmapImageRep: or -displayRectIgnoringOpacity:inContext: is used to draw views to an alternate destination.10.6 also fixes an issue that sometimes prevented -viewWillDraw messages from being sent to views that were being printed.
Layer-Backed NSOpenGLViews and Pixel Formats
On 10.6, the layer-backed operating mode of an NSOpenGLView now inherits pixel format attributes from the view’s existing OpenGL context. If the view does not have an existing OpenGL context (e.g. because the view begins life in layer-backed mode, without having first operated in conventional view compositing mode), AppKit checks whether the view provides a -pixelFormat method. If so, AppKit invokes the view’s -pixelFormat method and examines the result to construct a compatible OpenGL pixel format and context for use in layer-backed mode.NSOpenGLLayer
OS X v10.6 introduces a new “NSOpenGLLayer” class, that supports fully generalized OpenGL usage in layer-backed mode.On OS X v10.5, AppKit supported OpenGL rendering in layer-backed mode only through the use of NSOpenGLView. When an app set wantsLayer to YES for an NSOpenGLView or one of its ancestor views, AppKit would automatically set up a suitable backing layer and OpenGL context for rendering, using the view’s existing, surface-backed OpenGL context (if any) as a “share” context (so that the same texture IDs and other OpenGL object references would carry over to layer-backed mode).
On OS X v10.6, the addition of NSOpenGLLayer makes it possible for any arbitrary NSView (not necessarily derived from NSOpenGLView) to render via OpenGL in layer-backed mode -- much as an NSOpenGLContext can be bound to any arbitrary NSView in non-layer-backed mode. The essential requirements for enabling an arbitrary view to render via OpenGL in layer-backed mode are as follows:
1. Subclass NSOpenGLLayer.
2. In your NSOpenGLLayer subclass, override -openGLPixelFormatForDisplayMask: to return an autoreleased NSOpenGLPixelFormat suitable for the specified set of displays and the content you want to render.
3. If you need your context to share OpenGL objects with another, existing context, you may also want to override -openGLContextForPixelFormat: so that you can specify the desired “share” context when creating the new context. (You should return an autoreleased NSOpenGLContext.) Note that you can ask an NSOpenGLLayer for its associated view, which may help you to obtain a pointer to the desired “share” context.
4. In your view class, override -makeBackingLayer to return an autoreleased instance of your NSOpenGLLayer subclass. For example:
- (CALayer *)makeBackingLayer {
return [[[CubeViewOpenGLLayer alloc] init] autorelease];
}
5. In your view class, override -setFrameSize: to make your layer’s openGLContext current and -update it, and to re-specify the OpenGL viewport and GL_MODELVIEW / GL_PROJECTION transforms as you would normally do when an OpenGL view is resized. You can use this same technique to retrieve the layer’s openGLContext anytime you need to make it current or message it for any other reason:- (void)setFrameSize:(NSSize)newSize {
[super setFrameSize:newSize];
NSOpenGLContext *openGLContext = [(NSOpenGLLayer *)[self layer] openGLContext];
[openGLContext makeCurrentContext];There are a number of additional issues involved in implementing a fully general OpenGL view -- one that can be switched into and out of layer-backed mode at will. The necessary techniques are likely to be illustrated by a future update to the LayerBackedOpenGLView code sample.
[openGLContext update];
[self reshape]; // Assume we've defined a -reshape method that calls glViewport()
// and updates the GL_PROJECTION and GL_MODELVIEW matrices.
}
Layer Contents Placement and Redraw Policy API
OS X v10.6 introduces new API on NSView, that clients can use to dramatically improve the performance of layer-backed view resize animations. The new NSView methods are accessors for two new properties: layerContentsRedrawPolicy and layerContentsPlacement.- (NSViewLayerContentsRedrawPolicy)layerContentsRedrawPolicy;
- (void)setLayerContentsRedrawPolicy:(NSViewLayerContentsRedrawPolicy)newPolicy;
- (NSViewLayerContentsPlacement)layerContentsPlacement;Prior to 10.6, AppKit did not provide any way for implementers or users of views to express how a view’s drawn content would be affected by resizing the view, and whether redrawing of the view’s content would be required. Without such information, AppKit has had to assume that a view’s content could change in arbitrary ways when the view was resized, and must be redrawn at each intermediate frame of an animated resize. In layer-backed mode, this forces AppKit to divert view resizing animations off of Core Animation’s asynchronous render+evaluation thread, and implement the resizing using timer-driven animation on AppKit’s main runloop -- yielding both reduced performance and sometimes visually unpleasant synchronization issues when AppKit-driven resize animations are combined with asynchronous, Core Animation-evaluated animations.
- (void)setLayerContentsPlacement:(NSViewLayerContentsPlacement)newPlacement;
Using this new API, the implementation of a view class, or code that uses instances of a particular view type, can control how resizing is handled when the view is layer-backed. The primary enabler for this is the new layerContentsRedrawPolicy property, which has the following declaration and allowed values:
enum {
NSViewLayerContentsRedrawNever = 0,
NSViewLayerContentsRedrawOnSetNeedsDisplay = 1,
NSViewLayerContentsRedrawDuringViewResize = 2,
NSViewLayerContentsRedrawBeforeViewResize = 3
};
typedef NSInteger NSViewLayerContentsRedrawPolicy;
The semantics of these modes are:NSViewLayerContentsRedrawNever—Leave the layer's contents alone. Never mark the layer as needing display, or draw the view's contents to the layer. This is the way AppKit treats developer-provided layers on both 10.5 and 10.6.
NSViewLayerContentsRedrawOnSetNeedsDisplay—Map view -setNeedsDisplay...: activity to the layer, and redraw affected layer parts by invoking the view's -drawRect:, but don't mark the view or layer as needing display when the view's size changes.
NSViewLayerContentsRedrawDuringViewResize—Resize the layer and redraw the view to the layer when the view's size changes. If the resize is animated, AppKit will drive the resize animation itself and will do this resize+redraw at each step of the animation. Affected parts of the layer will also be redrawn when the view is marked as needing display. (This mode is a superset of NSViewLayerContentsRedrawOnSetNeedsDisplay.) This is the way we treat AppKit-generated view backing layers today.
NSViewLayerContentsRedrawBeforeViewResize—Resize the layer and redraw the view to the layer when the view's size changes. This will be done just once at the beginning of a resize animation, not at each frame of the animation. Affected parts of the layer will also be redrawn when the view is marked as needing display. (This mode is a superset of NSViewLayerContentsRedrawOnSetNeedsDisplay.)
For a view that has no associated layer, or that has been assigned a developer-provided layer using NSView’s -setLayer: API, the default layerContentsRedrawPolicy is NSViewLayerContentsRedrawNever, with an accompanying layerContentsPlacement of NSViewLayerContentsPlacementScaleAxesIndependently. This instructs AppKit that it is not allowed to replace the layer’s content, and provides the same content placement as CALayer’s default contentsGravity setting of kCAGravityResize.
For a view that has acquired an AppKit-generated backing layer, AppKit sets the view’s layerContentsRedrawPolicy to a default of NSViewLayerContentsRedrawDuringViewResize, forcing the view’s content to be continually redrawn into the view’s backing layer during animated resizing of the view, which produces strictly correct but not optimally performant results.
If you know that redrawing at each animation frame is not necessary to produce correctly rendered results for a particular view, or are willing to accept an approximation of the view’s intermediate appearance during potentially brief animations in exchange for an animation performance and smoothness benefit, you can change the view’s layerContentsRedrawPolicy to one of the other supported values. When doing this, you should also specify the desired layerContentsPlacement for the view. The layerContentsPlacement determines how the backing layer’s existing cached content image will be mapped into the layer as the layer is resized. (It is analogous to, and underpinned by, CALayer’s contentsGravity property, as indicated by the comments below.)
The provided layerContentsPlacement values are as follows:
enum {
NSViewLayerContentsPlacementScaleAxesIndependently = 0, // equivalent to kCAGravityResize
NSViewLayerContentsPlacementScaleProportionallyToFit = 1, // equivalent to kCAGravityResizeAspect
NSViewLayerContentsPlacementScaleProportionallyToFill = 2, // equivalent to kCAGravityResizeAspectFill
NSViewLayerContentsPlacementCenter = 3,
NSViewLayerContentsPlacementTop = 4,
NSViewLayerContentsPlacementTopRight = 5,
NSViewLayerContentsPlacementRight = 6,
NSViewLayerContentsPlacementBottomRight = 7,
NSViewLayerContentsPlacementBottom = 8,
NSViewLayerContentsPlacementBottomLeft = 9,
NSViewLayerContentsPlacementLeft = 10,
NSViewLayerContentsPlacementTopLeft = 11
};
typedef NSInteger NSViewLayerContentsPlacement;
The first three values specify that the layer’s existing content should be displayed scaled in one of three supported ways when the layer is resized, while being kept centered within the layer’s bounds. The remaining nine values specify that instead of being scaled, the layer’s contents should be displayed at their existing size, and pinned to one of nine canonical positions relative to the layer’s new frame.NSOpenGLView Minor Method Behavior Changes
On OS X v10.5 and earlier, invoking NSOpenGLView’s -setPixelFormat: method with the view’s current pixelFormat object as its parameter could cause the pixelFormat to be deallocated, if nothing else had retained it. This has been fixed on 10.6.The same applied to NSOpenGLView’s -setOpenGLContext: method and its context parameter. This has also been fixed on 10.6. Additionally, on 10.5 and earlier -setOpenGLContext: would invoke -clearGLContext, even when the given context was the same as the view’s existing openGLContext. To reduce the risk of compatibility breakage, this behavior has been preserved on 10.6, for application binaries that were linked on 10.5 and earlier. Applications linked on 10.6 and later will only see -setOpenGLContext: invoke -clearGLContext when the view is actually being switched to a different context.
New NSOpenGL-CGL Bridging Methods
To provide improved interoperability between the NSOpenGL and CGL APIs, OS X v10.6 completes the set of CGL-typed initializer and getter methods provided by the NSOpenGLContext, NSOpenGLPixelBuffer, and NSOpenGLPixelFormat classes. Using these methods, one can initialize an NSOpenGL object to wrap an object of the corresponding CGL type, and/or get the CGL object that’s wrapped by a corresponding NSOpenGL object.Continuing the convention we've followed of avoiding explicit use of CGL argument types in AppKit API, the arguments and return values are typed as void *, and must be cast by clients to the appropriate underlying type (CGLContextObj, CGLPBufferObj, or CGLPixelFormatObj) when necessary. Also following our existing convention, the -init... and getter methods listed here include the names of the underlying CGL types: CGLContextObj, CGLPixelFormatObj, and CGLPBufferObj. The CGL objects are retained by their owning NSOpenGL wrapper for the NSOpenGL wrapper's lifetime.
@interface NSOpenGLContext
...
- (id)initWithCGLContextObj:(void *)context;
- (void *)CGLContextObj; // 10.3 and later
...
@end
@interface NSOpenGLPixelBuffer
...
- (id)initWithCGLPBufferObj:(void *)pbuffer;
- (void *)CGLPBufferObj;
...
@end
@interface NSOpenGLPixelFormat
...
- (id)initWithCGLPixelFormatObj:(void *)format;
- (void *)CGLPixelFormatObj; // 10.3 and later
...
@end
Layer Tree Rendering and Modal Windows
An issue that could cause flicker when using Core Animation layer tree rendering in a modal window has been fixed.Printing Layer-Backed Views
On OS X v10.5 through 10.5.2, attempting to print layer-backed views would produce empty output. This has been fixed for 10.5.3 and on 10.6, such that printing now proceeds in the same way as if the views were not layer-backed. (The views are drawn into the printing context.)AppKit does not currently provide automatic printing support for standalone, developer provided layer trees. To generate printed output in such cases, it is necessary to use Core Animation’s CARenderer API together with OpenGL to render to a bitmapped image, and then print the resultant image.
Live toolbar layout during customization
Prior to SnowLeopard, toolbars would not show the actual layout produced while rearranging, adding, or removing toolbar items. In SnowLeopard, toolbars should show their correct layout even during customization.Toolbars no longer validate for some eventsAs an optimization, NSToolbar no longer validates for the following events: NSLeftMouseDragged, NSRightMouseDragged, NSOtherMouseDragged, NSMouseEntered, NSMouseExited, NSScrollWheel, NSCursorUpdate, NSKeyDown. In addition, validation for NSKeyUp and NSFlagsChanged events is deferred for up to .85 seconds in SnowLeopard, with the timer restarting for every new deferrable event. So a sequence of key events will not trigger any validation at all, until either a pause of .85 seconds, or an event other than NSKeyUp or NSFlagsChanged is processed.
To trigger validation for a single toolbar manually, call -[NSToolbar validateVisibleItems]. To trigger validation for all toolbars, call [NSApp setWindowsNeedUpdate:YES]
Flipped images in toolbars
A common but incorrect practice is to call setFlipped:YES on an image that you plan to draw into a flipped graphics context. This is incorrect because if the image is later drawn into a normal unflipped context, the image will appear upside down. However, Leopard and earlier contained a bug in which images set on NSToolbarItem (via setImage:) would ignore their isFlipped property. SnowLeopard fixes this bug for apps compiled on SnowLeopard with the 10.6 SDK; as a result, some images which should have drawn upside down in Leopard, will begin doing so when your app is recompiled.If you recompile your app on SnowLeopard and discover that your toolbar item images are drawing upside down, then it indicates that you are calling setFlipped:YES somewhere within your code. You should remove those calls and replace the image drawing with methods that correctly handle flipped contexts. See the discussion of setFlipped: in these Release Notes for more discussion.
Spell checkers must be in a Services directory
In SnowLeopard, spell checkers will only be recognized if they are in one of the standard Services directory (/Library/Services, ~/Library/Services, or /System/Library/Services). This is to prevent compatibility problems with spell checkers from previous versions of the OS.setuid/setgid apps disallowed
As a security measure, SnowLeopard takes steps to prevent applications that use AppKit from running setuid or setgid. If AppKit detects that it is running issetugid(), the following will happen:Under 64 bit, it will log a message and then exit(EXIT_FAILURE).
Under 32 bit, it will give the user a chance to authenticate as an administrator. If the attempt succeeds, the app will run as normal; if the user fails to authenticate, or cancels, it will exit(EXIT_FAILURE). If the attempt fails because the authentication dialog could not be shown, then it will perform a linked on or after check. Apps linked before SnowLeopard will be allowed to run; applications linked on or after SnowLeopard will be exited.
This only affects applications that have the setuid or setgid Unix permission bit set, or apps that inherit this bit from a fork() of a setugid app. This does not affect applications run via sudo, su, or normally as root.
Tool tips on NSTabViewItem
NSTabViewItem now supports custom tool tips on the tabs themselves. See the NSTabViewItem.h header for more information.Underscore treated like minus key
Starting with Leopard, AppKit began interpreting ⌘= as ⌘+ if no menu item claims the key equivalent ⌘+ and the pressed = key also has a + on it. In SnowLeopard, this support has been extended to interpret ⌘_ as ⌘-, if the pressed key has both _ and -.New NSWorkspace APIs
NSWorkspace now contains APIs for gathering information about file labels, and for changing the desktop background. NSWorkspace also contains new APIs for duplicating files and moving them to the trash, which are more powerful and convenient than the older performFileOperation:. There are additional APIs not mentioned in these notes. See the NSWorkspace class header for more information.NSWorkspace now posts notifications when apps are hidden, unhidden, activated, or deactivated. It also posts notifications when the name or mount location of a volume is changed. Lastly, there is a notification for when the available file colors or labels changes. The new app notifications fire for all apps, compared to the existing notifications which fire for only foreground apps.
NSRunningApplication
SnowLeopard includes a new class, NSRunningApplication, for inspecting and manipulating running applications on the system. See the NSRunningApplication header in AppKit for more information.NSApplication setHelpMenu:
The new setHelpMenu: API on NSApplication allows apps to determine which menu gets the Spotlight for Help search field. The default Main Menu nib, when created on SnowLeopard, will automatically set the Help menu in the same manner as the Window menu.NSMenu validation
The NSMenu method -performActionForItemAtIndex: no longer triggers menu validation. This is because validation is typically done during menu tracking or key equivalent matching, so the subsequent performActionForItemAtIndex: validation was redundant. To trigger validation explicitly, use the -[NSMenu update] method.NSMenu Function Key Equivalents
NSF16FunctionKey through NSF19FunctionKey are now available as key equivalents in menu items.NSMenu performActionForItemAtIndex: highlighting and accessibility
The NSMenu method performActionForItemAtIndex:, when called, now triggers highlighting in the menu bar. It also sends out appropriate accessibility notifications indicating the item was selected.NSMenu popUpMenuPositioningItem: atLocation: inView:
NSMenu has a new method for popping up a menu as if it were a popup button. This is meant to be an replacement for the Carbon function PopUpMenuSelect(). See the NSMenu.h header for more information.NSMenu confinementRectForMenu:
NSMenu has a new delegate method confinementRectForMenu: onScreen: to allow some control over where a menu may pop up. See the NSMenu.h header for more information.NSPopUpButtonCell supports NSImageOnly
Popup buttons now support the NSImageOnly image position, which prevents the title from showing in the control.NSMenu key equivalent matching is one per event
In Leopard, the main menu may be queried for key equivalent matches multiple times within a particular event's lifecycle, if no menu item is found to match. This was a performance problem because a menus would be populated multiple times for a single event. In SnowLeopard, we only populate and search the menu once per key event.It is possible, though unlikely, that apps depended on the old behavior. For example, if an override of -[NSApplication sendEvent:] changed key equivalents so that they match the event, then these key equivalents would no longer work in SnowLeopard. For this reason, the user default "NSAllowMultipleKeyEquivalentSearchesPerEvent" is available to restore the Leopard behavior on SnowLeopard.
Nibless apps and the application menu
In Leopard and earlier, apps that tried to construct a menu bar without a nib would get an undesirable stubby application menu that could not be removed. To work around this problem on Leopard, you can call the undocumented setAppleMenu: method and pass it the application menu, like so:[NSApp setAppleMenu:[[[NSApp mainMenu] itemAtIndex:0] submenu]];In SnowLeopard, this workaround is unnecessary and should not be used. Under SnowLeopard, the first menu is always identified as the application menu.
NSApplication application menu item title
In Leopard, the first item in the main menu (the application menu) would have its title modified to the empty string. In SnowLeopard, this is only done for apps linked on Leopard or earlier: new apps will not see this title modified. The title of the this menu item is ignored for display purposes - the application menu always reflects the application name.Main menu items no longer specially retained during tracking
In Leopard, every menu item contained directly or indirectly within the main menu would be retained during menu tracking. This was an inefficiency, but some apps would message menu items after removing them within menuNeedsUpdate:. In SnowLeopard, menu items within the main menu are retained across calls to menuNeedsUpdate:, and then only for 32 bit apps compiled on Leopard or earlier. You should not depend on menu items being retained after they are removed from the main menu.Services Menu visual changes
The Services menu now categories Services based on their send and receive types, and shows icons for Services. The Services menu is also flattened - no more submenus. When updating your Service for SnowLeopard, make sure your Service has an icon and a descriptive title that makes it easy to identify your Service in the flattened menu. Also consider how your Service can be made contextual, as discussed under Services Contextuality. See the SysServices guide for more information.Services Preferences
SnowLeopard allows the users to enable or disable individual Services. The preference pane is accessible from the Services Preferences menu item within the Services menu, and also via System Preferences.Services Contextuality
In SnowLeopard, Services can appear only in certain contexts. For example, the Open URL Service appears only if the selected text contains a URL, the Reveal in Finder Service appears only if a file path is selected, and the Set Desktop Picture Service appears only if images are selected in Finder. The contextuality of a Service is specified within its Info.plist. For the full list of ways a Service can be made contextual, see the System Services guide in the developer documentation.Non-contextual Services disabled by default
In SnowLeopard, Services that are not contextual are disabled by default. They can be reenabled within the Services Preferences. Because the contextual Services feature is new in SnowLeoaprd, all existing non-Apple Services are disabled by default.Services that are updated for SnowLeopard will appear enabled by default again. A Service indicates it is updated for SnowLeopard by adding the NSRequiredContext key to its Info.plist, as documented in the System Services guide. Services that cannot limit themselves to certain contexts, but still want to be enabled for SnowLeopard, can add an empty NSRequiredContext and will appear by default.
Services in context menus
In SnowLeopard, Services may appear in context menus. Context menus will generally show a subset of those Services available in the Services menu.The following Services will not show in context menus, even if they appear in the Services menu:
- Services that already have equivalent normal menu items in the context menu, such as Open URL or Look Up in Dictionary.
- Services that have neither a send or receive type.
- Services that will not operate on the selection (have no send type) when a selection is present. For example, Capture Selection from Screen does not appear in context menus if there is selected text.
- A fixed list of Services that cannot be made contextual and would appear everywhere, such as Make New Sticky Note or New Email With Attachment.
All other Services that appear in the Services menu should also appear in context menus.
Services can be Automator workflows
In SnowLeopard, you can create a Service from an Automator workflow. To create an Automator workflow Service, start Automator and choose Service from the list of starting points. Workflow Services are first class citizens: they can accept and/or return data, and be made contextual via the same mechanism as regular Services.Workflow Services must be in one of the Library/Services directories to be recognized. These Service directories are live: adding or removing a Workflow Service from ~/Library/Services/ or /Library/Services/ will cause the Services menu to be updated immediately.
Fetching the cursor set by any application
SnowLeopard adds an NSCursor method that returns a cursor whose image and hot spot match those of the currently-displayed cursor on the system, regardless of which application set that cursor, and regardless of whether Cocoa or Carbon APIs were used to set it: +[NSCursor currentSystemCursor]This replaces the deprecated QDGetCursorData API.
(The existing +[NSCursor currentCursor] method only returns the cursor set by your application via NSCursor methods, but not cursors set by other applications or cursors set by your application using Carbon API.)
Displaying a Finder search results window
SnowLeopard adds an NSWorkspace method that displays a search results window in Finder for a specific query string: -[NSWorkspace showSearchResultsForQueryString:]This is the programmatic equivalent of the user switching to Finder, creating a new window, and typing the search string into the search field.
This effectively replaces Carbon's HISearchWindowShow API.
NSEvent additions for event monitoring
There are new NSEvent class methods for monitoring events just in your own application, or events in all applications:+ (id)addGlobalMonitorForEventsMatchingMask:(NSEventMask)mask handler:(void (^)(NSEvent *))block;+addLocalMonitorForEventsMatchingMask allows monitoring and modification of all events that are dispatched via -[NSApp sendEvent:]. +addGlobalMonitorForEventsMatchingMask allows monitoring (but not modification) of user input events dispatched to other applications, such as mouse events and keyboard events.
+ (id)addLocalMonitorForEventsMatchingMask:(NSEventMask)mask handler:(NSEvent *(^)(NSEvent *))block;
+ (void)removeMonitor:(id)eventMonitor;
Customizing Spotlight for Help
We introduced an API in AppKit to let developers implement searching their own custom Help Data. In general, users find the Help search functionality very useful. However, many large applications don't use Apple Help API because of cross platform requirements. Hence, important Help topics are not presented as part of the Help menu. The new API will allow developers to incorporate their own Help topics and take full advantage of the Help feature.In your application you implement the NSUserInterfaceItemSearching protocol and then register your object with -[NSApplication registerUserInterfaceItemSearchHandler:]. See NSUserInterfaceItemSearching.h for API.
NSObjects implements awakeFromNib
On OS X v10.6 and later, NSObject provides an implementation of awakeFromNib. This means that you can safely call through to [super awakeFromNib] in an overridden implementation when running on OS X v10.6 and later.Advice for People who Are Looking for -viewWillLoad and -viewDidLoad Methods in NSViewController
Even though NSWindowController has -windowWillLoad and -windowDidLoad methods for you to override the NSViewController class introduced in Mac OS 10.5 does not have corresponding -viewWillLoad and -viewDidLoad methods. You can override -[NSViewController loadView] to customize what happens immediately before or immediately after nib loading done by a view controller.New Support for Concurrent Document Opening in NSDocument
NSDocument now has the ability to read documents concurrently, using background threads. A new class method has been added to NSDocument to give you control over this:+ (BOOL)canConcurrentlyReadDocumentsOfType:(NSString *)typeName;Return whether instances of the receiving class can concurrently read documents of the specified type. The default implementation of this method returns NO. You can override it to return YES to enable concurrent opening of documents but you must make sure your document reading code can be safely executed concurrently, in non-main threads.
Note that NSUndoManager's mechanism for automatically creating one group per UI event doesn't coexist very well with NSUndoManager usage on non-main threads. You should disable undo registration during document reading, which is a good idea even in the absence of concurrency. See for example SKTDocument.m in /Developer/Examples/Sketch.
Note that code executed during the opening of a document triggered by an Apple event will not be able to get the current Apple event, because the event is suspended until all documents are read, to enable correct reporting of success or failure to the Apple event sender. If, for example, you are checking the current Apple event for a search string, you should not enable concurrent document opening unless you have another solution for getting the search string.
Advice for Overriders of -[NSDocumentController openDocumentWithContentsOfURL:display:error:]
Since the introduction of -[NSDocumentController openDocumentWithContentsOfURL:display:error:] in Mac OS 10.4 it has been easy to mask bugs, in either your application's code or its Info.plist file, that cause -[NSDocumentController typeForContentsOfURL:error:], also introduced in Mac OS 10.4, to return nil and an error inappropriately. The default implementation of -openDocumentWithContentsOfURL:display:error: was the only place in AppKit from which -typeForContentsOfURL:error: was frequently invoked so if you overrode the former method and did not invoke super or the latter method in your override you would not notice such bugs. With the introduction of support for concurrent document opening in Mac OS 10.6 there are now other places in AppKit from which -typeForContentsOfURL:error: is invoked. For backward binary compatibility with Mac OS 10.5 and earlier this is done only in applications linked against Mac OS 10.6. If you notice new document opening problems in your application when you start linking it against the Mac OS 10.6 SDK you might want to start debugging by seeing what -typeForContentsOfURL:error: is returning.Advice for Overriders of -[NSDocument close]
Merely overriding -[NSDocument close] is usually not sufficient for many purposes because it's often not invoked at application termination time. This is for performance and there are no plans to change this in the future. It's best to avoid requiring the sort of resource cleanup that must be done at document closing time but, if it's unavoidable in your application, try initiating such cleanup for documents that are still open in your application delegate's -applicationWillTerminate: method in addition to an override of -[NSDocument close]. Both -[NSDocument close] overrides and -applicationWillTerminate: application delegate methods are by the way at odds with the new "Fast Killing of Applications" mechanism that is described in the Foundation release notes and which we would like you to adopt.New Opportunity for Customization of Save Panels Presented by NSDocument
In earlier versions of OS X there was no way for a subclass of NSDocument to customize the initial value of the file name field in presented save panels, not even by overriding -[NSDocument prepareSavePanel]. In Mac OS 10.6 NSDocument now uses the NSSavePanel methods added in Mac OS 10.6 so it can allow this kind of customization. If you override -prepareSavePanel and send a -setNameFieldStringValue: message to the save panel NSDocument will not overwrite the value that's set. It also doesn't overwrite any value set by sending -setDirectoryURL: to the save panel in your override of -[NSDocument prepareSavePanel].New Support for "Save As PDF…" in NSDocument Printing
-[NSDocument printDocumentWithSettings:showPrintPanel:delegate:didPrintSelector:contextInfo:] has a new behavior in Mac OS 10.6: if the passed-in print settings dictionary has an NSPrintJobDisposition entry whose value is NSPrintSaveJob, indicating that the printed pages should be written to a PDF file, but no NSPrintJobSavingURL (new) or NSPrintSavePath (old, deprecated) entry indicating where the PDF file should be written, then NSDocument will present a save panel asking the user where the PDF file should be saved. See TextEdit's -[Document saveDocumentAsPDFTo:] for an example of using this.Bug Fixes in NSDocumentController
In earlier versions of OS X -[NSDocumentController documents] simply returned a pointer to its internal array of documents, which could change as documents were opened or closed, requiring invokers to make a copy to enumerate while closing documents. This was a bug and has been fixed in Mac OS 10.6. A copy of the documents array (autoreleased of course) is now returned.In Mac OS 10.5 -[NSDocument displayNameForType:], when passed a UTI for a type that conforms to another type for which the application also has a CFBundleDocumentTypes entry, could return the display name for that other type. This was a bug and has been fixed in Mac OS 10.6.
In Mac OS 10.5 -[NSDocument displayNameForType:], when passed a UTI for which the application has a CFBundleDocumentTypes entry that includes a CFBundleTypeName subentry, would return nil if there was no corresponding InfoPlist.strings entry. This was a bug and has been fixed in Mac OS 10.6. Now the unlocalized value of the CFBundleTypeName entry is returned if there is no corresponding InfoPlist.strings entry.
In Mac OS 10.5 -[NSDocument typeFromFileExtension:] would return unpredictable values when passed nil. This meant that -[NSDocumentController typeForContentsOfURL:error:] could also return unpredictable values, in apps in which not all of the Info.plist CFBundleDocumentTypes entries have LSItemContentTypes entries. (See the description of how -typeForContentsOfURL:error: works nowadays in the "Support for UTIs in NSDocumentController" section of the AppKit release notes for Mac OS 10.5.) This was a bug and has been fixed in Mac OS 10.6.
New Support for URLs and Good Error Reporting in NSFileWrapper
NSFileWrapper has been modernized in Mac OS 10.6 and now deals in URLs instead of paths, and reports errors using NSError. These old methods have been deprecated:- (id)initWithPath:(NSString *)path;These new methods have been published:
- (id)initSymbolicLinkWithDestination:(NSString *)path;
- (NSString *)symbolicLinkDestination;
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)atomicFlag updateFilenames:(BOOL)updateFilenamesFlag;
- (BOOL)needsToBeUpdatedFromPath:(NSString *)path;
- (BOOL)updateFromPath:(NSString *)path;
- (NSString *)addFileWithPath:(NSString *)path;
- (NSString *)addSymbolicLinkWithDestination:(NSString *)path preferredFilename:(NSString *)filename;
- (id)initWithURL:(NSURL *)url options:(NSFileWrapperReadingOptions)options error:(NSError **)outError;(We're not publishing new NSURL/NSError-using replacements for -addFileWithPath: and -addSymbolicLinkWithDestination: because the new methods would not actually be necessary and the old methods were so unpopular.)
- (id)initSymbolicLinkWithDestinationURL:(NSURL *)url;
- (NSURL *)symbolicLinkDestinationURL;
- (BOOL)writeToURL:(NSURL *)url options:(NSFileWrapperWritingOptions)options originalContentsURL:(NSURL *)originalContentsURL error:(NSError **)outError;
- (BOOL)matchesContentsOfURL:(NSURL *)url;
- (BOOL)readFromURL:(NSURL *)url options:(NSFileWrapperReadingOptions)options error:(NSError **)outError;
New NSFileWrapperWritingAtomic and NSFileWrapperWritingWithNameUpdating options have been published for use with -writeToURL:options:originalContentsURL:error:. They correspond to the atomically: and updateFilenames: parameters, respectively, of -writeToFile:atomically:updateFilenames:, with the exception that NSFileWrapperWritingWithNameUpdating, unlike updateFilenames:YES, only causes -setFilename: to be sent to child file wrappers, not the receiver itself.
See the comments in <AppKit/NSFileWrapper.h> for more information.
New Support for Incremental Writing in NSFileWrapper
The new -writeToURL:options:originalContentsURL:error: method mentioned above differs from the now-deprecated -writeToFile:atomically:updateFilenames: method in that in has an additional parameter in which you can pass a URL that locates the previous revision of a file package being saved. When the value of that parameter is not nil NSFileWrapper attempts to avoid unnecessary I/O by merely writing hard links to files instead of actually writing out their contents.New Reading Options in NSFileWrapper
The new -initWithURL:options:error: and -readFromURL:options:error: messages mentioned above have options: parameters. The possible options are:NSFileWrapperReadingImmediate - This causes descendent file wrappers to be instantiated and their contents and attributes read immediately. It's good to use this when your application uses NSFileWrapper to represent new attachments.
NSFileWrapperReadingWithoutMapping - This causes NSFileWrapper to never use file mapping for regular file contents. NSFileWrapper does a good job of deciding when to use file mapping instead of nonlazily reading the entire contents of files into memory but you can use this if necessary to make sure your application is particularly robust against the user doing things like unplugging external hard disks without ejecting them first.
New Support for Preserving Metadata in NSFileWrapper
In earlier versions of OS X the only file attributes that NSFileWrapper ever attempted to preserve when writing were the file modification date and the POSIX permissions. In Mac OS 10.6 it now reads and writes these attributes:• Creation and modification dates
• File name extension hiding
• HFS creator and file type codes
• POSIX permissions
• Extended attributes
• The resource fork
Because NSFileWrapper deals in the same kind of attribute dictionaries as NSFileManager, you can specify the values of the first four kinds of attribute by invoking -setFileAttributes:, passing a dictionary with values for these keys:
• NSFileCreationDate and NSFileModificationDate
• NSFileExtensionHidden
• NSFileHFSCreatorCode and NSFileHFSTypeCode
• NSFilePosixPermissions
Be careful when using -setFileAttributes:. It doesn't add to the receiver's set of file attributes, it completely replaces it.
Bug Fixes in NSFileWrapper
In earlier versions of OS X NSFileWrapper did not take into account the fact that two items whose names differ only by case can't both be written to the same directory in most file systems without one overwriting the other. This bug has been fixed in Mac OS 10.6. -[NSFileWrapper addFileWrapper:] for example will now create a new unique file name for a child whose preferred file name is @"TestyMCTest.test" if there is already a child whose unique file name is @"TestyMcTest.test".In earlier versions of OS X sending -writeToFile:atomically:updateFilenames: to an NSFileWrapper would do three things for updateFilenames:YES:
• Send -setFilename: to the receiver and its child file wrappers.
• Reread the file attributes of the receiver and its child wrappers from the file system items just written.
• Remap the contents of any regular file wrappers to the corresponding files just written.
The last two things were not right and in Mac OS 10.6 they are no longer done. The remapping of regular file contents in particular caused trouble with applications holding files open at surprising times (memory mapping a file effectively opens it). updateFilenames:YES now just causes -setFilename: to be sent to the receiver and its child file wrappers.
New Cursor Invalidation in -[NSSplitView adjustSubviews]
In earlier versions of OS X -[NSSplitView adjustSubviews] did not invalidate the split view's cursor. This made it difficult to correctly animate divider positioning by simply sending -setFrameSize: to the results of sending -animator to the two subviews on either side of the divider and letting -adjustSubviews get invoked repeatedly during the animation. In Mac OS 10.6 -[NSSplitView adjustSubviews] now invokes [[self window] invalidateCursorRectsForView:self] so the cursor over the divider is always correct after such an animation.Bug Fix in -[NSSplitView minPossiblePositionOfDividerAtIndex:]
Mac OS 10.5 added a new -minPossiblePositionOfDividerAtIndex: method to NSSplitView as well as the ability to hide dividers. -minPossiblePositionOfDividerAtIndex: when passed a divider index of 0 is supposed to return zero minus the divider thickness when that divider is hidable. (Conceptually the user can drag the topmost or leftmost divider right off the top or left edge of a split view if it is hidable, and the position of a divider is its top or left edge.) In Mac OS 10.5 it instead returned zero when the subview above or to the left of the divider was collapsed. This was a bug and has been fixed in Mac OS 10.6.Change In Invocation of NSSplitView Delegate Constraint Methods For Hidden Dividers
In Mac OS 10.5 an NSSplitView would pass the exact same value returned by -minPossiblePositionOfDividerAtIndex: or -maxPossiblePositionOfDividerAtIndex: when sending -splitView:constrainMinCoordinate:ofSubviewAt: or -splitView:constrainMaxCoordinate:ofSubviewAt:, respectively, to the split view's delegate. Because that value takes into account the fact that a hidden divider is effectively draggable off the edge of the split view it would be surprisingly small or large, respectively, for hidable dividers. This made it difficult to specify the minimum sizes of subviews using the relatively simple technique described in <AppKit/NSSplitView.h>. This has been changed in Mac OS 10.6. The value passed to -splitView:constrainMinCoordinate:ofSubviewAt: or -splitView:constrainMaxCoordinate:ofSubviewAt: is now the same regardless of whether the relevant divider is hidable. For backward binary compatibility this is only done in applications linked against Mac OS 10.6 or later.NSPrintInfo Improvements
To let you easily take advantage of NSPrintInfo's KVO-compliance (without having to observe the NSPrintInfo's attributes dictionary), accessor methods have been published for the print scaling attribute:- (void)setScalingFactor:(CGFloat)scalingFactor;NSPrintInfo is now as KVO-compliant for "scalingFactor" as it is for "paperSize," "orientation," etc.
- (CGFloat)scalingFactor;
Because we're standardizing on NSURL as the representation of locations of files in Mac OS 10.6, a new key into the attributes dictionary has been published:
NSString *NSPrintJobSavingURL;The value must be an NSURL containing the location to which the job file will be saved, for use when the job disposition is NSPrintSaveJob.
The NSPrintSavePath key has been deprecated.
In earlier versions of OS X there was no way for you to specify that the name extension of the file being written for an NSPrintSaveJob job disposition should be hidden. A new key for that has been published:
NSString *NSPrintJobSavingFileNameExtensionHidden;The value must be a boolean NSNumber. The default is NO.
New Job Style Hints in NSPrintPanel
In Mac OS 10.6 two new job style hints have been published for use with -[NSPrintPanel setJobStyleHint:]. They correspond to Core Printing's new kPMPresetGraphicsTypeAll and kPMPresetGraphicsTypeNone graphics types, respectively. There's no new job style hint corresponding to Core Printing's kPMPresetGraphicsTypeGeneral. A nil job style hint still means the same thing as that.NSString *const NSPrintAllPresetsJobStyleHint;
NSString *const NSPrintNoPresetsJobStyleHint;
Better Print Preview
The Print Preview on the print panel now draws respecting Border, N-Up, Mirror, Reverse Page Orientation, and Page Range.Also, the page range text/controls underneath the preview shows the physical pages to be printed after taking into consideration the user's settings for N-Up, Page Range, etc...
Printing just the selected content of a document
The NSPrintPanel can now optionally add a "Selection" radio option to the list of page range options (All, From:To:, Selection). To enable "Selection" as a user selectable page range in the print panel, Add NSPrintPanelShowsPrintSelection to the NSPrintPanel options when setting up the print operation.- (NSPrintOperation *)printOperationWithSettings:(NSDictionary *)printSettings error:(NSError **)outError {
...
NSPrintOperation *op = [NSPrintOperation printOperationWithView:...
NSPrintPanel *printPanel = [op printPanel];
[printPanel setOptions:[printPanel options] | NSPrintPanelShowsPrintSelection];
...
}
To print only the selection, the view supplied to the NSPrintOperation must check the isSelectionOnly property of the printInfo and adjust appropriately when determining the page range (knowsPageRange:) and drawing (drawRect:).- (BOOL)knowsPageRange:(NSRangePointer)aRange {
NSPrintOperation *curPrintOperation = [NSPrintOperation currentOperation];
BOOL printSelectionOnly = [[curPrintOperation printInfo] isSelectionOnly];
...
}
- (void)drawRect:(NSRect)rect {
BOOL printSelectionOnly = NO;
if(![NSGraphicsContext currentContextDrawingToScreen]) {
NSPrintOperation *curPrintOperation = [NSPrintOperation currentOperation];
BOOL printSelectionOnly = [[curPrintOperation printInfo] isSelectionOnly];
}
...
}
NSBrowser - Item Based only features
NSBrowser no longer caches the contents of each column. Instead it depends on the data source to provide speedy results. One ability this enables is that the same object can now appear in multiple columns. A consequence of this is that the following new API have been removed:- (NSIndexPath *)indexPathForItem:(id)item;The reloadItem:reloadChildren: method has been replaced with:
- (id)parentForItem:(id)item;
- (void)reloadItem:(id)item reloadChildren:(BOOL)flag;
- (void)reloadDataForRowIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column;The following method:
- (id)itemAtRow:(NSInteger)row column:(NSInteger)column;has been replaced with the following to provide better consistency with method names:
- (id)itemAtRow:(NSInteger)row inColumn:(NSInteger)column;You can now specify the row height:
- (void)setRowHeight:(CGFloat)height;Also the delegate can specify variable row heights by implementing the following method:
- (CGFloat)rowHeight;
- (CGFloat)browser:(NSBrowser *)browser heightOfRow:(NSInteger)row inColumn:(NSInteger)columnIndex;The delegate should inform the NSBrowser of row height changes via this method:
- (void)noteHeightOfRowsWithIndexesChanged:(NSIndexSet *)indexSet inColumn:(NSInteger)columnIndex;
NSBrowser - General
Updated the header comment for -browser:sizeToFitWidthOfColumn: to inform developers that they can opt out of supplying a column width for any particular column by returning -1. When NSBrowser gets a -1 return value, it will make its own column width determination as if the delegate did not implement this method.New API to get the column/row at a point and one to get the frame.
Finds the row and column located at 'point', returning YES if both can be found. If a row does not exist at 'point', then -1 is set for the row. If a column does not exist at 'point', then -1 is set for the column. 'point' is expected to be in the NSBrowser's coordinate system. *row and *column may be NULL. The correct BOOL value is returned and the non NULL *row or *column (if any) are filled in.
- (BOOL)getRow:(NSInteger *)row column:(NSInteger *)column forPoint:(NSPoint)point;Returns the frame of the row at 'row' / 'column' including the area for the expandable arrow. The returned NSRect is in the NSBrowser coordinate space.
- (NSRect)frameOfRow:(NSInteger)row inColumn:(NSInteger)column;
MultiTouch
In addition to gestures, developers can now get individual touches on a multitouch trackpad. You must explicitly opt-in in order to receive the new touch responder methods. Touches may or may not move the cursor via separate mouse events. The mouse event may occur slightly before or after its associated touch event.The same event that contains the touches may also contain a gesture. The gesture NSResponder methods are called in addition to the new touch NSResponder methods.
MultiTouch - NSView
New methods on NSView allow an application to opt in to receive touches. By default, views do not accept touch events:- (void)setAcceptsTouchEvents:(BOOL)flag;In some cases, the user may rest a thumb or other touch on the device. If the device can detect this (MacBooks with glass trackpad) then it will mark the touch as resting. By default, these touches are not delivered and are not included in the event's set of touches. Touches may transition in and out of resting at any time. Unless the view wants restingTouches, began / ended events are simulated as touches transition from resting to active and vice versa. In general resting touches should be ignored.
- (BOOL)acceptsTouchEvents;
- (void)setWantsRestingTouches:(BOOL)flag;
- (BOOL)wantsRestingTouches;
MultiTouch - NSEvent
There is a new method on NSEvent to extract the touches from the event.This is only valid for gesture events (Gesture, Magnify, Swipe, Rotate, etc.). A view can get all of the touches associated with a gesture without overriding the touch responder methods. Touches that target view, or any of view's descendants, are returned. Pass nil as the view to get touches regardless of their targeted view.
- (NSSet *)touchesMatchingPhase:(NSTouchPhase)phase inView:(NSView *)view;
MultiTouch - NSTouch
An NSTouch is a snapshot of a particular touch at some instance in time. That is quite different from an iPhone UITouch which persists for the life of the touch and is modified by the system during its life. The identity property is used to follow a specific touch across its lifetime.Touches do not have a corresponding screen location. The first touch of a touch collection is latched to the view underlying the cursor using the same hit detection as mouse events. Additional touches on the same device are also latched to the same view as any other touching touches. A touch remains latched to its view until the touch has either ended or is cancelled.
NSTouchPhaseBegan:
A finger touched the device. Or, a resting touch transitioned to an active touch and resting touches are not wanted by the view hierarchy.
NSTouchPhaseMoved:
A finger moved on the device.
NSTouchPhaseStationary:
A finger is touching the device, but hasn't moved since the previous event.
NSTouchPhaseEnded:
A finger was lifted from the screen. Or, an active touch transitioned to a resting touch and resting touches are not wanted by the view hierarchy.
NSTouchPhaseCancelled:
The system cancelled tracking for the touch, as when (for example) the window associated with the touch resigns key or is deactivated.
enum {
NSTouchPhaseBegan = 1 << 0,
NSTouchPhaseMoved = 1 << 1,
NSTouchPhaseStationary = 1 << 2,
NSTouchPhaseEnded = 1 << 3,
NSTouchPhaseCancelled = 1 << 4,
NSTouchPhaseTouching = NSTouchPhaseBegan | NSTouchPhaseMoved | NSTouchPhaseStationary,Unlike the iPhone, NSTouch objects do not persist for the life of the touch.
NSTouchPhaseAny = NSUIntegerMax
};
typedef NSUInteger NSTouchPhase;
@interface NSTouch : NSObject <NSCopying>Use the identity property to track changes to a particular touch during the touch's life. While touch identities may be re-used, they are unique during the life of the touch, even when multiple devices are present. Note: identity objects implement the NSCopying protocol so that they may be used as keys in an NSDictionary. Use isEqual: to compare two touch identities.
@property(readonly) id<NSObject, NSCopying> identity;Normalized, absolute position [0,1] of the touch on the device where (0, 0) is the lower left of the device surface.
@property(readonly) NSTouchPhase phase;
@property(readonly) NSPoint normalizedPosition;The following are properties of an NSTouch, but they really describe properties of the underlying touch device.
@property(readonly) BOOL isResting;
The digitizer that generated the touch. Useful to distinguish touches emanating from multiple-device scenario:
@property(readonly) id device;The range of the touch device in points (72ppi). Note: 0,0 is the lower left of the surface:
@property(readonly) NSSize deviceSize;
MultiTouch - NSResponder
Once a view opts in to receive touches, the following new responder methods are called. Each touch event may have more than one touch in various phases. Each responder method corresponding to that phase is called. (Note: the order these methods are called is undefined.)Note: It is possible for more than one of the following methods to be called for the same event. The order the methods are sent is not guaranteed.
/* A new set of touches has been recognized. To get the set of touches that began for this view (or descendants
of this view): [event touchesMatchingPhase:NSTouchPhaseBegan inView:self]; Note: this is not always the point
of contact with the touch device. A touch that transitions from resting to active may be part of a Began set.
*/
- (void)touchesBeganWithEvent:(NSEvent *)event;
/* One or more touches have moved. To get the set of touches that moved for this view (or descendants
of this view): [event touchesMatchingPhase:NSTouchPhaseMoved inView:self];
*/
- (void)touchesMovedWithEvent:(NSEvent *)event;
/* A set of touches have been removed. To get the set of touches that ended for this view (or descendants
of this view): [event touchesMatchingPhase:NSTouchPhaseEnded inView:self]; Note: this is not always the point
of removal with the touch device. A touch that transitions from active to resting may be part of an Ended set.
*/
- (void)touchesEndedWithEvent:(NSEvent *)event;
/* The System has cancelled the tracking of touches for any reason.
*/
- (void)touchesCancelledWithEvent:(NSEvent *)event;
NSDatePicker
User editing of a text date picker has been vastly improved. Editing a date/time field now replaces the contents of the field with the new digits. After each digit change, there is a short amount of time where the user can append more or delete digits (reseting the time out for each change). Once the timeout has occurred, future edits to the field replace the current value of the field. The new value in progress is committed if the timer runs out or, if adding any more digits would result in a invalid value for that field. Note, if the new value is committed before the time out expires, new edits cannot occur until the timeout expires. Return commits current changes and cancels the time out. Esc reverts any non committed edit. Delete will remove a digit but effectively sets an infinite timer until another change or UI focus movement occurs.MultiTouch - NSEvent
The subType of touch generated mouse events is now NSTouchEventSubtype. This is useful in when determining when to ignore mouse events in favor of touch events when they are both getting generated. This is not applicable to scrollWheel events.Gesture support
We have added event types and responder methods for gestures as generated by the trackpad on the MacBook Air and the newest generation MacBook Pro.NSEvent additions for gesture support
NSEventTypeGesture = 29An event of type NSEventTypeGesture is used for a gesture event that is not promoted to its own event type. An event of this type has additional information available in the CGEventRef.
NSEventTypeMagnify = 30,
NSEventTypeSwipe = 31,
NSEventTypeRotate = 18,
NSEventTypeBeginGesture = 19,
NSEventTypeEndGesture = 20
An event of type NSEventTypeBeginGesture and an event of type NSEventTypeEndGesture will be generated by the parser at the start and end of a stream of gestures, somewhat analogous to NSMouseDown and NSMouseUp. Applications can use the NSEventTypeEndGesture to commit a change, for example to do more detailed drawing or to register an undo operation.
An event of type NSEventTypeMagnify, NSEventTypeRotate, NSEventTypeSwipe, NSEventTypeBeginGesture, or NSEventTypeEndGesture will be delivered to the view under the mouse in the key window via the responder methods described below.
You can ask for a particular event type using -nextEventMatchingMask: or -nextEventMatchingMask:untilDate:inMode:dequeue:.
These accessors are valid for all events. When a gesture event is delivered to -[NSApplication sendEvent:] or dequeued via -[NSApplication nextEventMatchingMask:], it does not yet have a window target. At this point, -window is nil and -windowNumber is 0, and -locationInWindow is in screen coordinates. Before dispatching to -[NSWindow sendEvent:], NSApplication sets the window target in the event.
- (NSEventType)type;
- (NSUInteger)modifierFlags;
- (NSTimeInterval)timestamp;
- (NSWindow *)window;
- (NSInteger)windowNumber;
- (NSGraphicsContext*)context;
- (NSPoint)locationInWindow;
There is a new accessor, -magnification, that will return the desired magnification for an NSEventTypeMagnify event as a fraction. A magnification of 1 should be interpreted as increasing the scale factor by 100%, eg. from 100% to 200%, and a negative magnification should decrease the scale factor, with some minimum scale enforced so that you can never get to 0, for example.
- (CGFloat)magnification;The existing -rotation accessor is valid for NSEventTypeRotate events. The rotation is relative, and is measured in degrees, counterclockwise.
For NSEventTypeSwipe, the -deltaX and -deltaY accessors will return the swipe direction. A non-0 deltaX will represent a horizontal swipe, -1 for swipe right and 1 for swipe left. A non-0 deltaY will represent a vertical swipe, -1 for swipe down and 1 for swipe up.
Key equivalents
The fix to prevent NSKeyUp events from being sent to performKeyEquivalent: has been extended to include all applications, not just those built on Leopard or later.NSTrackingArea
There has been a change to the return value of -[NSTrackingArea userInfo]. When the NSTrackingArea is installed by the implementation of -[NSView addTrackingRect:owner:userData:assumeInside:], -[NSTrackingArea userInfo] will return nil rather than the given userData, which is declared to be a pointer and not guaranteed to be an object. The use of NSTrackingArea for this method is an implementation detail, and applications typically get the userData from the mouseEntered: or mouseExited: event. Returning nil from -[NSTrackingArea userInfo] for this case allows implementations of mouseEntered: and mouseExited: to send messages to the userInfo to determine whether the event is meant for a particular object in a class hierarchy. Prior to this change, there was no guarantee that the return of userInfo was a valid object.When setting the cursor for the current mouse location, for example after scrolling or when making a window key, NSWindow now sends cursorUpdate: through the responder chain. If no responder reponds to cursorUpdate:, a cursorRect added with addCursorRect:cursor: will be used if under the mouse. This change allows cursorUpdate: to coexist with addCursorRect:cursor:, but has binary compatibility implications. In particular, if a view calls invalidateCursorRectForView: from hitTest:, this can lead to a repeating pattern of invalidating cursor rects and hitTesting to set the current cursor. For this reason, the change to use cursorUpdate: is limited to applications linked on SnowLeopard or later.
NSResponder additions for gesture support
The following NSResponder methods have been added. The message will be sent to the view under the mouse in the key window.- (void)magnifyWithEvent:(NSEvent *)event;
- (void)rotateWithEvent:(NSEvent *)event;
- (void)swipeWithEvent:(NSEvent *)event;
- (void)beginGestureWithEvent:(NSEvent *)event;
- (void)endGestureWithEvent:(NSEvent *)event;
NSEvent
There are now NSEvent class methods providing access to some system settings. Because these are accessors for system settings, overrides will have no effect./* the time in which a second click must occur in order to be considered a doubleClick */There are also NSEvent class methods for getting the modifier flags and mouse button state outside of the event stream.
+ (NSTimeInterval)doubleClickInterval;
/* the time for which a key must be held down in order to generate the first key repeat event */
+ (NSTimeInterval)keyRepeatDelay;
/* the time between subsequent key repeat events */
+ (NSTimeInterval)keyRepeatInterval;
+ (NSUInteger)modifierFlags;
+ (NSUInteger)pressedMouseButtons;
NSWorkspace
We added NSWorkspace notifications for display wake and sleep. Few applications are likely to be interested in these notifications, but they may be useful for certain hardware-based drawing decisions, for example in OpenGL.NSString * const NSWorkspaceScreensDidSleepNotification;We also added an NSWorkspace notification for space switches. This notification is sent after a space switch has occurred.
NSString * const NSWorkspaceScreensDidWakeNotification;
NSString * const NSWorkspaceActiveSpaceDidChangeNotification;
NSWindow
You can now ask if a window is on the active space. If the window is offscreen, due to being minimized or hidden for example, -isOnActiveSpace will return YES if ordering the window onscreen will order it on to the active space. - (BOOL)isOnActiveSpace;There is an NSWindow class method, +windowNumbersWithOptions:, to retrieve window numbers for all onscreen windows. By default, +windowNumbersWithOptions: returns the window numbers for the visible windows belonging to the calling process on the active space. You can also request the window numbers for all visible windows, regardless of owner, and for windows on all spaces.
enum {
NSWindowNumberListAllApplications = 1 << 0,
NSWindowNumberListAllSpaces = 1 << 4
};
+ (NSArray *)windowNumbersWithOptions:(NSWindowNumberListOptions)options;There is also a class method to get the window that would be hit by a mouseDown at a given screenPoint. Pass a non-0 windowNumber to start the search below that window in z-order. Because this method uses the same rules as mouseDown hitTesting, windows with transparency at the given point, and windows that ignore mouseEvents, will not be returned.
+(NSInteger)windowNumberAtPoint:(NSPoint)point belowWindowWithWindowNumber:(NSInteger)windowNumber;There is new NSWindow API to override the default behavior where a sheet or modal panel disables application termination. - (BOOL)preventsApplicationTerminationWhenModal;
- (void)setPreventsApplicationTerminationWhenModal:(BOOL)flag;We added a notification, delegate methods, and window accessor for live resize. The notification and corresponding delegate methods are sent when window live resize starts do to a mouseDown in the resize corner, and when it ends due to a mouseUp.
NSString * const NSWindowWillStartLiveResizeNotification;The window delegate can implement the desired methods below to get automatically registered for the corresponding notification on the window.
NSString * const NSWindowDidEndLiveResizeNotification;
- (void)windowWillStartLiveResize:(NSNotification *)notification;While the mouse is down in the resize corner, -inLiveResize will return YES.
- (void)windowDidEndLiveResize:(NSNotification *)notification;
- (BOOL)inLiveResize;You can now call setFrame:display:animate: on a sheet to cause the sheet to resize while remaining correctly positioned on the document window. The document window will move if necessary to show the sheet completely onscreen. The animate flag must be YES to trigger this behavior. This is useful for expanding/collapsing a sheet, as we do for the save and print sheets, for example.
There is now API to disable windowServer moving of a window:
- (void)setMovable:(BOOL)flag;By default, windows can be dragged by their titlebar, toolbar, or bottom border, and by content if -isMovableByWindowBackground returns YES. The mouseDown/mouseDragged/mouseUp events that cause window moving are interpreted by the windowServer, and the window is moved in the windowServer process rather than in the application process. So, up until now, applications have not had a good way to disable or customize window moving. Calling setMovable:NO disables windowServer moving. If desired, you can implement application-controlled window moving by handling mouseDown/mouseDragged/mouseUp events in a window subclass. If a window is not movable, its relative screen position is also preserved in spaces F8 mode, but you can still drag the window to a different space in F8 mode.
- (BOOL)isMovable;
We've also added a heuristic to trigger a space switch for a client side move of a window. The window must return NO for isMovable, and must have its origin changed while the mouse is down. One thing this heuristic does not address is the ability to pull a non-movable window to another space by clicking in its draggable area and switching spaces. Lastly, there is an attempt to preserve the origin of a non-movable window on display configuration change, but it will be moved if necessary to keep the window onscreen.
NSWindow's firstResponder is KVO-compliant. Note we don't generally guarantee KVO-compliance for NSWindow and NSView properties; you should not assume KVO-compliance unless specifically documented.
NSWindow autodisplay behavior
NSWindow autodisplay is now throttled to occur no more frequently than the beam sync interval (about 1/60 second). On Leopard and previous, marking a view as dirty in an autodisplay window would cause window display to occur at the end of the event loop. On SnowLeopard, window display may be deferred until the beam sync time interval has elapsed. This is important for performance, since it avoids blocking the main thread in drawing operations.NSWindow collection behavior
In Leopard, we introduced the concept of window collection behavior. We added an enumeration to specify that a window could be visible on all spaces or that it would become visible on the current space when ordered front, or that it would get default behavior. The default behavior is currently dependent on window level.When a window has NSNormalWindowLevel, it gets these default behaviors:
- the window is visible and selectable in Exposé F9 and F10 modes
- the window is visible during Spaces F8 mode, and is associated with one space at a time. Dragging the window to a space boundary triggers a space switch
- the window participates in window cycling (cmd-` and ctrl-F4)
When a window has any other window level, it gets these default behaviors:
- the window is hidden in Exposé F9 and F10 modes
- the window is hidden in Spaces F8 mode, and floats across space switches if offscreen during a switch. Dragging the window to a space boundary does not trigger a space switch
- the window does not participate in window cycling
In order to provide additional application control, we've additional window collection behaviors to be used with -[NSWindow setCollectionBehavior:]
enum {
// participates in spaces, exposé. Default behavior if windowLevel == NSNormalWindowLevel
NSWindowCollectionBehaviorManaged = 1 << 2,
// floats in spaces, hidden by exposé. Default behavior if windowLevel != NSNormalWindowLevel
NSWindowCollectionBehaviorTransient = 1 << 3,
// unaffected by exposé. Stays visible and stationary, like desktop window
NSWindowCollectionBehaviorStationary = 1 << 4,
};
enum {
// default behavior if windowLevel == NSNormalWindowLevel
NSWindowCollectionBehaviorParticipatesInCycle = 1 << 5,
// default behavior if windowLevel != NSNormalWindowLevel
NSWindowCollectionBehaviorIgnoresCycle = 1 << 6
};
NSWindowCollectionBehaviorManaged
- the window is visible and selectable in Exposé F9 and F10 modes
- the window is visible during Spaces F8 mode, and is associated with one space at a time. Dragging the window to a space boundary triggers a space switch
NSWindowCollectionBehaviorTransient
- the window is hidden in Exposé F9 and F10 modes
- the window is hidden in Spaces F8 mode, and floats across space switches if offscreen during a switch. Dragging the window to a space boundary does not trigger a space switch
NSWindowCollectionBehaviorStationary
- the window is unaffected by Exposé. It stays in its current position and will most likely be covered by the Exposé shield window, just as the desktop is.
NSWindowCollectionBehaviorParticipatesInCycle
- the window participates in window cycling (cmd-` and ctrl-F4)
NSWindowCollectionBehaviorIgnoresCycle
- the window does not participate in window cycling
As in Leopard, -collectionBehavior will return only the behavior explicitly set via -setCollectionBehavior, or NSWindowCollectionBehaviorDefault if no behavior has been set explicitly.
The following examples illustrate how the new constants interact with the window-level-based default behavior.
[window setCollectionBehavior:NSWindowCollectionBehaviorDefault]- window will get behavior based on its window level, as described above under "Window collection behavior based on window level"
[window setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces]- window will be visible on all spaces, and will have behavior based on its window level for exposé and window cycling
[window setCollectionBehavior:NSWindowCollectionBehaviorMoveToActiveSpace]- window will become visible on active space when ordered front, and will have behavior based on its window level for exposé and window cycling
[window setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces|NSWindowCollectionBehaviorManaged|NSWindowCollectionBehaviorIgnoresCycle]- window will be visible on all spaces, will be visible and selectable in exposé, and will not participate in window cycling
[window setCollectionBehavior:NSWindowCollectionBehaviorTransient|NSWindowCollectionBehaviorParticipatesInCycle]- will be hidden in spaces and exposé, and will participate in window cycling
NSWindow modal window level in LSUIElement applications
Modal windows in LSUIElement applications get special window level treatment, where the window level is NSModalPanelWindowLevel whether or not the owning application is active. Modal windows belonging to regular applications are promoted to NSModalPanelWindowLevel when the application is active, but restored to NSNormalWindowLevel when the owning application is inactive, in order to prevent modal windows from floating above other applications. Since LSUIElement applications follow different activation rules, active status is not required in order to keep the modal window in front of other windows.NSWindow and NSScreen Color Space support (Modified since WWDC 2009)
NSScreen has an accessor to get the colorSpace of a screen.@interface NSScreen : NSObjectThere is also a notification sent when the colorSpace of a screen changes, NSScreenColorSpaceDidChangeNotification. The notification object is the screen whose color space has changed.
...
- (NSColorSpace *)colorSpace;
...
@end
In applications built on Leopard and previous, a window will inherit the colorSpace of the main screen (that is, the screen containing the menu bar). If a window returns YES for displaysWhenScreenProfileChanges, it will instead inherit that colorSpace of the screen which contains the majority of the window. displaysWhenScreenProfileChanges:YES is the default for windows in applications built on SnowLeopard or later. Either of these inheritance behaviors can be overridden by explicitly setting the color space of the window, which can be desirable to display content with better accuracy.
@interface NSWindow : NSResponder
...
/* -setColorSpace: allows you to specify a colorSpace that matches custom content. Calling -setColorSpace: with an unsupported colorSpace will raise an exception.
Typically, this would be called immediately after creating the window; changing the colorSpace of a window with backingStore will trigger redisplay
*/
- (void)setColorSpace:(NSColorSpace *)colorSpace;
- (NSColorSpace *)colorSpace;
...
@end
NSWindow Carbon/Cocoa Interaction
Cocoa has a notion of a main window distinct from the key window. Historically, we have not supported Cocoa main windows in Carbon apps; a Cocoa window in a Carbon app has been either key or inactive. In SnowLeopard, we added support for Cocoa main windows in Carbon apps. This support is limited to apps linked on SnowLeopard or later since it has binary compatibility implications.NSWindowDepth constants
NSWindow has existing API to get/set a window depth limit:- (void)setDepthLimit:(NSWindowDepth)limit;We haven't previously published the possible values for NSWindowDepth. The way it's worked historically is that one would call NSBestDepth with parameters specifying characteristics of the window depth, and NSBestDepth would return a value suitable for passing to setDepthLimit:.
- (NSWindowDepth)depthLimit;
NSWindowDepth NSBestDepth (NSString *colorSpace, NSInteger bps, NSInteger bpp, BOOL planar, BOOL *exactMatch);NSWindow now publishes the supported values for NSWindowDepth. Note that this is a depth limit, which means that the window may use a lower depth depending on graphics capabilities.enum {
NSWindowDepth32BitRGB = 0x208,
NSWindowDepth64BitRGB = 0x210,
NSWindowDepth128BitRGB = 0x220
};
Displaying dictionary contents
We added a facility to display a definition or other dictionary contents for a selection, similar to HIDictionaryWindowShow from Carbon.Both -showDefinitionForAttributedString:atPoint: and -showDefinitionForAttributedString:range:options:baselineOriginProvider: show a window that displays the definition (or other subject depending on available dictionaries) of the specified attributed string.
showDefinitionForAttributedString:atPoint: takes an attributedString "attrString", and a baseline origin of that string in the receiver view coordinate system "textBaselineOrigin". This method can be used for implementing the same functionality as NSTextView's 'Look Up in Dictionary' contextual menu on a custom view.
-showDefinitionForAttributedString:range:options:baselineOriginProvider: takes an attributedString "attrString", a range in that string (normally the selected range) "targetRange", an options dictionary "options", and an origin provider "originProvider". If there is no selection, the "targetRange" should be zero-length to cause the appropriate range to be auto-detected around the given offset. "options" may be nil, or may contain a dictionary with presentation options. If a small overlay window is selected as default presentation (see NSDefinitionPresentationTypeKey option for details), the overlay text is rendered starting from the origin specified by the originProvider block. The originProvider block should return the baseline origin of the first character at proposed adjustedRange in the receiver view coordinate system. If the receiver is an NSTextView, both attrString and originProvider may be nil, in which case the text view will automatically supply values suitable for displaying definitions for the specified range within its text content. This method does not cause scrolling, so clients should perform any necessary scrolling before calling this method.
@interface NSView(NSDefinition)
- (void)showDefinitionForAttributedString:(NSAttributedString *)attrString
atPoint:(NSPoint)textBaselineOrigin;
- (void)showDefinitionForAttributedString:(NSAttributedString *)attrString
range:(NSRange)targetRange
options:(NSDictionary *)options
baselineOriginProvider:(NSPoint (^)(NSRange adjustedRange))originProvider;
@endNSDefinitionPresentationTypeKey is an optional key in '"options" dictionary that specifies the presentation type of the definition display. The possible values are NSDefinitionPresentationTypeOverlay, which produces a small overlay window at the string location, or NSDefinitionPresentationTypeDictionaryApplication, which invokes the Dictionary application to display the definition. Without this option, the definition will be shown in either of those presentation forms depending on 'Contextual Menu:' setting in Dictionary application preferences by default.
NSString * const NSDefinitionPresentationTypeKey;
NSString * const NSDefinitionPresentationTypeOverlay;
NSString * const NSDefinitionPresentationTypeDictionaryApplication;
NSApplication termination
NSApplication now disables the terminate: action while waiting for the delegate to call replyToApplicationShouldTerminate: after returning NSTerminateLater from applicationShouldTerminate:. The purpose of this change is to avoid multiple calls to applicationShouldTerminate: while the application is doing termination-time cleanup. It has a side effect of disabling the Quit menu while waiting for the delegate. This should be desirable behavior, but is limited to applications linked on SnowLeopard or later to avoid preventing termination of applications whose delegates may not be calling replyToApplicationShouldTerminate: properly.NSHelpManager
In Leopard and previous releases, NSHelpManager has done automatic registration of help books in the main bundle in its implementations of showHelp:, openHelpAnchor:inBook:, and findString:inBook:. In SnowLeopard,we have added API so a plugin can register its help book for later use in help lookup.The following method registers one or more help books in the given bundle. The main bundle is automatically registered by -openHelpAnchor:inBook: and -findString:inBook:. You can use -registerBooksInBundle: to register help books in a plugin bundle, for example. The Info.plist in the bundle should contain a help book directory path, which specifies one or more folders containing help books. Returns NO if the bundle doesn't contain any help books or if registration fails. Returns YES on successful registration.
- (BOOL)registerBooksInBundle:(NSBundle *)bundle;
NSDockTile plugin (Modified since WWDC 2009)
An application may customize its dock tile when not running via a plugin whose principal class implements the NSDockTilePlugIn protocol. The name of the plugin is indicated by a NSDockTilePlugIn key in the application's Info.plist file, and the plugin should have an extension of .docktileplugin. The plugin is loaded in a system process at login time or when the application tile is added to the Dock. When the plugin is loaded, the principal class' implementation of -setDockTile: is invoked. If the principal class implements -dockMenu, -dockMenu is invoked whenever the user causes the application's dock menu to be shown. When the dock tile is no longer valid (eg. the application has been removed from the dock, -setDockTile: is invoked with a nil NSDockTile.Note that the plugin needs to have its own copy of the application icon, unless it is completely overriding drawing by setting its own contentView in the dockTile.
@protocol NSDockTilePlugIn <NSObject>
@required
- (void)setDockTile:(NSDockTile*)dockTile;
@optional
- (NSMenu*)dockMenu;
@end
NSViewController-based NSCollectionViewItem
In Mac OS 10.6, the superclass of NSCollectionViewItem was changed from NSObject to NSViewController. This was done to improve how the prototype view is replicated. The view replication method used previously in Mac OS 10.5 works for many simple views, but some connections and bindings may not be copied correctly.With the new NSViewController-based NSCollectionViewItem, this limitation can be overcome by initializing the prototype NSCollectionViewItem with a nib name and bundle, from which the view can reliably replicated.
Converting your NSCollectionViewItem prototypes to using nibs is usually very simple. First, create a new empty nib in Interface Builder. Next, copy your prototype view into the new nib. Change the class of the File's Owner of the new nib to NSCollectionViewItem and connect the view to its 'view' outlet. In the new nib, reconnect any bindings from your view to the NSCollectionViewItem. Save the new nib. In your original nib, delete the prototype view and enter the name of your new nib (and the bundle identifier, if required—leaving it blank will search the main bundle) in the prototype NSCollectionViewItem's inspector and save the nib.
The new NSCollectionViewItem is completely compatible with existing ones. Using a nib-based prototype view is optional and NSCollectionViewItems archived in 10.5 will still be readable in 10.6.
NSCollectionView Drag and Drop
In Mac OS 10.6, NSCollectionView has a new drag and drop API similar to the existing APIs for NSTableView, NSBrowser, etc. For an NSCollectionView to be a dragging source, it must have a delegate that implements and returns YES from collectionView:canDragItemsAtIndexes:withEvent: and collectionView:writeItemsAtIndexes:toPasteboard:. For an NSCollectionView to be a dragging destination, it must have a delegate that implements collectionView:validateDrop:proposedIndex:dropOperation: and collectionView:acceptDrop:index:dropOperation:. For more detail, see the comments in NSCollectionView.h.Two methods have been added to NSCollectionView, which may be necessary within your drag and drop delegate method implementations. The first is itemAtIndex:, which returns the NSCollectionViewItem associated with the represented object at the given index. This is especially useful if you need to get a reference to specific subviews of the NSCollectionView. The second is frameForItemAtIndex:, which returns the frame that the receiver has calculated for the subview at the given index. You should always use this method instead of calling [[[collectionView itemAtIndex:index] view] frame]. Doing this would force the view to be created prematurely, if the collection view is trying to lazily load its items. This method is especially useful in the collectionView:draggingImageForItemsAtIndexes:withEvent:offset: delegate method to determine which views are in the visible area of the collection view.
NSCollectionView bug fixes
Signficant bugs in NSCollectionView were fixed in Mac OS 10.6When an NSCollectionView is resized, it will no longer cause its enclosing scroll view to scroll to the top.
Strange glitches in NSCollectionView's animations are fixed. Subviews will no longer jump unexpectedly, and will not stutter when their positions are animated. NSCollectionView will also work better together with NSScrollViews that autohide scrollers.
For NSCollectionViews that do not use the new nib-based prototype views, the view copying mechanism was made more reliable. In Mac OS 10.5, the views could potentially be copied before all their bindings or other connections had been loaded from the nib.
NSCollectionView will now correctly trigger KVO notifications for selectionIndexes when the selection is changed by mouse events.
NSCollectionView now ensures that its subviews are correctly positioned on pixel boundaries to avoid blurring or gaps between subviews.
NSSplitView appearance changes
In Mac OS 10.5, the default value of -[NSSplitView dividerColor] for thin dividers was a gray color, regardless of the window's style. This created a usually undesirable appearance when drawn on textured windows. In applications linked on or after Mac OS 10.6, -dividerColor will now return [NSColor clearColor] by default for split views with thin dividers on textured windows.Also, NSSplitView has stopped drawing different versions of the divider dimple. From now on, it will only draw the dimmed version, regardless of the containing window's key state.
Formal Protocol Adoption
In Mac OS 10.6, AppKit switched to using formal protocols for all delegates and data sources to provide better compile-time type checking. Required protocol methods are marked with @required, where possible. The rest are marked with @optional.The affected classes are NSAlert, NSAnimation, NSApplication, NSBrowser, NSComboBox, NSComboBoxCell, NSControl, NSDatePicker, NSDatePickerCell, NSDrawer, NSImage, NSLayoutManager, NSMatrix, NSMenu, NSOutlineView, NSPathCell, NSPathControl, NSRuleEditor, NSSavePanel, NSSound, NSSpeechRecognizer, NSSpeechSynthesizer, NSSplitView, NSTableView, NSTabView, NSText, NSTextField, NSTextStorage, NSTextView, NSTokenField, NSTokenFieldCell, NSToolbar, and NSWindow
The changes will introduce new warnings in code using these classes' delegates. Fortunately, the changes needed to correct these warnings are fairly simple.
• Your delegate classes need to declare conformance to the new protocols. For example:
@interface MyDelegate : NSObject <NSApplicationDelegate> ... @end• Sending messages to [foo delegate] or [foo dataSource] that are not in the delegate's protocol will generate a warning. This is not generally recommended since there are no guarantees about the delegate's class. However, you may work around this by casting the result of [foo delegate] or [foo dataSource] to id. Also, you should always perform a respondsToSelector: check before invoking a method on [foo delegate] or [foo dataSource] that is not an @required method in the protocol.
• If you have a subclass of one of these classes that adds additional delegate methods you must also create a subprotocol and override -delegate and -setDelegate:. For example:
@protocol MySplitViewDelegate;
@interface MySplitView : NSSplitView
- (id <MySplitViewDelegate)delegate;
- (void)setDelegate:(id <MySplitViewDelegate>)delegate;
@end
@protocol MySplitViewDelegate <NSStreamDelegate>
- (void)additionalDelegateMethod;
@end
@implementation MySplitView
- (id <MySplitViewDelegate)delegate {
return (id <MySplitViewDelegate>)[super delegate];
}
- (void)setDelegate:(id <MySplitViewDelegate>)delegate {
[super setDelegate:delegate];
}
@end• If you need to target Leopard or Tiger with the same sources, you should conditionally declare empty protocols, or else the compiler will complain about missing protocols declarations. For example:
#if MAC_OS_X_VERSION_10_6 > MAC_OS_X_VERSION_MAX_ALLOWED
@protocol NSTableViewDelegate <NSObject> @end
#endif
New NSSplitView delegate method
To make it easier for your application to maintain the size of given subviews of a split view while the split view is being resized, NSSplitView in Mac OS 10.6 has a new -splitView:shouldAdjustSizeOfSubview: delegate method you can implement. It controls how -adjustSubviews behaves so you don't have to rewrite so much of its default behavior in a -splitView:resizeSubviewsWithOldSize: delegate method.By returning NO from this method, you can lock the size of a split view subview while the split view is resized. However, if the split view shrinks beyond the point that all of the resizable subviews are completely hidden, the subviews you returned NO for will start resizing as well to prevent -adjustSubviews from creating an invalid subview layout.
New NSSplitView pane splitter divider style
In Mac OS 10.6, NSSplitView has a new divider style called NSSplitViewPaneSplitterDividerStyle. This new style is significantly different than the "thick" style because it draws an opaque gradient along the entire length of the dividers and its width is slightly smaller. This style matches the appearance of the split view dividers in many of Apple-created applications.Snow Leopard's Human Interface Guidelines strongly encourage you to migrate away from the "thick" style to this new style. Because of this, when you create a new NSSplitView instance in Interface Builder you will only be able to choose between the "thin" and "pane splitter" divider styles.
NSSplitView's paneSplitter and setPaneSplitter: methods are deprecated in Mac OS 10.6 in favor of the new divider style.
NSCell controlView property changes
The declarations for controlView and setControlView: are on NSCell, but until SnowLeopard the storage for the controlView property was at the NSActionCell level. -[NSCell controlView] always returned nil and -[NSCell setControlView:] did nothing. This caused a variety of unexpected behaviors.For applications linked on or after SnowLeopard, the storage for this property is at the NSCell level, and the NSCell accessors behave as you might expect. The fix is limited to apps linked on or after SnowLeopard. This is because NSCell does not retain its controlView, so there are new opportunities for dangling pointers. If your app keeps a static NSCell instance that you use for drawing in many different contexts, for example, you may need to be careful to clear out the controlView between uses. This requirement was already in place for NSActionCell, so this is only an issue if you use something that doesn't descend from that class.
Also, prior to SnowLeopard, NSActionCell propagated its controlView to its copy in copyWithZone:. This is not correct and causes problems. The controlView is a transient property set just before a cell is drawn. As methods are invoked on the cell in the course of copying it, the original cell's control typically ends up dirtied and redrawn. Since a cell does not retain its controlView, this causes crashes if the original control view is deallocated.
For applications linked against the SnowLeopard SDK or later, the controlView is not propagated.
NSImageCell disabled images
In Leopard and previous, there is no out of the box view that displays an image that dims out when it is disabled. One can try to fake this with a button, but then the view accepts key focus when full keyboard access is enabled, has accessibility problems, darkens when pressed, etc.One might expect NSImageView to do this, but NSImageView instead shows that it is disabled by drawing its frame differently. There are cases where you want this: When the image view is set up as an image well, the image may be user data that you are just presenting. It's odd to dim it out, especially when it suffices to dim the image well.
However, it seems unambiguous that if an image view has no frame, it is correct to show disabled by dimming the image.
This is the behavior for applications linked on or after SnowLeopard. The linkage check is here because some applications do disable image views without intending grayed out drawing. This could easily arise from experimenting with checkboxes in Interface Builder.
NSButton
The disclosure triangle style of NSButton is now able to invert to white on dark backgrounds and such, by interpreting the backgroundStyle property of the cell.A caveat is that unfortunately our art for the raised background style is not as widely suitable as would be nice. It's really drawn for a source list style table, and doesn't look good very on the window surface. This control isn't suitable directly on the window surface for now. We're sorry. To avoid making existing apps look worse in this case, we only interpret the background styles for apps that have linked on or after 10.6, though the 10.5-ish non-engraved art doesn't look very good here either.
NSImage, CGImage, and CoreGraphics impedance matching
NSImage has changed substantially internally for SnowLeopard in ways that developers might want to be aware of. There is also some new API. AppKit imaging is now more closely aligned with CoreGraphics.The basic change is to move to consistent use of CGImage internally. For example, in Leopard and previous, -[NSImage lockFocus] drew into a view in an offscreen window. Now it draws into a buffer, and a CGImage is extracted from the buffer in +unlockFocus. NSImage no longer uses windows at all in its implementation. We've entirely deprecated NSCachedImageRep, which is the NSImageRep subclass that represents drawing backed by a window. NSCachedImageRep does still work, but AppKit never instantiates it internally.
This has the potential to break clients. On Leopard and previous, +[NSView focusView] returned a view from within +[NSImage lockFocus]. Hopefully you were unaware of this! Now it returns nil. If you were looking at [[NSView focusView] isFlipped], you may wish to look at [[NSGraphicsContext currentContext] isFlipped] instead. The two coincide whenever focus is actually locked on a view.
Another issue we've seen is with clients calling -lockFocus on an image, then drawing the image elsewhere before calling -unlockFocus. This no longer works. The NSImage itself is not modified until -unlockFocus is called, as described above.
NSImage in Leopard and previous rounded destination rectangles to integral values in user-space, which was not so good for resolution independence, and not good for people who genuinely wanted images placed where they were put, with antialiasing. This rounding is out, but may mean that some images look blurry.
We've added a method of extracting CGImages from an NSImage, -[NSImage CGImageForProposedRect:context:hints:]. Why all the parameters? Well, an NSImage is potentially resolution independent, and may have multiple representations suited for different contexts. For example, an NSImage may have a hand drawn 16x16 bitmap as well as a 512x512 bitmap. This allows the image to display well at large and very small sizes. A CGImage is more like a single pixel-based representation. Think of -CGImageForProposedRect:context:hints: as producing a snapshot of how the NSImage would draw if it was asked to draw in *proposedDestRect in the passed context.
A snapshot is a good way to think of it, but may lead you to think the method is less efficient than it really is. There's nothing guaranteed about the characteristics of the returned CGImage (e.g., pixel size, colorspace) except that drawing it in the passed context produces the same results as drawing the original NSImage in the context. In particular, if an NSImage is backed by a single NSBitmapImageRep, then it will (probably) always return the one CGImage that backs the bitmap. That's a valid snapshot for the image for any destination rect, because in this case the CGImage _does_ capture all the drawing the NSImage is capable of.
The rect, context and hints are first used to select the best representation for the described destination using new API -[NSImage bestRepresentationForRect:context:hints:], then the image rep is asked for a CGImage using -[NSImageRep CGImageRepForProposedRect:context:hints:]. NSImage may cache the CGImage, same as it may cache for normal drawing and controlled in the same way, and the CGImage is returned.
The NSImageRep level method is intended as an override point for image rep subclasses that naturally have a CGImage available. For example, NSBitmapImageRep overrides it to return the CGImage that naturally backs the rep. You don't need to override the method except possibly for performance, though. The NSImageRep-level implementation will produce a CGImage by making a buffer and calling -[self draw]. That's likely to be the best possible implementation for reps that aren't naturally CGImage backed. -draw remains the only method of NSImageRep that a subclasser really needs to override.
One subtlety: The 'proposedRect' parameter passes a pointer to an NSRect. This is because a CGImage is necessarily pixel-integral, while an NSImage is not. In order to produce a CGImage for rect (0.5, 0.5, 4.0, 4.0) without distortion or double-antialiasing, we may have to produce a 5x5 CGImage, and also inflate the proposedRect. Drawing the CGImage in the out-value in proposed rect is the same as drawing the NSImage in the in-value of proposed rect.
For example, this block of code
- (void)drawRect {
NSRect dstRect = NSMakeRect(0.5, 0.5, 4.0, 4.0);
CGImageRef cgImage = [pdfBackedImage CGImageForProposedRect:&dstRect context:[NSGraphicsContext currentContext] hints:nil];
// dstRect might be something like {{0.0, 0.0}, {5.0, 5.0}} on return
CGContextDraw([[NSGraphicsContext currentContext] graphicsPort], NSRectToCGRect(dstRect), cgImage);
}
should produce drawing equivalent to (or at least very close to) this code:- (void)drawRect {
NSRect dstRect = NSMakeRect(0.5, 0.5, 4.0, 4.0);
[pdfBackedImage drawInRect:dstRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
}
The latter code is better, though. Producing a snapshot CGImage may be more expensive than just drawing the NSImage (though we'll use an existing CGImage whenever possible), so prefer to just draw with -[NSImage drawInRect:fromRect:operation:fraction:] or related methods unless you truly require a CGImage.There is also a method for conveniently and efficiently producing an NSImage from a CGImage, -[NSImage initWithCGImage:size:].
NSImage Behavior: Simplifying caching and related interfaces
In Leopard and previous, NSImage has several methods that influence caching and related behavior, sometimes with important effects on how the image draws. These important effects have sometimes been a source of confusion for clients. For SnowLeopard, the intent is that caching should be handled more transparently, and that caching should affect only performance, not the basic meaning of how an image draws.To support this, the following methods are deprecated without replacement:
- (void)setCachedSeparately:(BOOL)flag;Any call to set one of these properties can be removed. Briefly:
- (BOOL)isCachedSeparately;
- (void)setCacheDepthMatchesImageDepth:(BOOL)flag;
- (BOOL)cacheDepthMatchesImageDepth;
- (void)setScalesWhenResized:(BOOL)flag;
- (BOOL)scalesWhenResized;
- (void)setDataRetained:(BOOL)flag;
- (BOOL)isDataRetained;
isCachedSeparately:This controlled whether an image shared a cache window with other instances of NSImage. NSImage no longer caches to windows.
cacheDepthMatchesImageDepth:This controlled the bit depth used in making cache windows. NSImage no longer caches to windows. A cache is now generated appropriate for the destination where an image is drawn, and it doesn't apply if the image is drawn into a context that doesn't match in some way that would have led to a different cache being created.
scalesWhenResized:One might think -scalesWhenResized would be a semantic property rather than a caching flag, but it really had to do with whether caches were invalidated when -[NSImage setSize:] was called. The size of an image is no longer relevant for caching purposes - only the size of the rect where the image is drawn is important. In SnowLeopard, NSImage caching should never have any obviously observable user effect like an image being drawn into a different size region of the screen. Caching is about performance.
The complete behavior when the property was set to NO was complex, but it did not reliably achieve keeping the image from scaling. It did make the scaled image draw blocky, sometimes.
isDataRetained:When this was NO, which it was by default, an image was permitted to throw out its backing data when drawn and keep something only appropriate for the context it which it was first used. For example, a PDF-backed image would typically be rasterized when first drawn, and the PDF data would be irrecoverably discarded. In SnowLeopard, NSImage no longer throws out data in such a way that the original can no longer be reconstructed.
Please note that -[NSImage setCacheMode:] is not deprecated. In particular, you can use NSImageCacheNever to disable all use of caches in NSImage. Disabling caching may be an optimization if the image is known to be temporary, or if there is caching taking place elsewhere that would make a cache at this level wasted memory.
NSImage: deprecating -[NSImage setFlipped:], adding a drawing method that respects context flippedness
The flipped attribute of NSImage is widely misunderstood. We are deprecating it for SnowLeopard, and replacing its typical uses with less error-prone API.The property describes the orientation of the internal coordinate system of the NSImage. Just as a superview never cares about the flippedness of its subviews, a user of an NSImage should not care about its flippedness.
The typical (flawed) use case is to try to call [image setFlipped:[[NSGraphicsContext currentContext] isFlipped]] just prior to drawing, but this does not accomplish the intended goal. If called before caching, then representations end up caching upside down, and the flip is absorbed into the cache. If called after caching, it has no effect-the cached representation is already supposed to incorporate any necessary flipping. In the former case, if the NSImage is drawn anywhere else later, it ends up upside down in _that_ place, which is also confusing because the bug and the expression of the bug are far apart. Lack of understanding regarding flippedness is also frequently the source of poorly performing code, in which people make unnecessary intermediate buffers to work around perceived framework bugs. The framework behaves according to design, but contrary to expectation, and the semantics are not all that useful. It's also difficult to change the semantics of -[NSImage isFlipped], because a lot of code is very closely dependent on the current behavior. Rather than attempt this, we have deprecated the property.
We are providing a simple and correct way to draw images in a flipped or unflipped context, which is a draw method that can account for context flippedness. We are also adding a hints parameter matching the hints in -bestRepresentationForRect:context:hints:.
- (void)drawInRect:(NSRect)dstRectPass YES for respectFlipped to get the fancy new behavior. One note for those that understand the CTM and worry that this method has an odd interaction, where modifying the CTM could fail to have any effect on image drawing: This is not the case. This method branches behavior based on [[NSGraphicsContext currentContext] isFlipped]. Modifying the CTM might turn your axes upside down, but it will not alter the result of -[NSGraphicsContext isFlipped]. They're completely orthogonal.
fromRect:(NSRect)srcRect
operation:(NSCompositingOperation)op
fraction:(CGFloat)alpha
respectFlipped:(BOOL)respectContextIsFlipped
hints:(NSDictionary *)hints;
A second valid use of -[NSImage setFlipped:] was to specify the flippedness of the context obtained via -[NSImage lockFocus]. There are cases, for example drawing directly via NSLayoutManager, that require a flipped context. To cover this case, we add
- (void)lockFocusFlipped:(BOOL)flipped;This doesn't alter the state of the image itself, only the context on which focus is locked. It means that (0,0) is at the top left and positive along the Y-axis is down in the locked context.
NSImage: Breaking change to drawAtPoint:fromRect:operation:fraction:
-[NSImage drawAtPoint:fromRect:operation:fraction:] has different behavior for applications linked on or after SnowLeopard than it did previously. We corrected a bug that has substantial impact on behavior, but it's weird enough, and easy enough to work around, that we think we can get away with fixing it. The natural thing to do if you ever encountered this bug was to switch to doing something else, not to depend on it.Prior to SnowLeopard, -drawAtPoint:fromRect:... called -drawInRect:fromRect:... passing [image size] as the size of the destination rect. If 'image' was 100x10, then the snippet
[image drawAtPoint:NSZeroPoint fromRect:NSMakeRect(0,0,10,10) operation:NSCompositeSourceOver fraction:1.0];was equivalent to
[image drawInRect:NSMakeRect(0,0,10,100) fromRect:NSMakeRect(0,0,10,10) operation:NSCompositeSourceOver fraction:1.0];which makes little sense. This doesn't even preserve the aspect ratio of the source rect. In applications linked on SnowLeopard, the snippet is instead equivalent to
[image drawInRect:NSMakeRect(0,0,10,10) fromRect:NSMakeRect(0,0,10,10) operation:NSCompositeSourceOver fraction:1.0];This obviously may require changes when you recompile your code, so search your source for drawAtPoint. The change has no effect on applications that pass {{0,0}, [image size]} for the source rect, which is what most applications do. As with the other drawing methods that take source rects, NSZeroRect is treated as sentinel meaning 'the whole image'.
If you were passing a partial source rect on Leopard and earlier, and you need compatibility on Leopard and SnowLeopard, then replace your usage of -drawAtPoint:fromRect:operation:fraction: with -drawInRect:fromRect:operation:fraction:.
NSImage: Deprecating old drawing methods
NSImage has a suite of drawing methods that start with "draw" and a suite of drawing methods that start with "dissolve" or "composite".- (void)dissolveToPoint:(NSPoint)point fraction:(CGFloat)aFloat;The "draw" methods are newer, and are generally what you want to be using.
- (void)dissolveToPoint:(NSPoint)point fromRect:(NSRect)rect fraction:(CGFloat)aFloat;
- (void)compositeToPoint:(NSPoint)point operation:(NSCompositingOperation)op;
- (void)compositeToPoint:(NSPoint)point fromRect:(NSRect)rect operation:(NSCompositingOperation)op;
- (void)compositeToPoint:(NSPoint)point operation:(NSCompositingOperation)op fraction:(CGFloat)alpha;
- (void)compositeToPoint:(NSPoint)point fromRect:(NSRect)rect operation:(NSCompositingOperation)op fraction:(CGFloat)alpha;
The "composite" methods have surprising semantics. They set the current transformation matrix of the graphics context to be the identity matrix (or more accurately, the identity scaled up by the default user space scale factor – think default window scaling with resolution independence) before drawing. That is, in a scaled or rotated view, composite methods draw as if the view is not scaled or rotated.
This isn't usually what one is really after. Often people use these methods because they seem to produce right-side-up drawing in flipped views. If that's what you're after, use the new method -[NSImage drawInRect:fromRect:operation:fraction:respectContextFlipped:hints:] passing YES for respectContextFlipped.
We are not currently providing a direct replacement, but if you do have a case that uses the composite methods for their intended purpose, you can reproduce the result by manipulating the graphics context before drawing.
NSImage: Deprecating -lockFocusOnRepresentation:
The NSImage method:- (void)lockFocusOnRepresentation:(NSImageRep *)imageRepresentation;is deprecated because it is commonly misunderstood and easy to replicate. It does not set up the rep as a drawing destination, it sets the image up as a drawing destination, then draws the rep into it. You can replace usage with
[image lockFocus];
[rep drawInRect:NSMakeRect(0,0,[image size].width, [image size].height)];
NSImageRep: More flexible drawing
This addition to NSImageRep provides drawing flexibility equivalent to NSImage's. Prior to SnowLeopard, one needed to wrap a rep up with an NSImage to draw with any operation other than NSCompositeCopy, for example. That's a pain since it's far more common to draw with NSCompositeSourceOver.- (BOOL)drawInRect:(NSRect)dstRect
fromRect:(NSRect)srcRect
operation:(NSCompositingOperation)op
fraction:(CGFloat)alpha
respectFlipped:(BOOL)respectContextIsFlipped
hints:(NSDictionary *)hints;
NSImage: Hit testing for non-transparent regions
NSImage has gained a method that tests whether clicks and drags hit an opaque part of an image.- (BOOL)hitTestRect:(NSRect)testRectDestSpaceMore precisely, this method answers the question, "If you were to draw the image in the passed destination rect in the passed context respecting the passed flippedness with the passed hints, would the test rect in the context intersect a non-transparent portion of the image?". The information about the drawing destination is necessary because an NSImage draws differently depending on where it's going. Consider a typical OS X icon that has a 16x16, 32x32 and 256x256 pixel representations. The choice of representation to draw depends on the destination, so accurate hit testing also needs destination information.
withImageDestinationRect:(NSRect)imageRectDestSpace
context:(NSGraphicsContext *)context
hints:(NSDictionary *)hints
flipped:(BOOL)flipped;
However, the context and hints parameters can be omitted, and we'll assume the context is set up like a window context with default scaling. This is the same as in -[NSImage bestRepresentationForRect:context:hints:] and the other NSImage methods that take similar parameters. If you don't have an NSGraphicsContext handy but you know that when the image draws it is into a context that differs from the window's context in some respects, you can use the hints to describe that context.
The flipped parameter is called out separately rather than supplied as a hint because it's a critical parameter. It determines the orientation with which the image draws in the destination rect, and therefore which portion of the image you are hit testing. If you are passing a non-nil context, you probably want to pass [context isFlipped]. If you don't have a context handy, but you're are calling this from within a view and hit testing an image that you draw in drawRect:, then you should probably pass [view isFlipped] and the rect where you draw the image for imageRectDestSpace.
There is one special case behavior. If the test rect has {0,0} size, then this method instead tests whether the origin point hits something non-transparent in the image. You could think of this as testing a rect with infinitesimal size.
NSImage: New standard images
We added a passel of new names for use with +[NSImage imageNamed:]. Search for AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER near the bottom of NSImage.h, or look at the "Media" tab in Interface Builder's library panel.NSImage: Orientation metadata (e.g. exif) now respected by default
Some image file formats, such as JPEG, contain metadata that describes the 'orientation' of the data backing the image. A camera may tag its image data as rotated by 90 degrees counterclockwise relative to what the user should see.NSImage has never interpreted this metadata. For apps linked on or after SnowLeopard, we do. Preview and ImageKit interpreted this information on 10.5. WebKit currently does not. ImageIO, the fundamental image reading framework, reports the information but does not account for it.
We interpret the data in a way that is transparent to clients. Bitmap data, pixel size, and all other properties are reported with respect to the image after orientation correction.
This is a major behavior change in some ways, but we do not expect it to impact very much code in practice. Still, there are some developers who will need to conditionally opt-out for compatibility. We introduce a new piece of API on NSImage to support them. This is a backward-looking method for opting out of the new behavior.
- (id)initWithDataIgnoringOrientation:(NSData *)data;Who needs this? Consider a document format that references images. If a document is saved on Leopard, it's usually correct for it to look as it did on Leopard when opened on SnowLeopard. In a drawing program that includes images, the user may have manually rotated an image to compensate for ignored orientation information. The image would end up double rotated if the app began to interpret the embedded orientation. See the end of this section for an example of how to handle this kind of compatibility constraint.
It's not all that common to need to change code, though. In OS X, only AppKit needed to react to the change. The difference is relevant to image loading, not to working with images, and is only relevant for images which are user data as opposed to embedded in a framework. Images written out by, say, TIFFRepresentation or by archiving an NSImage are always in a normalized orientation, so no changes necessary when reading them. Most apps _want_ to interpret image orientation data. A big exception is existing documents that directly reference image files that came from the user untouched.
-[NSImage initWithDataIgnoringOrientation:] is really a convenience method. Another way to ignore image orientation is to use ImageIO's CGImageSource to make a CGImage, and then -[NSImage initWithCGImage:size:] to represent it as an NSImage. ImageIO reports orientation metadata, but does not automatically interpret it.
We also provide a global opt-out. If you set the user default NSBitmapImageRepRespectOrientation to NO, NSImage will behave the old way all of the time. This is an option of last resort for developers who cannot update affected code used by their app for some reason.
Here's an example of the work that might be required when moving an app up to the 10.6 SDK:
AppKit itself reads and writes the RTFD format. Here's what we had to do: First, each image attachment in memory now has an internal 'noOrient' bit, and we added a /noorient tag in the on-disk file format. If noOrient is set, then when the corresponding image is loaded from disk, it's loaded with -[NSImage initWithDataIgnoringOrientation:]. Each RTFD document on disk also has a global version tag. For old versions, every image attachment is implicitly noOrient. When a new image is added to a document in memory, the bit is not set. Finally, when we save an RTFD in our current file format version, we write out the /noorient tag for any image attachment on which it is set.
NSImage: CALayer accepts NSImage as contents
You may now call -[CALayer setContents:] with an NSImage as the contents object. Previously you needed a CGImage.There is one subtlety. CoreAnimation is built to work with a raster object, a bitmap.
If your image comes from a bitmap file like a TIFF then there is no issue, but a PDF, for example, will be rasterized. The layer will not scale like a PDF. It will scale like a bitmap. If the default way we produce the raster form doesn't meet your needs, you can exert more control using -[NSImage CGImageForRect:context:hints:]. This will produce a CGImage that you can set as the contents of the layer.
CALayer will also perform caching. You should not modify your NSImage after setting it as the contents of a layer. When or if your changes show up would be arbitrary. This issue not specific to CALayer; it is never a good idea to modify an NSImage unless you just created it and the modifications are part of the initial setup of the image. If an NSImage has escaped to code you don't own, or an NSImage has been handed to you by code you don't own, you should not modify it.
Note: Even before this change, you could not call -[CALayer contents] and assume that the result was a CGImage. The method is typed id, and the object returned could be one of several different private types in 10.5.
NSImage: Clarifying the contract for threading
NSImage, NSImageRep, and all of its subclasses have the same threading contract as NSMutableDictionary, NSMutableArray, NSMutableString and company.If you aren't calling mutating methods, an image can be safely used from arbitrary threads.
If you _are_ mutating an image, you need to guarantee that the image is not being accessed on any other threads while the mutation is occurring, or risk crashes. You need to have full knowledge of what references the image and be able to prevent, say, drawing on any other thread.
In practice, this often means that you may only modify an image as part of its initial setup, before it has escaped to the app at large. Copying an image and modifying the copy is valid and occasionally necessary. We try hard to make copies cheap. The backing data is shared whenever possible (without impairing thread safety).
By "mutating method" we mean those methods that do explicit mutation, like -setSize: or -addRepresentation:. We do _not_ mean methods like -drawInRect:fromRect:operation:fraction: that may modify the image's ivars as a side effect of caching and lazy faulting and such. If an image has internal caching or laziness, it will be handled in a way that does not impair thread safety. Locking focus on an image and drawing in it is a mutation, however, as is modifying the bitmapData of an NSBitmapImageRep.
There's one notable exception to the above rule: Even though it's a mutation, you may call -[NSBitmapImageRep incrementalLoadFromData:complete:] while other threads are accessing the image.
Subclassers of NSImageRep take note: You need to adhere to this contract as well. Expect non-mutating methods to be called on arbitrary threads.
NSBitmapImageRep: CoreGraphics impedance matching and performance notes
Release notes above detail core changes at the NSImage level for SnowLeopard. There are also substantial changes at the NSBitmapImageRep level, also for performance and to improve impedance matching with CoreGraphics.NSImage is a fairly abstract representation of an image. It's pretty much just a thing-that-can-draw, though it's less abstract than NSView in that it should not behave differently based aspects of the context it's drawn into except for quality decisions. That's kind of an opaque statement, but it can be illustrated with an example: If you draw a button into a 100x22 region vs a 22x22 region, you can expect the button to stretch its middle but not its end caps. An image should not behave that way (and if you try it, you'll probably break!). An image should always linearly and uniformly scale to fill the rect in which its drawn, though it may choose representations and such to optimize quality for that region. Similarly, all the image representations in an NSImage should represent the same drawing. Don't pack some totally different image in as a rep.
That digression past us, an NSBitmapImageRep is a much more concrete object. An NSImage does not have pixels, an NSBitmapImageRep does. An NSBitmapImageRep is a chunk of data together with pixel format information and colorspace information that allows us to interpret the data as a rectangular array of color values.
That's the same, pretty much, as a CGImage. In SnowLeopard an NSBitmapImageRep is natively backed by a CGImageRef, as opposed to directly a chunk of data. The CGImageRef really has the chunk of data. While in Leopard an NSBitmapImageRep instantiated from a CGImage would unpack and possibly process the data (which happens when reading from a bitmap file format), in SnowLeopard we try hard to just hang onto the original CGImage.
This has some performance consequences. Most are good! You should see less encoding and decoding of bitmap data as CGImages. If you initialize a NSImage from a JPEG file, then draw it in a PDF, you should get a PDF of the same file size as the original JPEG. In Leopard you'd see a PDF the size of the decompressed image. To take another example, CoreGraphics caches, including uploads to the graphics card, are tied to CGImage instances, so the more the same instance can be used the better.
However: To some extent, the operations that are fast with NSBitmapImageRep have changed. CGImages are not mutable, NSBitmapImageRep is. If you modify an NSBitmapImageRep, internally it will likely have to copy the data out of a CGImage, incorporate your changes, and repack it as a new CGImage. So, basically, drawing NSBitmapImageRep is fast, looking at or modifying its pixel data is not. This was true in Leopard, but it's more true now.
The above steps do happen lazily: If you do something that causes NSBitmapImageRep to copy data out of its backing CGImageRef (like call bitmapData), the bitmap will not repack the data as a CGImageRef until it is drawn or until it needs a CGImage for some other reason. So, certainly accessing the data is not the end of the world, and is the right thing to do in some circumstances, but in general you should be thinking about drawing instead. If you think you want to work with pixels, take a look at CoreImage instead - that's the API in our system that is truly intended for pixel processing.
This coincides with safety. A problem we've seen with our SnowLeopard changes is that apps are rather fond of hardcoding bitmap formats. An NSBitmapImageRep could be 8, 32, or 128 bits per pixel, it could be floating point or not, it could be premultiplied or not, it might or might not have an alpha channel, etc. These aspects are specified with bitmap properties, like -bitmapFormat. Unfortunately, if someone wants to extract the bitmapData from an NSBitmapImageRep instance, they typically just call bitmapData, treat the data as (say) premultiplied 32 bit per pixel RGBA, and if it seems to work, call it a day.
Now that NSBitmapImageRep is not processing data as much as it used to, random bitmap image reps you may get ahold of may have different formats than they used to. Some of those hardcoded formats might be wrong.
The solution is _not_ to try to handle the complete range of formats that NSBitmapImageRep's data might be in, that's way too hard. Instead, draw the bitmap into something whose format you _know_, then look at that.
That looks like this:
NSBItmapImageRep *bitmapIGotFromAPIThatDidNotSpecifyFormat;This produces no more copies of the data than just accessing bitmapData of bitmapIGotFromAPIThatDidNotSpecifyFormat, since that data would need to be copied out of a backing CGImage anyway. Also note that this doesn't depend on the source drawing being a bitmap. This is a way to get pixels in a known format for any drawing, or just to get a bitmap. This is a much better way to get a bitmap than calling -TIFFRepresentation, for example. It's also better than locking focus on an NSImage and using -[NSBitmapImageRep initWithFocusedViewRect:].
NSBitmapImageRep *bitmapWhoseFormatIKnow = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:width pixelsHigh:height
bitsPerSample:bps samplesPerPixel:spp hasAlpha:alpha isPlanar:isPlanar
colorSpaceName:colorSpaceName bitmapFormat:bitmapFormat bytesPerRow:rowBytes
bitsPerPixel:pixelBits];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmapWhoseFormatIKnow]];
[bitmapIGotFromAPIThatDidNotSpecifyFormat draw];
[NSGraphicsContext restoreGraphicsState];
unsigned char *bitmapDataIUnderstand = [bitmapWhoseFormatIKnow bitmapData];
So, to sum up:
(1) Drawing is fast. Playing with pixels is not.
(2) If you think you need to play with pixels, (a) consider if there's a way to do it with drawing or (b) look into CoreImage.
(3) If you still want to get at the pixels, draw into a bitmap whose format you know and look at those pixels.
NSImage performance debugging: NSNewBitmapBackingStore
The most fundamental rule to image performance on OS X is this: If you see something drawing repeatedly on the screen, it should be drawn with the same identical image object each time.In 10.6 there's a private function to help you notice if you aren't managing this. NSNewBitmapBackingStore is the funnel point in AppKit for making a new buffer of image data. New buffers of image data mean new images.
Thus, you don't want to see this function trip on every frame during repeated drawing situations like live resize. DTrace via Instruments is a nice way to see if this is happening. In Instruments, select "Build New Instrument…" from the Instrument menu. Fill in "AppKit" for the Library and NSNewBitmapBackingStore for the function. Now start recording in Instruments, and use your app as normal. You'll see a blip whenever the function is called. A smattering of calls is normal, but if you see it called furiously then you likely have a performance problem.
A few things to note:
(1) This is a private function. That means it won't necessarily be there in the future. You can use it to debug, but don't compile knowledge of it into your app.
(2) The goal is not to avoid seeing this function called, the goal is to make your app perform better. If you drop to Quartz level to make images, for example, you can bypass calling this function without making your app better. Rather, if you see this function being called a bunch, try to understand why new images are being created and address the cause. The other material in these release notes may help you understand.
NSBitmapImageRep notable bug fix: -[NSBitmapImageRep CGImage] safer now
In Leopard, -[NSBitmapImageRep CGImage] would under most circumstances return a CGImage that was not retained by the bitmap rep, but which would cause a crash if its data was accessed (e.g., by drawing it) after the bitmap rep had been destroyed.This is fixed in SnowLeopard.
NSBitmapImageRep: Expanded colorspace support
Prior to SnowLeopard, NSBitmapImageRep supported specific colorspaces through a colorspace name property, and ICC profile data through -[NSBitmapImageRep valueForProperty:] with NSImageColorSyncProfileData property.For SnowLeopard, we add support for working directly with colorspaces.
- (NSColorSpace *)colorSpace;
- (NSBitmapImageRep *)bitmapImageRepByConvertingToColorSpace:(NSColorSpace *)targetSpace renderingIntent:(NSColorRenderingIntent)renderingIntent;Both -[NSBitmapImageRep bitmapImageRepByConvertingToColorSpace:renderingIntent:] and -[NSBitmapImageRep bitmapImageRepByRetaggingWithColorSpace:] may fail, in which case they will return nil. The latter definitely _will_ fail if you pass a colorspace that has a different color space model than the receiver. That is, if your original image is sRGB, you can only retag with some other RGB colorspace.
- (NSBitmapImageRep *)bitmapImageRepByRetaggingWithColorSpace:(NSColorSpace *)newSpace;
Both methods will return the original bitmap if it already has the requested colorspace.
NSComboBox
For apps linked on or after SnowLeopard, if a combo box is configured to send action on enter only, then it will not send its action when a selection is made from the drop down, either with the mouse or the keyboard. Only enter in the text field part of the combo box will send the action.NSDrawThreePartImage accepts nil caps
As of 10.6, NSDrawThreePartImage can handle a nil for either the start cap or the end cap in NSDrawThreePartImage. If you pass nil for both, you'll be tiling the fill image either horizontally or vertically across the specified rect.Support for EPS images
EPS images can now be read without crashing, though images with the bounding box specified at the end of the file will probably still load with an incorrect bounding box.Rotated PDFs in patterns
PDFs with a /Rotate entry are now correctly displayed rotated when used in patterns (e.g. +[NSColor colorWithPatternImage:]).NSPasteboard
Multiple Pasteboard Items
Historically, NSPasteboard has been able to store multiple data representations of a single logical item. Each representation is data of a particular type, keyed by the type name. In SnowLeopard, NSPasteboard adds support for multiple pasteboard items. Each item represents a single logical item which can hold multiple data representations. Prior to multiple pasteboard items, plural pboard types were necessary to put multiple pieces of data onto the single logical item of the pasteboard. For example, the NSFilenamesPboardType consists of an array of file paths all combined into one serialized property list. Using multiple pasteboard items, the analogous operation would result in multiple pasteboard items, with each file URL represented by a single pasteboard item.Interacting with NSPasteboard has always been data type-centric, centered around getting and setting data for particular types. The new NSPasteboard API also allows for higher-level class-centric operations, while still allowing type-centric interaction when necessary.
For instance, the following code writes the appropriate data from an NSAttributedString onto the general pasteboard:
NSAttributedString *attString =...;The following code reads data from each pasteboard item and creates an NSAttributedString instance from each item that contains data an attributed string can be initialized with:
NSPasteboard *pboard = [NSPasteboard generalPasteboard];
[pboard clearContents];
[pboard writeObjects:[NSArray arrayWithObject:attString]];
NSPasteboard *pboard = [NSPasteboard generalPasteboard];And, for things such as menu item validation, the following code checks if at least one NSAttributedString instance can be created from the contents of the pasteboard:
NSArray *desiredClasses = [NSArray arrayWithObject:[NSAttributedString class];
NSArray *attributedStrings = [pboard readObjectsForClasses:desiredClasses] options:nil];
NSPasteboard *pboard = [NSPasteboard generalPasteboard];This functionality is supported by two new protocols, NSPasteboardWriting and NSPasteboardReading. These protocols respectively allow a class to specify which data types its instances can provide to the pasteboard, and which data types its instances can be created from. The Cocoa classes NSAttributedString, NSImage, NSColor, NSURL, NSString, and NSSound implement this protocol so that common pasteboard types can be easily written and read. Other classes, such as an application's model classes, can also implement these protocols and be used in the same fashion.
NSArray *desiredClasses = [NSArray arrayWithObject:[NSAttributedString class];
BOOL canRead = [pboard canReadObjectForClasses:desiredClasses] options:nil];
See NSPasteboard.h for more information and examples of using multiple pasteboard items.
NSPasteboardItem
Although the class-centric APIs are a convenient abstraction from the details of data types, there are times when it is necessary to interact with the contents of the pasteboard by data type. The NSPasteboardItem class, introduced in SnowLeopard, represents a single pasteboard item and can hold multiple data representations. The NSPasteboardItem API is very similar to the original NSPasteboard API, with facilities for getting and setting data by type, as well as adding a data provider to provide data lazily.NSPasteboardItem implements the NSPasteboardWriting and NSPasteboardReading protocols. As such, you add pasteboard items to the pasteboard by calling -writeObjects: and you can read pasteboard items from the pasteboard by calling -readObjectsForClasses:options: and specifying the NSPasteboardItem class. As a convenience, the NSPasteboard method -pasteboardItems method will return the array of all pasteboard items. The NSPasteboard validation method -canReadItemWithDataConformingToTypes: will check if any pasteboard item contains data conforming to the specified types. This supports writing validation code without asking for and iterating through all pasteboard items.
Some cases where you may need to use a pasteboard item directly:
1. When the set of types you wish to write to the pasteboard does not map well to a particular class. For instance, you may have a custom pasteboard type which is a property list that does not map directly to a class in your application. In this case, you might create a pasteboard item, set the property list for your custom type, and then write the pasteboard item to the pasteboard.
2. When you implement a delegate method or subclass a method that receives a pasteboard. In this case the superclass or class you are the delegate of has already written to the pasteboard. In your delegate or overridden method, you might get the pasteboard items, see what data has already been provided, and then call methods on the NSPasteboardItem instances to set additional or replacement data for particular types.
3. When reading the pasteboard and the destination of the data is not an object. For instance, if your application commonly used CGImages, you might get the array of pasteboard items, and iterate through it looking for image data, getting the data for an image type, and creating the CGImages.
An NSPasteboardItem instance can be associated with a single pasteboard. When you first create a pasteboard item, it can be written to any pasteboard. When you pass in a pasteboard item to -writeObjects:, that pasteboard item becomes bound to the pasteboard it was written to. When you retrieve pasteboard items using -pasteboardItems or -readObjectsForClasses:options:, the returned pasteboard items are associated with the messaged pasteboard. Passing a pasteboard item that is already associated with a pasteboard into -writeObjects: causes an exception to be raised. Note that as long as a pasteboard item is associated with a pasteboard, all methods to set data take affect on the pasteboard.
Pasteboard items are intended to be transient objects, used during a single pasteboard interaction, not held onto and used repeatedly. A pasteboard item is only valid until the owner of the pasteboard changes. If a pasteboard item is stale because the pasteboard owner has changed, it will return nil or NO values from its methods.
NSPasteboardItem instances are created only when necessary. In the code example above, an attributed string is both written to and read from the pasteboard without an NSPasteboardItem instance being created. The class-centric approach should be used when possible, and the type-centric approach of NSPasteboardItem should be used when necessary.
See NSPasteboardItem.h for more information.
Additional NSPasteboard Notes
- By historical convention, absolute URLs have always been expected on the pasteboard. The NSURL implementation of the NSPasteboardWriting protocol formalizes this by not providing types or data to be written to the pasteboard unless the URL is an absolute URL.- In SnowLeopard, filtering pasteboards only operate and are valid on the first item on the pasteboard.
- In SnowLeopard, none of the Cocoa classes that implement NSPasteboardWriting and NSPasteboardReading write or read the types used on the NSFontPboard and NSRulerPboard pasteboards.
UTIs on the Pasteboard
All NSPasteboard APIs introduced in Snow Leopard accept UTIs only and do not accept NSPboardType strings. The new APIs will accept an informally defined UTI string. This is any string which follows the naming convention for UTIs, but is not formally declared in an Info.plist. These informal UTI strings are useful for declaring pasteboard types which are for internal use within an application, since it may not be desirable to formally declare those types. If you do formally declare UTIs for use on the pasteboard, the UTIs should conform to the public.data type.Migrating from pboard types to UTIs
Use of pboard types should be replaced with the use of UTIs. Pboard types will be deprecated in a future release. For all pboard types declared in AppKit, see NSPasteboard.h for the migration path for each pboard type. In almost all cases, migration consists of substituting the pboard type constant for an analogous UTI-based constant. The main exception is the NSFilenamesPboard type, where the migration path is to use the new multiple pasteboard item API to read and write file URLs on the pasteboard. For custom pboard types, the migration path is either to create a constant that follows UTI naming conventions and use it as an undeclared informal UTI, or to formally declare that constant. See below for additional information if other applications rely on your existing custom pboard types.Moving public pboard types to UTIs
For applications that currently define a public pboard type that other applications rely on, the following is necessary to ensure that existing applications will continue to see your pboard type even after your application has moved from pboard types to UTIs:1. Formally declare the new pasteboard type UTI as an exported type declaration.
2. Ensure the declared UTI conforms to the public.data type.
3. Add a tag specification for the 'com.apple.nspboard-type' tag to the type declaration. Note that the value you provide should be the literal string value of the pboard type, not the name of the constant. An example tag specification to associate a UTI with a pboard type:
<key>UTTypeTagSpecification</key>
<dict>
<key>com.apple.nspboard-type</key>
<string>MyCustomPboardType</string>
</dict>
Deprecating PICTPboardType
NSPICTPboardType is deprecated in SnowLeopard. The PICT format was formally deprecated in Tiger along with QuickDraw. Applications should not be explicitly providing or looking for PICT data on the pasteboard.To aid in this deprecation, if PICT is the only image type on the pasteboard, as is sometimes the case when copying images from 32-bit Carbon applications, a translated image type will be automatically reported and provided by NSPasteboard. The translated type is added to the types array ahead of PICT so that the deprecated PICT format is not the preferred format.
Although NSPICTPboardType and its UTI equivalent kUTTypePICT will appear in a pasteboard's type array retrieved from the existing NSPasteboard API, it may cease to be reported in future releases.
Reading image types from the pasteboard
Historically, TIFF has been the single commonly used pasteboard type for image data. With the introduction of NSPasteboard support of UTIs, it is possible to put image data of any format onto the pasteboard.This means that when retrieving image data from the pasteboard that you should be certain to use the array of types returned by [NSImage imageTypes], not just the NSTIFFPboardType.
For backwards compatibility for applications linked before SnowLeopard, if we find image data UTIs on the pasteboard, but no TIFF data present, NSPasteboard will automatically promise and translate to TIFF. For applications linked on SnowLeopard, you should update your pasteboard code to look for and accept any image type, rather than just TIFF. Using the new writeObjects: / readObjectsForClasses:options: methods with NSImage will handle this automatically.
Promised data and Sudden Termination
When an application promises to provide data lazily to a pasteboard, sudden termination is disabled to prevent the application from terminating without having the opportunity to provide the promised data at application quit. Sudden termination is re-enabled when either all of promised data is provided or the ownership of the pasteboard changes.Pasteboard types that predate OS X
NSPasteboard no longer promises deprecated pasteboard types that predate OS X, and have never been public in OS X: "NeXT plain ascii pasteboard type", "NXColor pasteboard type", "NeXT filename pasteboard type", "NXTypedFilenamePboardType".Using pbcopy and pbpaste with different encodings
The command line tools pbcopy and pbpaste now use standard locale environment variables to determine the character encoding used for reading and writing string data. See man pages for details.Accessibility
NSImage Accessibility Description
-accessibilityDescription Property
In SnowLeopard, NSImage adds an -accessibilityDescription property. The string should be a localized accessibility description. If an NSImage has an accessibility description, Cocoa interface elements that display images will report the description to accessibility. This functionality is currently supported by NSImageCell, NSButtonCell, and NSSegmentedCell.This makes it easier for a single user interface element to report different AXDescriptions depending on which image is shown. For instance, in a chat application, different images might be used to report status such as "online", "offline", "away". Now, you can set the accessibility description for each image. As you change which image is displayed, the image cell will report the description of the currently set image.
Default accessibility description strings for named system images
In Leopard, constants were introduced in NSImage.h for named system images. In SnowLeopard, these images now have a default accessibility description, providing an additional amount of default accessibility for commonly used images.AccessibilityImageDescriptions.strings file
In applications, most images used in the interface are named resources loaded using NSImage's +imageNamed: method. If you provide a .strings file called 'AccessibilityImageDescriptions.strings', it will be checked for an accessibility description. The keys in the .strings file are the names of the images as provided to +imageNamed:. The values are the accessibility description.Image Description Search Path
When an image is asked for its accessibility description, if an accessibility description string has been set programmatically using -setAccessibilityDescription: that string will be returned. If no string has been set programatically, the string value for that image in the application's AccessibilityImageDescriptions.strings file, if present, will be returned. Finally, if AppKit provides a default value for the image, that value will be returned.Additional Accessibility Description Notes
Note that calling +imageNamed: repeatedly with the same name returns the same instance of NSImage. If you change the accessibility description on a named image, the description will change everywhere that named image instance is being used. If you need different descriptions for the same image, when used in different places in the UI, use an accessibility overridden attribute to provide the description on the UI element itself.Note also that setting an overridden AXDescription attribute on an element will override the value provided by the element itself, including the accessibility description of the displayed image.
NSMenu and NSMenuItem Accessibility
In SnowLeopard, both NSMenu and NSMenuItem support the NSAccessibilityAdditions method accessibilitySetOverrideValue:forAttribute:. The support is only for values of type NSString, and is intended to allow adding an AXDescription to a menu or menu item that contains an image instead of text.
NSLevelIndicator role changes
In SnowLeopard, NSLevelIndicator reports new accessibility roles and subroles.The discrete and continuous capacity styles report the new NSAccessibilityLevelIndicatorRole as their role. They also report the new NSAccessibilityWarningValueAttribute and NSAccessibilityCriticalValueAttribute attributes.
The relevancy indicator style now reports the NSAccessibilityRelevanceIndicatorRole as its role.
The rating indicator style continues to report its role as a slider, but with the NSAccessibilityRatingIndicatorSubrole.
Changes in setting AXValue of a text view or text field
Setting the AXValue of a text view or focused text field now behaves like the user entering text, including triggering bindings. For text fields it is recommended to set the AXFocus of the text field to true before setting its AXValue. In the future, text fields may require focus before having their value set through accessibility, to more closely mirror direct user interaction.New outline view accessibility notifications
NSOutlineView supports the new NSAccessibilityRowExpandedNotification and NSAccessibilityRowCollapsedNotification notifications. Assistive applications register to receive the notification from the outline element. The element reported with the notification is the outline row that expanded or collapsed.Optional array attribute methods
Accessibility clients such as VoiceOver have long had the ability to get a count or a subarray for any accessibility attribute whose value is an array. In addition, various internal accessibility operations rely on getting the index of an element's child. The default framework implementation for each of these operations calls -accessibilityAttributeValue: to retrieve the entire array of values, and then performs the appropriate operation. If these new methods are implemented, they will be used instead. For accessibility objects with many children, or many values in any array attribute, the results to these methods can sometimes be calculated without generating the entire array of values, which can improve performance./* Given an accessibility child of an object, return the index of that child in the parent.
*/
- (NSUInteger)accessibilityIndexOfChild:(id)child;
/* Return the count of an accessibility array attribute.
*/
- (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute;
/* Return a subarray of values of an accessibility array attribute. Note this method does not take a range.
The max count is the maximum desired number of items requested by an accessibility client.
This number may be beyond the bounds of your array.
*/
- (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount;
Miscellaneous Accessibility Changes
- NSTableView and subclasses now correctly return the selected columns to accessibility, not selected column indexes.- For a table view with hidden columns, AXColumn now reports the correct AXIndex.
- NSProgressIndicator no longer erroneously reports an AXChildren attribute to accessibility.
- NSScroller no longer reports its AXChildren attribute twice.
- Sort buttons now correctly report their role as AXButton with AXSortButton as a subrole.
- The NSAccessibilitySortButtonRole constant has been deprecated and the NSAccessibilitySortButtonSubrole constant added.
- NSAccessibilityUnknownOrientationValue constant added to match AX API.
- Circular sliders now report their AXOrientation as AXUnknownOrientation.
- AXBusyIndicators and AXProgressIndicators now have an AXOrientation attribute. The bar style reports AXHorizontalOrientation, the spinning style reports AXUnknownOrientation.
- NSAccessibilityPlaceHolderValueAttribute added. Interface elements with placeholder strings report the value of the placeholder using this attribute.
Miscellaneous Accessibility Changes
- Setting AXFrontmost to true on a Cocoa app now brings all windows to the front as happens in Carbon apps.Text Checking
Snow Leopard includes a new facility known as text checking, which provides a unified interface for a variety of types of checking, including spell checking, grammar checking, URL detection, smart quotes, date and address detection via Data Detectors, and others. Among other things, this facility allows for the automatic identification of the languages used in a piece of text, so that spellchecking can proceed without the user having to label text as to language.The basic interface for this facility is available in NSSpellChecker, in the form of the methods
- (NSArray *)checkString:(NSString *)stringToCheck
range:(NSRange)range
types:(NSTextCheckingTypes)checkingTypes
options:(NSDictionary *)options
inSpellDocumentWithTag:(NSInteger)tag
orthography:(NSOrthography **)orthography
wordCount:(NSInteger *)wordCount;
- (NSInteger)requestCheckingOfString:(NSString *)stringToCheckwhich request unified text checking for the given range of the given string. The checkingTypes should be a bitmask of checking types from the Foundation header NSTextCheckingResult.h, describing which types of checking are desired. The options dictionary allows clients to pass in options for certain types of checking. The orthography and wordCount arguments return these two attributes of the range as a whole, while the return value is an array of NSTextCheckingResult objects describing particular items found during checking and their individual ranges. The first method here invokes checking synchronously and returns results immediately; the second requests checking in the background, and returns results at some later point to a completion handler block. The completion handler block will be invoked in an arbitrary context; if completion work needs to be performed on the main thread, the client will need to arrange for that via a performSelector method or the equivalent. The return value of the second method is a monotonically increasing sequence number, which can be used to keep track of requests in flight, and which will be supplied as an argument to the completion block.
range:(NSRange)range
types:(NSTextCheckingTypes)checkingTypes
options:(NSDictionary *)options
inSpellDocumentWithTag:(NSInteger)tag
completionHandler:(void (^)(NSInteger seqNumber, NSArray *results, NSOrthography *orthography, NSInteger wordCount))block;
NSTextCheckingResult and NSOrthography are two new classes defined in Foundation for use with text checking. An instance of NSTextCheckingResult represents something that has been found during checking--a misspelled word, a sentence with grammatical issues, a detected URL, a straight quote to be replaced with curly quotes, and so forth. NSOrthography is a class used to describe the linguistic content of a piece of text, especially for the purposes of spelling and grammar checking. It describes (a) which scripts the text contains, (b) a dominant language and possibly other languages for each of these scripts, and (c) a dominant script and language for the text as a whole. The results of automatic language identification are described using NSOrthography. See the Foundation release notes for more details on these two classes.
NSSpellChecker has the additional methods
- (NSArray *)userPreferredLanguages;to facilitate the use of text checking and automatic language identification. Entries in the availableLanguages list are all available spellchecking languages in user preference order, as described in the spellchecker's info dictionary, usually language abbreviations such as en_US. The userPreferredLanguages will be a subset of the availableLanguages, as selected by the user for use with spellchecking, in preference order. If automaticallyIdentifiesLanguages is YES, then text checking will automatically use these as appropriate; otherwise, it will use the language set by setLanguage:. The older checkSpellingOfString:... and checkGrammarOfString:... methods will use the language set by setLanguage:, if they are called with a nil language argument.
- (BOOL)automaticallyIdentifiesLanguages;
- (void)setAutomaticallyIdentifiesLanguages:(BOOL)flag;
- (NSArray *)guessesForWordRange:(NSRange)range inString:(NSString *)string language:(NSString *)language inSpellDocumentWithTag:(NSInteger)tag;
- (NSArray *)userQuotesArrayForLanguage:(NSString *)language;
- (NSDictionary *)userReplacementsDictionary;
There are also the following methods to support the user interface associated with text checking:
- (NSPanel *)substitutionsPanel;along with the constants
- (NSViewController *)substitutionsPanelAccessoryViewController;
- (void)setSubstitutionsPanelAccessoryViewController:(NSViewController *)accessoryController;
- (void)updatePanels;
- (NSMenu *)menuForResult:(NSTextCheckingResult *)result
string:(NSString *)string
options:(NSDictionary *)options
atLocation:(NSPoint)location
inView:(NSView *)view;
NSString *NSTextCheckingOrthographyKey;to be used as keys in the options dictionaries for the checkString:..., requestCheckingOfString:..., and menuForResult:... methods described above. All of these keys are optional.
NSString *NSTextCheckingQuotesKey;
NSString *NSTextCheckingReplacementsKey;
NSString *NSTextCheckingReferenceDateKey;
NSString *NSTextCheckingReferenceTimeZoneKey;
NSString *NSTextCheckingDocumentURLKey;
NSString *NSTextCheckingDocumentTitleKey;
NSString *NSTextCheckingDocumentAuthorKey;
The substitutions panel provides an interface for various auxiliary functionality associated with text checking. It may have an accessory view if an accessory view controller is set for it. The updatePanels method is one that clients should call when any of their state changes with respect to spelling, grammar, or text checking, that might affect the spelling or substitutions panel; for example, if a client changes whether one of the text checking features is turned on, it should call this method on the shared spellchecker. The menuForResult:string:options:atLocation:inView: method returns a menu containing contextual menu items suitable for certain kinds of detected results (notably date/time/address results). The string argument is optional; if supplied, it should be the string contents of the range detected by the result. The location in the view will be used if an action eventually calls for putting up a temporary window.
NSTextCheckingOrthographyKey may be used to supply an NSOrthography indicating an orthography to be used as a starting point for orthography checking, or as the orthography if orthography checking is not enabled. The value for NSTextCheckingQuotesKey should be an NSArray containing four strings to be used with NSTextCheckingTypeQuotes (opening double quote, closing double quote, opening single quote, and closing single quote in that order). The default value used if this key is not present is taken from user preferences, and may be determined via the -userQuotesArrayForLanguage: method. The value for NSTextCheckingReplacementsKey should be an NSDictionary containing replacements to be used with NSTextCheckingTypeReplacement; if this is not specified, values will be taken from user's preferences, and these default values may be obtained via the -userReplacementsDictionary method.
The NSTextCheckingReferenceDateKey, NSTextCheckingReferenceTimeZoneKey, NSTextCheckingDocumentURLKey, NSTextCheckingDocumentTitleKey, and NSTextCheckingDocumentAuthorKey keys may be used to specify metadata associated with the current document for use with NSTextCheckingTypeDate, NSTextCheckingTypeAddress, and NSTextCheckingTypeLink. NSTextCheckingReferenceDateKey can be used to supply an NSDate to be used as a referent for relative dates, and NSTextCheckingReferenceTimeZoneKey can be used to supply an NSTimeZone to be used as a referent for dates without time zones; the current date and time zone will be used if these are not specified. These keys may be used if specific information is available about the document--for example, the date and time zone of its composition, for something like an email document. NSTextCheckingDocumentURLKey can be used to specify an NSURL associated with the document, NSTextCheckingDocumentTitleKey an NSString representing a title associated with the document, and NSTextCheckingDocumentAuthorKey an NSString representing a name of an author associated with the document. These are especially useful with the -menuForResult:string:options:atLocation:inView: method, which supplies menu items appropriate for a contextual menu to be displayed for a detected item at the given location in a view.
Text Checking in NSTextView
In addition, there are methods on NSTextView to support its use of text checking.- (void)setAutomaticDataDetectionEnabled:(BOOL)flag;Turning on automatic data detection enables detection of both date and address/phone number items; turning on automatic dash substitution enables automatic conversion of sequences of ASCII '-' to typographic dashes; turning on automatic text replacement enables automatic substitution of a variety of static text items based on user preferences; and turning on automatic spelling correction enables autocorrection of certain misspellings. In addition, text checking is now used for continuous spelling and grammar checking, automatic link detection, and automatic quote substitution. The -enabledTextCheckingTypes and -setEnabledTextCheckingTypes: methods allow the state of all of these forms of checking to be retrieved or set at once.
- (BOOL)isAutomaticDataDetectionEnabled;
- (void)toggleAutomaticDataDetection:(id)sender;
- (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag;
- (BOOL)isAutomaticDashSubstitutionEnabled;
- (void)toggleAutomaticDashSubstitution:(id)sender;
- (void)setAutomaticTextReplacementEnabled:(BOOL)flag;
- (BOOL)isAutomaticTextReplacementEnabled;
- (void)toggleAutomaticTextReplacement:(id)sender;
- (void)setAutomaticSpellingCorrectionEnabled:(BOOL)flag;
- (BOOL)isAutomaticSpellingCorrectionEnabled;
- (void)toggleAutomaticSpellingCorrection:(id)sender;
- (NSTextCheckingTypes)enabledTextCheckingTypes;
- (void)setEnabledTextCheckingTypes:(NSTextCheckingTypes)checkingTypes;
- (void)orderFrontSubstitutionsPanel:(id)sender;The -orderFrontSubstitutionsPanel: method brings forward a panel provided by NSSpellChecker that allows control over the various enabled substitution types. For example, it provides the user the ability to use the -checkTextInSelection: and -checkTextInDocument: action methods. Ordinarily text checking occurs in the background, and results that replace text are applied only to text that has been typed by the user, but these two methods cause the currently enabled text checking types to be applied immediately to the selection or to the document, respectively, and results that replace text are applied regardless of the origin of the text.
- (void)checkTextInSelection:(id)sender;
- (void)checkTextInDocument:(id)sender;
- (void)checkTextInRange:(NSRange)range types:(NSTextCheckingTypes)checkingTypes options:(NSDictionary *)opts;The checkTextInRange:types:options: and handleTextCheckingResults:forRange:orthography:wordCount: methods will be called by NSTextView as appropriate when text checking is started and when results are received, respectively. They are not intended to be called by clients, but can be overridden to observe or modify text checking behavior. The same sort of observation or modification will also be available to the delegate, using the two new NSTextView delegate methods
- (void)handleTextCheckingResults:(NSArray *)results
forRange:(NSRange)range
types:(NSTextCheckingTypes)checkingTypes
options:(NSDictionary *)options
orthography:(NSOrthography *)orthography
wordCount:(NSInteger)wordCount;
- (NSDictionary *)textView:(NSTextView *)viewthat will be called by checkTextInRange:types:options: and handleTextCheckingResults:forRange:orthography:wordCount: respectively.
willCheckTextInRange:(NSRange)range
options:(NSDictionary *)options
types:(NSTextCheckingTypes *)checkingTypes;
- (NSArray *)textView:(NSTextView *)view
didCheckTextInRange:(NSRange)range
types:(NSTextCheckingTypes)checkingTypes
options:(NSDictionary *)options
results:(NSArray *)results
orthography:(NSOrthography *)orthography
wordCount:(NSInteger)wordCount;
Bidirectional Text
Snow Leopard includes a number of new facilities to support the entry and editing of bidirectional text, such as Hebrew or Arabic. First, there is a new attributed string attribute, NSWritingDirectionAttributeName, intended to provide a higher-level alternative to the inclusion of explicit bidirectional control characters such as LRE, RLE, LRO, RLO, and PDF in text. This gives an attributed string equivalent to what in HTML would be expressed by such constructs as <span dir="ltr"></span>, <span dir="rtl"></span>, <bdo dir="ltr"></span>, and <bdo dir="rtl"></span>..The value of this attribute should be an array of NSNumbers, each of which should have a value of 0, 1, 2, or 3, considered as consisting of either NSWritingDirectionLeftToRight (= 0) or NSWritingDirectionRightToLeft (= 1) plus one of the new constants NSTextWritingDirectionEmbedding (= 0) or NSTextWritingDirectionOverride (= 2). This array represents a sequence of nested bidirectional embeddings or overrides, in order from outermost to innermost, with 0 (NSWritingDirectionLeftToRight | NSTextWritingDirectionEmbedding) corresponding to a LRE/PDF pair in plain text or <span dir="ltr"></span> in HTML, 1 (NSWritingDirectionRightToLeft | NSTextWritingDirectionEmbedding) corresponding to a RLE/PDF pair in plain text or a <span dir="rtl"></span>, 2 (NSWritingDirectionLeftToRight | NSTextWritingDirectionOverride) corresponding to a LRO/PDF pair in plain text or <bdo dir="ltr"></span>, and 3 (NSWritingDirectionRightToLeft | NSTextWritingDirectionOverride) corresponding to a RLO/PDF pair in plain text or <bdo dir="rtl"></span> in HTML.
In general, there should be very few situations that require the use of more than one level of bidirectional embeddings or overrides, but where necessary NSWritingDirectionAttributeName will support nesting, up to the maximum nesting depth provided for by the Unicode bidirectional algorithm. To determine the value for this attribute for a given character, note all of the embeddings or overrides that should apply to that character, in the order of their nesting from outermost to innermost, and for each one add an NSNumber to the array whose value specifies the type of the embedding or override as described above.
The intent is that the new attribute should be used in preference to inserting explicit bidirectional control characters in text, whenever text is being edited, even if rich text is not being used; that is, these attributes are considered to be useful even with plain text, because of the difficulties of editing text containing explicit control characters. When the contents of a text storage are actually converted into a plain-text file format for storage or interchange, then the attribute would need to be replaced by explicit bidirectional control characters, and likewise when loading plain text into a text storage for editing, explicit bidirectional control characters would need to be replaced by suitable values of the attribute. NSTextView will do this automatically on copy and paste, and NSAttributedString on conversion to and from external formats. In some cases, NSTextView may automatically apply this attribute to text that is typed in bidirectional contexts, where this is necessary to resolve ambiguities in directionality. Users of purely left-to-right text should never encounter this attribute.
In addition, there are new action methods defined on NSResponder and implemented in NSTextView for setting the values of both NSWritingDirectionAttributeName and the existing paragraph-level base writing direction defined on NSParagraphStyle.
- (void)makeBaseWritingDirectionNatural:(id)sender;
- (void)makeBaseWritingDirectionLeftToRight:(id)sender;
- (void)makeBaseWritingDirectionRightToLeft:(id)sender;
- (void)makeTextWritingDirectionNatural:(id)sender;The first three of these set the paragraph-level base writing direction on NSParagraphStyle to NSWritingDirectionNatural, NSWritingDirectionLeftToRight, or NSWritingDirectionRightToLeft respectively. The latter three of these set the new character-level NSWritingDirectionAttributeName. Specifically, makeTextWritingDirectionNatural: removes NSWritingDirectionAttributeName, and makeTextWritingDirectionLeftToRight: and makeTextWritingDirectionRightToLeft: set it to a single left-to-right or right-to-left embedding respectively. (No action methods are provided to set bidirectional overrides, or to nest embeddings or overrides.)
- (void)makeTextWritingDirectionLeftToRight:(id)sender;
- (void)makeTextWritingDirectionRightToLeft:(id)sender;
These action methods are intended to be used both as the targets of menu items and for key bindings. The intent is that the base writing direction methods should be the target of three menu items, and the text writing directions should be the target of three similar menu items, within the Writing Direction menu. This menu will be provided in the context menu when appropriate, and applications that are concerned with editing text should also supply it in their main menu. TextEdit has been updated to follow these guidelines. Default key bindings are also provided for these methods, but they are enabled only for users of Hebrew or Arabic, or those who have otherwise enabled a suitable preference in the Text tab of the Language & Text preferences.
The previous public action method for writing direction, -toggleBaseWritingDirection:, is no longer recommended for use, and will be formally deprecated in the future.
Additional Key Binding Methods
There are four new additional key binding methods, defined on NSResponder and implemented in NSTextView,- (void)moveToLeftEndOfLine:(id)sender;intended for binding to cmd- and cmd-shift-left/right arrow, in order to disambiguate the former similar methods expressed in terms of the beginning and end of the line in cases of bidirectional text.
- (void)moveToRightEndOfLine:(id)sender;
- (void)moveToLeftEndOfLineAndModifySelection:(id)sender;
- (void)moveToRightEndOfLineAndModifySelection:(id)sender;
There are also a number of other key binding methods, also defined on NSResponder and implemented in NSTextView, that have been present and bound to keys for some time, but not explicitly public:
- (void)moveToBeginningOfLineAndModifySelection:(id)sender;These methods have now been made public.
- (void)moveToEndOfLineAndModifySelection:(id)sender;
- (void)moveToBeginningOfParagraphAndModifySelection:(id)sender;
- (void)moveToEndOfParagraphAndModifySelection:(id)sender;
- (void)moveToEndOfDocumentAndModifySelection:(id)sender;
- (void)moveToBeginningOfDocumentAndModifySelection:(id)sender;
- (void)pageDownAndModifySelection:(id)sender;
- (void)pageUpAndModifySelection:(id)sender;
- (void)moveParagraphForwardAndModifySelection:(id)sender;
- (void)moveParagraphBackwardAndModifySelection:(id)sender;
- (void)scrollToBeginningOfDocument:(id)sender;
- (void)scrollToEndOfDocument:(id)sender;
- (void)insertSingleQuoteIgnoringSubstitution:(id)sender;
- (void)insertDoubleQuoteIgnoringSubstitution:(id)sender;
NSLayoutManager Methods
NSLayoutManager now has a method- (NSUInteger)characterIndexForPoint:(NSPoint)pointanalogous to glyphIndexForPoint:inTextContainer:fractionOfDistanceThroughGlyph:, but expressed in character index terms. The method returns the index of the character falling under the given point, expressed in the given container's coordinate system; if no character is under the point, the nearest character is returned, where nearest is defined according to the requirements of selection by mouse. However, this is not simply equivalent to taking the result of the corresponding glyph index method and converting it to a character index, because in some cases a single glyph represents more than one selectable character, for example an fi ligature glyph. In that case, there will be an insertion point within the glyph, and this method will return one character or the other, depending on whether the specified point lies to the left or the right of that insertion point. In general, this method will return only character indexes for which there is an insertion point. The partial fraction is a fraction of the distance from the insertion point logically before the given character to the next one, which may be either to the right or to the left depending on directionality.
inTextContainer:(NSTextContainer *)container
fractionOfDistanceBetweenInsertionPoints:(CGFloat *)partialFraction;
In addition, there is a new method
- (void)fillBackgroundRectArray:(NSRectArray)rectArray count:(NSUInteger)rectCount forCharacterRange:(NSRange)charRange color:(NSColor *)color;used as a primitive for rect drawing by -drawBackgroundForGlyphRange:atPoint: for actually filling rects with a particular background color, whether due to a background color attribute, a selected or marked range highlight, a block decoration, or any other rect fill needed by that method. As with -showPackedGlyphs:..., the character range and color are merely for informational purposes; the color will already be set in the graphics state. If for any reason you modify it, you must restore it before returning from this method. You should never call this method, but you might override it. The default implementation will simply fill the specified rect array. The graphics operation used for this fill is controlled by a link check; for compatibility reasons, it will be NSCompositeCopy for applications linked prior to Snow Leopard, and NSCompositeSourceOver for applications linked on Snow Leopard or later. Applications can control the operation used, or modify the drawing, by overriding this method in an NSLayoutManager subclass.
NSTextView Undo Coalescing
NSTextView now has a method- (BOOL)isCoalescingUndo;to go along with the previous -breakUndoCoalescing. The new method permits clients to determine when undo coalescing is in progress.
NSTextList Starting Item Number
NSTextList now has two methods- (void)setStartingItemNumber:(NSInteger)itemNum;for supplying a starting item number for a given list. The default value is 1. This value will be used only for ordered lists, and ignored in other cases.
- (NSInteger)startingItemNumber;
NSAttributedString Constants
The existing NSDocumentTypeDocumentAttribute and NSDocumentTypeDocumentOption express document types in terms of a set of constants particular to NSAttributedString. In Snow Leopard, there are new constants, NSFileTypeDocumentAttribute and NSFileTypeDocumentOption, that express the same information in terms of system-wide UTIs. Where these are used for output, in expressing the type of a document that has been read, both file type and document type will be provided; as input, however, when indicating the type to be written or the type to be forced on loading, the two are mutually exclusive.NSAttributed string also has two new metadata attributes, NSManagerDocumentAttribute and NSCategoryDocumentAttribute.
NSTableView / NSOutlineView - General updates
NSTableView and NSOutlineView now support autosizing of table columns when you double click on the resize divider that is directly to the right of a given column. By default, NSTableView iterates every row in the table, accesses a cell via preparedCellAtRow:column:, and requests the -cellSize to find the appropriate largest width to use. For performance, it is recommended that the programmer implement a new delegate method (-tableView:sizeToFitWidthOfColumn: or -outlineView:sizeToFitWidthOfColumn:) to quickly determine the size.The indentationPerLevel property on NSOutlineView now works better for applications linked on SnowLeopard or higher. Previously, it would indent every level by indentationPerLevel -- including the items at level 0. This has the undesired side effect of indenting too much when the value was large, and not enough to show a disclosure triangle when the value was small. In SnowLeopard-linked applications, if the indentationPerLevel is greater than 0, level-0 will have a specific width that contains enough room for the disclosure triangle based on the current selectionHighlightStyle. All other levels will properly be indented by 'indentationPerLevel'.
NSTableView now has a method to correctly invalidate cells for redrawing: -reloadDataForRowIndexes:columnIndexes:. The method will effeciently redisplay only the row/columns that are actually visible.
NSTableView now supports restricting the reordering of particular columns with a new delegate method: -tableView:shouldReorderColumn:toColumn:.
For applications linked on SnowLeopard and later, -cellSizeForBounds: (and subsequently -cellSize) will return the correct width for NSTableHeaderCells. Previously, it would not return a size large enough for the sort indicator, if the associated NSTableColumn had a sortDescriptorPrototype set.
Mac OS 10.5 Leopard introduced sub-cell focusing. SnowLeopard now allows developer control over the sub-cell focusing with several methods. -shouldFocusCell:atColumn:row: can be overridden to control whether or not a cell can be focused. -focusedColumn is the particular column that has focus. Note that the focus is only shown for the -selectedRow (if available). Finally, focused cells allow clicking via the space bar, and a method is provided for developers to call or override: -performClickOnCellAtColumn:row:.
Leopard introduced custom menu highlighting when a menu is assigned to an NSTableView instance. Subclassers that override -menuForEvent: and return a new menu instance would cause NSTableView to incorrectly highlight. Previously, it would watch NSMenuDidEndTrackingNotification for just one menu, and now it watches for all menus to correct this problem. For Leopard compatibility, it is recommended that you don't create a new menu instance, and instead use the current one as shown in the DragNDropOutlineView demo application.
On Leopard, NSOutlineView's expandItem: method started allowing 'nil' as an option to expand all items from the root. Before Leopard, this would cause an assertion. If you are deploying your application on a release before Leopard, you should not use expandItem:nil, as it will assert.
NSTableViewSelectionHighlightStyle updates: There is now a highlight style that draws no highlighting at all, but still allows rows to be selected: NSTableViewSelectionHighlightStyleNone. The NSTableViewSelectionHighlightStyleSourceList has also been updated to no longer de-select all rows when clicking on empty space in the table view.
For applications linked on SnowLeopard or later, the NSTableHeaderCell will always draw the sort indicator on the right side, even if the alignment for the cell is set to the right side. Previously, it would draw it on the left when the alignment was set to the right.
For applications linked on SnowLeopard or later, -rowAtPoint: will correctly return -1 if the point passed is outside of the horizontal bounds of the view. Previously, only the vertical bounds was checked.
NSTableView now has a new draggingDestinationFeedbackStyle property. One of the NSTableViewDraggingDestinationFeedbackStyle values is acceptable for it. The NSTableViewDraggingDestinationFeedbackStyleSourceList option is appropriate for source lists, or things that appear as source lists but don't use the source list highlighting style (such as AddressBook). NSTableViewDraggingDestinationFeedbackStyleRegular is now the standard default look, which differs from Leopard by drawing a solid round-rect blue background around drop target rows. For compatibility, this style will be the default for applications that link on SnowLeopard or later. Other applications will use NSTableViewDraggingDestinationFeedbackStyleSourceList, which was the default look for Leopard. Applications that target Leopard can still get the SnowLeopard look by first doing a [tableView respondsToSelector:@selector(setDraggingDestinationFeedbackStyle:)] check before calling the method. There is also a NSTableViewDraggingDestinationFeedbackStyleNone option to show "no drop feedback". This style is appropriate for subclasses that which to control the drawing themselves and disable the default NSTableView drawing.
Prior to SnowLeopard, NSTableView and NSOutlineView would always attempt to enable live-resize content caching, even if a subclass overrode preservesContentDuringLiveResize and returned NO. On SnowLeopard, it now works to return NO from preservesContentDuringLiveResize to avoid live-resize image caching. Prior to SnowLeopard, one could disable it by overriding -drawRect: and doing nothing but calling [super drawRect:rect]. It is recommended to do both if your application is targeting prior releases.
For applications that link on SnowLeopard and higher, NSOutlineView will correctly set the "isItemExpanded:item" bit for item to YES only AFTER sending the outlineViewItemWillExpand: notification. Similarly, NSOutlineView will correctly set the "isItemExpanded:item" bit for item to NO only AFTER sending the outlineViewItemWillCollapse: notification. For applications that link against an OS prior to SnowLeopard, the bit will be incorrectly set before sending the notification.
It has always been incorrect to return a negative row count from NSTableView and NSOutlineView delegate methods. For applications that link on SnowLeopard and higher, returning a negative count will now assert to assist the developer in debugging such problems.
For applications linked on SnowLeopard and later, the disclosure triangle (outline cell) in an NSOutlineView will correctly be white when the row is selected. It is possible to override this behavior by implementing -willDisplayOutlineCell: to explicitly set the backgroundStyle of the outline cell to NSBackgroundStyleLight. IE:
- (void)outlineView:(NSOutlineView *)outlineView willDisplayOutlineCell:(id)cellNSTableView and NSOutlineView will automatically add attributes to plain text for the source list (NSTableViewSelectionHighlightStyleSourceList) highlight style (selectionHighlightStyle). On Leopard, these attributes were added before the backgroundStyle was set on the cell, sometimes producing incorrectly colored cells on rows that immediately followed a group-row cell. This has been fixed on SnowLeopard, and can be worked around on Leopard with the following delegate method:
forTableColumn:(NSTableColumn *)tableColumn item:(id)item {
// This explicitly makes the disclosure triangles always be dark
[cell setBackgroundStyle:NSBackgroundStyleLight];
}
- (NSCell *)outlineView:(NSOutlineView *)outlineViewOn Leopard when you right click on a row in NSTableView that is a "group row" (ie: the column is -1, indicating it spans across all the rows), it will incorrectly call preparedCellAtColumn:row: with a column value that is not -1. This has been fixed on SnowLeopard to properly call it with -1, but applications that need to target Leoaprd will need to take this change into consideration and be prepared for the incorrect call.
dataCellForTableColumn:(NSTableColumn *)tableColumn item:(id)item {
NSCell *result = tableColumn != nil ? [tableColumn dataCell] : nil;
if (result != nil) {
[result setBackgroundStyle:NSBackgroundStyleLight];
}
return result;
}
NSSavePanel / NSOpenPanel - NSURL and Blocks
The NSSavePanel and NSOpenPanel now have NSURL properties, delegate methods. In addition, new block-based methods are available for displaying the panels. These versions should be used instead of the non-NSURL versions, which will be deprecated in a future release. The old versions and their replacements:NSSavePanel:
filename -> URL
directory -> directoryURL
requiredFileType -> Always the first item in allowedFileTypes
panel:isValidFilename: -> panel:validateURL:error:
panel:directoryDidChange: -> panel:didChangeToDirectoryURL
panel:compareFilename:width:caseSensitive: -> Deprecated and should not be used for performance reasons
panel:shouldShowFilename: -> panel:shouldEnableURL:
beginSheetForDirectory:file:modalForWindow:modalDelegate:didEndSelector:contextInfo: -> beginSheetModalForWindow:completionHandler:
runModalForDirectory:file: -> runModal
NSOpenPanel:
filenames -> URLS
beginSheetForDirectory:file:types:modalForWindow:modalDelegate:didEndSelector:contextInfo: -> beginSheetModalForWindow:completionHandler:
beginForDirectory:file:types::modelessDelegate:didEndSelector:contextInfo: -> beginSheetModalForWindow:completionHandler:
runModalForDirectory:file:types: -> runModal
runModalForTypes: -> runModal
You'll notice that there are now three acceptable ways to show the open or save panel:
- (NSInteger)runModal;The methods no longer take a directory or initial file type. Instead, the properties can be set prior invoking the above methods. Prior to SnowLeopard, there was no way to set the initial filename; this can now be done with:
- (void)beginSheetModalForWindow:(NSWindow *)window completionHandler:(void (^)(NSInteger result))handler;
- (void)beginWithCompletionHandler:(void (^)(NSInteger result))handler;
- (NSString *)nameFieldStringValue;Calling setNameFieldStringValue: will cause the 'value' to be processed slightly to be an acceptable file name, possibly including hiding the extension based if -isExtensionHidden is set to YES.
- (void)setNameFieldStringValue:(NSString *)value;
You'll notice that the NSOpenPanel version has no way to show the enabled "fileTypes". Applications that link on SnowLeopard can now use allowedFileTypes/setAllowedFileTypes:, which was previously not used for the NSOpenPanel. Please refer to the header comments for more information.
Example use of the new block based methods:
// Run a modeless open panel for pdf file types:
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
openPanel.allowedFileTypes = [NSArray arrayWithObject:@"com.adobe.pdf"];
[openPanel beginWithCompletionHandler:^(NSInteger result) {
if (result) {
NSLog(@"Picked: %@", openPanel.URLs);
}
}];
// Run a sheet modal for 'window' that allows saving as a jpegCompatibility notes: If a delegate implements both the old and new delegate methods, the new delegate methods will be used on SnowLeopard, while the older delegate methods will be used on older versions of the OS.
NSSavePanel *savePanel = [NSSavePanel savePanel];
savePanel.allowedFileTypes = [NSArray arrayWithObject:@"jpg"];
savePanel.nameFieldStringValue = @"foo.jpg";
[savePanel beginSheetModalForWindow:window completionHandler:^(NSInteger result) {
if (result) {
NSLog(@"Save as: %@", savePanel.URL);
}
}];
NSSavePanel / NSOpenPanel - General Updates
The NSSavePanel and NSOpenPanel now have a keyboard shortcut to show hidden files: cmd-shift-period. Hidden files will be shown only while that instance is open.Using only UTIs to enable application file types in the open panel was not working on Leopard (com.apple.application-bundle, com.apple.application-bundle, com.apple.application-file). The work around was to include the "app" extension in the enabled file types. On SnowLeopard, all the UTIs work correctly, but for applications targeting Leopard as the minimum operating system, the "app" extension should also be included.
The NSOpenPanel now supports Quick Look. Select any files and press the space bar to invoke Quick Look.
The list view mode for the open and save panels now allow multiple columns to be viewed. Right click on the column header to show or hide them.
The open and save panel now allows connecting to shared servers. The sidebar also now contains a SEARCH FOR section.
The save panel now respects the Finder option for "Show all file extensions". If it is set, then the "Hide Extension" checkbox will be hidden and the extension will always be shown by always returning YES from -isExtensionHidden.
For applications that link on SnowLeopard and higher: NSSavePanel now tracks the accessoryView's frame. The programmer can dynamically make changes to the frame and the panel will properly layout. In addition, animated changes can be done using the animator proxy, such as: [[accessoryView animator] setFrame:frame].
NSPathControl / NSPathCell
The NSPathCell would incorrectly call [openPanel setCanChooseDirectories:YES] when about to show the NSOpenPanel. On 10.6, it will correctly set the value to YES only if the allowedTypes contains 'public.folder' or is nil.For SnowLeopard linked applications, NSPathCell will correctly return an appropriate height from - (NSSize)cellSizeForBounds:(NSRect)bounds for NSPathStyleStandard and NSPathStyleNavigationBar.
NSPathComponentCell (and subsequently NSPathCell and NSPathControl) no longer encodes the image property when the URL property is a file URL. Instead, it grabs the appropriate image from NSWorkspace. Previously, if the file didn't exist, the previously set images would show up. Now, the appropriate current images will be used from the file system, and if the file at URL no longer exists, it will show a generic image. You must re-save nibs on SnowLeopard for the new encoding to take effect.
NSPathCell will now properly highlight the NSPathComponentCell that is clicked upon when a context menu is shown. Correctly setting a menu can be done in one of two ways: set the -menu property for the NSPathCell (or the NSPathControl - it forwards the setMenu: to the cell). In the menu's delegate, one can access the clickedPathComponentCell and update the menu based on the cell that was clicked. In the menu's action, one can access the clickedPathComponentCell and process the action based on the item that was initially clicked. Alternatively, each NSPathComponentCell can have the -menu property set, and the appropriate menu will be returned from NSPathCell via -menuForEvent:inRect:ofView:. Subclasses of NSPathControl, NSPathCell, and NSPathComponentCell should not override -menuForEvent: / -menuForEvent:inRect:ofView: if they wish to opt into this behavior. Note that this behavior should only be used on applications that target SnowLeopard or higher, as it will not work properly on Leopard.
NSBrowser
NSBrowser now goes through the public methods -moveLeft: and -moveRight: when changing columns in response to a left/right keyboard arrow.On SnowLeopard linked applications, -scrollColumnToVisible: will now always attempt to scroll the start of the column to the visible rect. Previously, it would do nothing if part of the column was already visible.
On SnowLeopard linked applications, NSBrowser will return YES from -acceptsFirstResponder, unless the browser has been set to refuse first responder status with: -[browser setRefusesFirstResponder:YES]. Previously, it would only accept first responder status if it had at least one column that accepted first responder status. This made it impossible to set an NSBrowser as the first responder in IB.
On SnowLeopard linked applications, NSBrowser will correctly accept an empty index set to selectRowIndexes:inColumn: to allow deselection of all indexes in a given column. Previously an empty index set would be ignored.
NSBrowser - Context Menu
NSBrowser now properly supports the menu property. -[NSBrowser setMenu:] is propagated to subviews of NSBrowser matrices where appropriate to ensure the menu is available at all locations. Customizing -[NSBrowser menuForEvent:] will have no effect, as the individual subviews handle the menu. If you need to customize the menu, consider using a NSMenu delegate instead.While the contextual menu is displayed, you may call -[NSBrowser clickedRow] and -[NSBrowser clickedColumn] to determine which cell was underneath the mouse when the context menu was displayed. The return value of both these functions will be -1 if no cell was clicked.
NSBrowser - Convenience methods
In Leopard and earlier releases, the default column width of an NSBrowser was set by passing the column index -1 to -[NSBrowser setWidth:ofColumn:], and retrieved by passing -1 to -[NSBrowser widthOfColumn:]. There are now first-class methods for accessing this property: -[NSBrowser setDefaultColumnWidth:] and -[NSBrowser defaultColumnWidth].NSBrowser - Appearance
Browsers no longer leave a white gap at the bottom right side of the scroll bar unless it is necessary to do so, such as when displaying a window resize corner.When targeting a drop at the entire browser by setting the drop column to -1, the a highlight rectangle will be drawn around the entire control.
NSApplication - Full keyboard access
When implementing compound controls such as NSTableView, it can be useful to get the current state of full keyboard access to determine what your control’s Tab behavior should be. NSApplication now has a -isFullKeyboardAccessEnabled method which will return the current state of the setting from the Keyboard system preference pane.Bindings and Processing KVO Notifications
NSBindingDebugLogLevel
The Binding debug log level now recognizes values greater than 1. With the user default set to 2, Cocoa Bindings will log whenever a KVO notification gets processed. The result looks like:"Cocoa Bindings: <YourBoundView: 0x101ed6930> binding 'isIndeterminate' processing observer notification from object YourObservedObject 0x101ebed90 for keyPath your.observed.keyPath."
Cocoa Java
The Java runtime for running Cocoa applications has been removed in SnowLeopard.NSColorSpace
This new method returns the list of color spaces available on the system that are displayed by the color panel, in the order they are displayed in the color panel. Doesn't return arbitrary color spaces which may have been created on the fly, or spaces without user displayable names. Pass model==NSUnknownColorSpaceModel to get all color spaces. Empty array is returned if no color spaces are available for the specified model.+ (NSArray *)availableColorSpacesWithModel;
NSColorSpace
This new method returns a gray color space with 2.2 gamma. It mirrors the kCGColorSpaceGenericGrayGamma2_2 added in Quartz:+ (NSColorSpace *)genericGamma22GrayColorSpace;
NSColor
NSColors will now always properly compare isEqual: (and return the same hash) after archiving/unarchiving. Previously in some cases (and more often in 64-bit), a color would not compare isEqual: with its unarchived counterpart.Note that the individual component values will not all compare exactly the same after unarchiving, but they should compare the same if cast to float.
64-bit Conversion
There is now an improved tool for converting Cocoa applications to 64-bit:/Developer/Extras/64BitConversion/ConvertCocoa64
The underlying functionality is very similar to the tops script that shipped with Leopard; however, this is a Ruby script that will run tops for you on the specified source files, and also clean up some of the unnecessary warnings that tops inserts in your code.
Use of this (or some customized variant appropriate for your needs, or just some other automated tool of your choice) is highly recommended for the first-pass bulk conversion of Cocoa sources to 64-bit. In our experience, one-by-one manual conversion attempts can lead to errors stemming from copy/paste or oversight, and automated conversions avoid these sorts of problems.
NSApplication
-userInterfaceLayoutDirection is a new NSApplication interface that returns the default layout directionality of general user interface flow for the running application. The method returns NSUserInterfaceLayoutDirectionRightToLeft when running with localizations such as Arabic or Hebrew that should have the user interface layout origin on the right edge of the coordinate system.NSBezierPath
-cachesBezierPath and -setCachesBezierPath: methods are now officially declared to be deprecated.NSCell
NSCell now has -userInterfaceLayoutDirection and -setUserInterfaceLayoutDirection:. This new property describes the in-cell layout directionality. For NSCell subclasses that have multiple visual components in a single cell instance, this property should specify the directionality or flow of components.A new NSCell method, -fieldEditorForView:, is now invoked by -[NSWindow fieldEditor:forObject:] method allowing NSCell subclasses to easily provide a custom field editor object. If -fieldEditorFoView: returns a non-nil value, it's used for the cell object editing. The default implmentation returns nil.
A new NSCell property, -usesSingleLineMode, determines the layout behavior for text cells. When -usesSingleLineMode == YES, the Cocoa Text System forces to the text in a single line by ignoring line/paragraph separators and treating wrapping line breaking modes as the clipping mode. The field editor object is expected to filter line/paragraph separator characters entering into the cell value from user actions. Also, the baseline position for the text becomes fixed.
NSFontManager
The -changeAttributes: action message is now targeted to [NSFontManager target].NSGraphicsContext
-focusStack and -setFocusStack: methods are deprecated.There is a new NSImageInterpolation type, NSImageInterpolationMedium, added.
NSProgressIndicator
-animationDelay, -setAnimationDelay:, and -animate: methods are deprecated.NSSearchFiedlCell
All new NSSearchFieldCell instances use single-line mode. NSSearchFieldCell instances unarchived from nib files created on pre-10.6 systems with the clipping line break mode are interpreted to use single-line mode.NSSecureTextField
NSSecureTextField accepts input from various non-keyboard input sources such as Character Palette.NSRulerView
NSRulerView now layout markers from the right when attached to a right-to-left wirting direction paragraph style.NSTextFieldCell
NSTextFieldCell now fills its background with NSCompositeSourceOver instead of NSCompositeCopy.NSTextInputClient
There is a new optional method, -drawsVerticallyForCharacterAtIndex:, that can inform the Text Input system whether the protocol conforming client renders the character at index vertically.NSTextInputContext
The new NSTextInputContext class represents the interface to the Cocoa Text Input system. It represents a state or context unique to its client object such as the key binding state, input method communication session, etc. Each NSTextInputClient compliant object (typically an NSView subclass) carries its own NSTextInputContext instance. The default implementation of -[NSView inputContext] manages an instance automatically if the view subclass conforms to the NSTextInputClient protocol.The client object needs to interact with its NSTextInputContext in order to handle text inputs. The clients are expected to send -handleEvent: message for all key/mouse events received. The -[NSView interpretKeyEvents:] method sends the message for key events. Also, -invalidateCharacterCoordinates should be send whenever the visual location of the client changes (i.e. window frame change, view scrolling, etc).
The original Text Input system classes and protocol
The original Cocoa Text Input system introduced in OS X v10.0 is now deprecated. NSInputServer is replaced by the Input Method Kit framework. NSInputManager functionalities are integrated into the NSTextInputContext class. The NSTextInput protocol is replaced by the NSTextInputClient protocol introduced in OS X v10.5 Leopard.Mappings for deprecated NSInputManager methods:
+cycleToNextInputLanguage: and +cycleToNextInputServerInLanguage: are covered by more direct input source APIs such as the selectedKeyboardInputSource property.
-markedTextAbandoned: is replaced by -discardMarkedText
-handleMouseEvent: is replaced by -handleEvent:.
-markedTextSelectionChanged:client:, -wantsToInterpretAllKeystrokes, -wantsToHandleMouseEvents, and -wantsToDelayTextChangeNotifications are no longer necessary.
NSTextTab
NSDecimalTabStopType tab stops returns NSNaturalTextAlignment from -alignment for applications linked against OS X v10.6 SnowLeopard libraries. This allows correct decimal tab layout in right-to-left paragraph style. Applications can specify NSDecimalTabUsesNaturalAlignment == YES using NSUserDefaults to force the new behavior.NSTokenField
The behavior for handling whitespace characters such as space, tab, etc between tokens and tokenizing characters have changed in OS X v10.5 Leopard. In 10.4, those characters were silently trimmed before tokenizing. With 10.5, those characters are left untouched and included into tokens. If the whitespace trimming behavior is desirable, the delegate could implement -tokenField:representedObjectForEditingString: and perform the trimming in the method.In order to preserve binary compatibility with Tiger, the first post-Leopard AppKit now performs the trimming unless -tokenField:representedObjectForEditingString: is implemented by its delegate or the binary is linked against pre-Leopard releases.
Notes specific to MacOS X 10.5
New features and significant changes in AppKit
- 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
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 this. Most are due to the introduction of two new types, NSInteger and NSUInteger, as a way to represent "address-sized" integers on both 32 and 64-bit. NSInteger is defined as "int" on 32-bit and "long" on 64-bit, and NSUInteger is its unsigned counterpart. Almost all Cocoa-based APIs have been upgraded to use NSInteger or NSUInteger in place of int or unsigned int. NSInteger is analogous to CoreFoundation's CFIndex type.(Note that early in Leopard, NSInteger and NSUInteger were named NSInt and NSUInt, respectively. These old names have been removed before final release of Leopard.)
Moving forward, applications should be using these new types (and CGFloat - see below) instead of int, unsigned int, and float, since this will make an eventual move to 64-bit much easier. We recommend this even for apps that need to run on Tiger; they can accomplish this with their own, Tiger-only definitions of these types.
We have a conversion script in /Developer/Extras/64BitConversion to help convert Cocoa applications to 64-bit. Information about this script and the Cocoa 64-bit effort in general can be found in the 64-Bit Transition Guide for Cocoa.
In general it should be possible to use the same source base for both the 32 and 64-bit versions of an application or framework. Running this script on your source base to convert your sources to 64-bit should still enable them to build and run correctly under 32-bit. If needed, you can do:
#if __LP64__as a way to do 64-bit specific code.
...
#endif
In APIs where the term "int" appeared as a part of the method name (for instance, "intValue"), the term "integer" is used to represent this new NSInteger type, while "int" continues to refer to the native int type (which is 32-bit under both 32 and 64). Thus methods like intValue, numberWithInt:, scanInt:, etc continue to take or return ints, while methods such as integerForKey: in NSUserDefaults have been changed to take NSInteger. We are adding a number of counterpart methods in Foundation and AppKit that take or return NSInteger or NSUInteger arguments.
The new methods in Foundation are:
NSCoder:
- (void)encodeInteger:(NSInteger)intv forKey:(NSString *)key;NSString:
- (NSInteger)decodeIntegerForKey:(NSString *)key;
- (NSInteger)integerValue;NSScanner:
- (BOOL)scanInteger:(NSInteger *)ptr;NSNumber:
- (NSInteger)integerValue;For the AppKit, this means the following new methods in both NSCell and NSControl:
- (NSUInteger)unsignedIntegerValue;
- (id)initWithInteger:(NSInteger)value;
- (id)initWithUnsignedInteger:(NSUInteger)value;
+ (NSNumber *)numberWithInteger:(NSInteger)value;
+ (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value;
- (NSInteger)integerValue;We also have the following new constants in NSObjCRuntime.h:
- (void)setIntegerValue:(NSInteger)val;
- (void)takeIntegerValueFrom:(id)sender;
#define NSIntegerMax LONG_MAX
#define NSIntegerMin LONG_MIN
#define NSUIntegerMax ULONG_MAX
Note that by design, keyed archiving's handling of integral types is not strict. An integer quantity written with any of encodeInteger:forKey:, encodeInt32:forKey:, or encodeInt64:forKey: can be read back using any of the integer decode methods. If the value is too large to read using the specified decode method, an exception is raised.
For most integral values, we recommend the use of encodeInteger:forKey: and decodeIntegerForKey:. For values whose ranges are larger than what 32-bit signed integers can hold, the Int64: variants continue to be the more appropriate choice, even on 32-bit.
There are additional archiving and other considerations in the presence of 64-bit changes in our APIs. The 64-bit Transition Guide referred to above has more information on this topic and more.
CGFloat
Another major change in Cocoa APIs is the introduction of the CGFloat type in Quartz. This replaces the use of float, and is defined as double for 64-bit. Note that this is not a change dictated by the 64-bit move; however, we are taking advantage of the move to introduce this new type. The purpose of CGFloat is to provide higher precision and range in graphical values, for 64-bit applications. This type replaces the use of all graphical float types in Cocoa APIs, including those in Foundation's NSGeometry.h.Another change in NSGeometry.h is the redeclaration of NSRect, NSPoint, and NSSize using the Quartz counterparts, CGRect, CGPoint, and CGSize. Unfortunately, due to binary compatibility considerations, this change is done for 64-bit only. Note that the Objective C type signatures of these types thus differs on 64-bit from that on 32-bits.
Enum name removal
As a part of 64-bit clean-up, we added explicitly sized types where we were previously using enums. For instance, we went from:typedef enum NSAlertStyle {
NSWarningAlertStyle = 0,
NSInformationalAlertStyle = 1,
NSCriticalAlertStyle = 2
} NSAlertStyle;
toenum {
NSWarningAlertStyle = 0,
NSInformationalAlertStyle = 1,
NSCriticalAlertStyle = 2
};
typedef NSUInteger NSAlertStyle;
The latter makes sure that NSAlertStyle remains a fixed size and signedness no matter how the enum contents change.In doing this, we removed the enum names from the enum declarations, so any attempt to use "enum NSAlertStyle" will now fail. Please switch to using the typedef instead, which is fixed size.
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
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
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 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 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
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];AppKit responds by setting up a Core Animation renderer that animates and composites the layer tree on a background thread.
[view setWantsLayer:YES];
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
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 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
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
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];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:
[[NSAnimationContext currentContext] setDuration:0.25];
[[someDescendantOfTheRootView animator] setFrame:newFrame];
[NSAnimationContext endGrouping];
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];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 currentContext] setDuration:1.0]; // Animate enclosed operations with a duration of 1 sec
[[aView animator] setFrame:newFrame];
[NSAnimationContext endGrouping];
[NSAnimationContext beginGrouping];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 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];
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0.0]; // Makes value-set operations take effect immediately
[aViewOrMaybeAnAnimator setFrame:newFrame];
[NSAnimationContext endGrouping];
New NSView properties
Leopard AppKit adds some new properties to NSViews, which are described below with their corresponding accessor methods.- (void)setWantsLayer:(BOOL)flag;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.
- (BOOL)wantsLayer;
- (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;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.
- (CGFloat)alphaValue;
- (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.
- (void)setShadow:(NSShadow *)shadow;
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;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.
- (void)setCompositingFilter:(CIFilter *)filter;
- (NSArray *)contentFilters;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.
- (void)setContentFilters:(NSArray *)filters;
- (NSArray *)backgroundFilters;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.
- (void)setBackgroundFilters:(NSArray *)filters;
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];This was sometimes used to replace some animated content in response to a timer callback, for example.
/* Perform some drawing. */
[view unlockFocus];
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
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
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
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;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.
- (NSPoint)convertPointToBase:(NSPoint)aPoint;
- (NSSize)convertSizeToBase:(NSSize)aSize;
- (NSRect)convertRectFromBase:(NSRect)aRect;
- (NSPoint)convertPointFromBase:(NSPoint)aPoint;
- (NSSize)convertSizeFromBase:(NSSize)aSize;
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;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.)
- (void)viewDidUnhide;
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 NSViewSo an override of this method could do:
- (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
- (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
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;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.
- (void)setAnimations:(NSDictionary *)dict;
- (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
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
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.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.
glViewport(0, 0, bounds.size.width, bounds.size.height);
}
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.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.)
glViewport(0, 0, boundsInPixelUnits.size.width, boundsInPixelUnits.size.height);
}
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;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.
- (NSArray *)rowTemplates;
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;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.
- (NSView *)view;
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 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
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]
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
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 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
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
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
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;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.
- (void)removeAccessoryController:(NSViewController *)accessoryController;
- (NSArray *)accessoryControllers;
Slightly different new methods have also been added to the NSPrintPanel class:
- (void)addAccessoryController:(NSViewController<NSPrintPanelAccessorizing> *)accessoryController;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:
- (void)removeAccessoryController:(NSViewController<NSPrintPanelAccessorizing> *)accessoryController;
- (NSArray *)accessoryControllers;
- (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;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 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.
- (NSPrintPanelOptions)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;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.
- (NSString *)defaultButtonTitle;
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;The HTML help anchor for the print panel. You can override the standard anchor of the print panel's help button.
- (NSString *)helpAnchor;
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
In previous versions of 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;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 * /* PMPageFormat */)PMPageFormat;
- (void * /* PMPrintSettings */)PMPrintSettings;
- (void)updateFromPMPageFormat;Given that the NSPrintInfo's PMPageFormat or PMPrintSettings has been changed by something other than the NSPrintInfo itself, updates the NSPrintInfo to be consistent.
- (void)updateFromPMPrintSettings;
NSPrintInfo's Key-Value Coding and Key-Value Observing Compliance
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 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;If a job title is set it overrides anything that might be gotten by sending the printed view an [NSView(NSPrinting) printJobTitle] message.
- (NSString *)jobTitle;
In previous versions of 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 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 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 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: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.
-beginForDirectory:file:types:modelessDelegate:didEndSelector:contextInfo:
-runModalForDirectory:file:types:
-runModalForTypes:
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
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
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 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;join these methods, which might be deprecated in a future release of OS X, but are not yet:
+ (NSArray *)imageUnfilteredTypes;
+ (NSArray *)imageFileTypes;(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:].)
+ (NSArray *)imagePasteboardTypes;
+ (NSArray *)imageUnfilteredFileTypes;
+ (NSArray *)imageUnfilteredPasteboardTypes;
Support for UTIs in NSImageRep
In NSImageRep, these new methods:+ (Class)imageRepClassForType:(NSString *)type;join these methods, which might be deprecated in a future release of OS X, but are not yet:
+ (NSArray *)imageTypes;
+ (NSArray *)imageUnfilteredTypes;
+ (Class)imageRepClassForFileType:(NSString *)type;(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.)
+ (Class)imageRepClassForPasteboardType:(NSString *)type;
+ (NSArray *)imageFileTypes;
+ (NSArray *)imagePasteboardTypes;
+ (NSArray *)imageUnfilteredFileTypes;
+ (NSArray *)imageUnfilteredPasteboardTypes;
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;replace these deprecated methods:
+ (NSArray *)textUnfilteredTypes;
+ (NSArray *)textFileTypes;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.
+ (NSArray *)textPasteboardTypes;
+ (NSArray *)textUnfilteredFileTypes;
+ (NSArray *)textUnfilteredPasteboardTypes;
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 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 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 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;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.
- (id)openDocumentWithContentsOfURL:(NSURL *)absoluteURL display:(BOOL)displayDocument error:(NSError **)outError;
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 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 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 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
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
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;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.
- (void)setCanBecomeVisibleWithoutLogin:(BOOL)flag;
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;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.
- (NSWindowSharingType)sharingType;
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;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.
- (NSWindowBackingLocation)preferredBackingLocation;
- (NSWindowBackingLocation)backingLocation;
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;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.
- (NSURL *)representedURL;
- (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 *)windowNSWindow 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.
shouldDragDocumentWithEvent:(NSEvent *)event
from:(NSPoint)dragImageLocation
withPasteboard:(NSPasteboard *)pasteboard;
- (NSDockTile *)dockTile;-[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.
The action of the standard toolbar button now goes through the public toggleToolbarShown: method, passing the toolbar button as the sender.
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.
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;
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;The setCanBeVisibleOnAllSpaces/canBeVisibleOnAllSpaces API, introduced earlier in Leopard, is deprecated in favor of setCollectionBehavior:/collectionBehavior
- (NSWindowCollectionBehavior)collectionBehavior;
-(void)setCanBeVisibleOnAllSpaces:(BOOL)flag AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER_BUT_DEPRECATED;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.
-(BOOL)canBeVisibleOnAllSpaces AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER_BUT_DEPRECATED;
Sheets
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
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;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.
- (BOOL)showsSuppressionButton;
- (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;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.
- (NSView *)accessoryView;
- (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;Please refer to <AppKit/NSDockTile.h> and the documentation for further details on NSDockTile.
@end
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>Please refer to <AppKit/NSTrackingArea.h> and the documentation for further details on NSTrackingArea.
- (NSTrackingArea *)initWithRect:(NSRect)rect
options:(NSTrackingAreaOptions)options
owner:(id)owner
userInfo:(NSDictionary *)userInfo;
- (NSRect)rect;
- (NSTrackingAreaOptions)options;
- (id)owner;
- (NSDictionary *)userInfo;
@end
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
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;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.
+ (NSEvent *)eventWithEventRef:(const void * /* EventRef */)eventRef;
- (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.
+ (NSEvent *)eventWithCGEvent:(CGEventRef)cgEvent;
+ (void)setMouseCoalescingEnabled:(BOOL)flag;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.
+ (BOOL)isMouseCoalescingEnabled;
NSScrollWheel events will now be sent to the window under the mouse, whether or not the window is active. In previous version of 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;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.
- (BOOL)allowsNonContiguousLayout;
- (BOOL)hasNonContiguousLayout;
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)locationsAll 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.
startingGlyphIndexes:(NSUInteger *)glyphIndexes
count:(NSUInteger)count
forGlyphRange:(NSRange)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;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 OS X v10.2.
- (void)setUsesFontLeading:(BOOL)flag;
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;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
- (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;
- (NSUInteger)getLineFragmentInsertionPointsForCharacterAtIndex:(NSUInteger)charIndexallows 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.
alternatePositions:(BOOL)aFlag
inDisplayOrder:(BOOL)dFlag
positions:(CGFloat *)positions
characterIndexes:(NSUInteger *)charIndexes;
Finally, there is a new NSLayoutManager delegate method,
- (NSDictionary *)layoutManager:(NSLayoutManager *)layoutManagerThis 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.
shouldUseTemporaryAttributes:(NSDictionary *)attrs
forDrawingToScreen:(BOOL)toScreen
atCharacterIndex:(NSUInteger)charIndex
effectiveRange:(NSRangePointer)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;Command-delete is now bound to -deleteToBeginningOfLine:.
- (void)setAllowedInputSourceLocales:(NSArray *)localeIdentifiers;
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;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).
- (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 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 *)stringToCheckThe 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:
language:(NSString *)language details:(NSArray **)details;
NSString *NSGrammarRange;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.
NSString *NSGrammarUserDescription;
NSString *NSGrammarCorrections;
The corresponding client method on NSSpellChecker is
- (NSRange)checkGrammarOfString:(NSString *)stringToChecksimilar to the existing spellchecking methods. NSSpellChecker also has a new method,
startingAt:(NSInteger)startingOffset
language:(NSString *)language
wrap:(BOOL)wrapFlag
inSpellDocumentWithTag:(NSInteger)tag
details:(NSArray **)details;
- (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;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:.
- (BOOL)hasLearnedWord:(NSString *)word;
- (void)unlearnWord:(NSString *)word;
NSTextView has methods to control its use of grammar checking,
- (void)setGrammarCheckingEnabled:(BOOL)flag;If grammar checking is enabled, then it will be performed alongside spellchecking, whenever the text view checks spelling, whether continuously or manually.
- (BOOL)isGrammarCheckingEnabled;
- (void)toggleGrammarChecking:(id)sender;
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 OS X v10.2, but its interpretation has changed. Previously, any non-zero value would cause the spelling indicator to be displayed. For OS X v10.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;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.
- (void)setAlignmentRect:(NSRect)rect;
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;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.
- (void)drawWithExpansionFrame:(NSRect)cellFrame inView:(NSView *)view;
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;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.
- (void)setAllowedInputSourceLocales:(NSArray *)localeIdentifiers;
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 OS X v10.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:planesOne 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.
/* ...more parameters... */
colorSpaceName:NSCalibratedRGBColorSpace
/* ...more parameters... */
];
[bitmapImageRep setProperty:NSImageColorSyncProfileData withValue:iccProfileData];
An ICC profile can be obtained as NSData from an NSColorSpace instance using the "-ICCProfileData" accessor method:
NSColorSpace *colorSpace = [NSColorSpace sRGBColorSpace];It can also be obtained by initializing an NSData instance with the contents of an ".icc" profile file:
NSData *iccProfileData = [colorSpace ICCProfileData];
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;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 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.
- (void)setFillColor:(NSColor *)fillColor;
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;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.
- (void)setTransparent:(BOOL)flag;
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;Once a color gradient is defined, it can be used to draw both linear and radial gradients.
- (id)initWithColors:(NSArray *)colorArray;
- (id)initWithColorsAndLocations:(NSColor *)firstColor, ...;
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;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.
- (void)drawInBezierPath:(NSBezierPath *)path angle:(CGFloat)angle;
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;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.
- (void)drawInBezierPath:(NSBezierPath *)path relativeCenterPosition:(NSPoint)relativeCenterPosition;
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:
NSGradientDrawsBeforeStartingLocationThese 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.
NSGradientDrawsAfterEndingLocation
- (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;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.
- (void)getColor:(NSColor **)color location:(CGFloat *)location atIndex:(NSInteger)index;
- (NSColorSpace *)colorSpace;
- (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 *)tableViewto:
selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes
byExtendingSelection:(BOOL)extend;
- (NSIndexSet *)outlineView:(NSOutlineView *)outlineView
selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes
byExtendingSelection:(BOOL)extend;
- (NSIndexSet *)tableView:(NSTableView *)tableViewIn releases prior to Mac OS 10.5, for the following delegate method:
selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes;
- (NSIndexSet *)outlineView:(NSOutlineView *)outlineView
selectionIndexesForProposedSelection:(NSIndexSet *)proposedSelectionIndexes;
- (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];The new delegate method:
// 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];
- (BOOL)tableView:(NSTableView *)tableViewallows 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):
shouldTrackCell:(NSCell *)cell
forTableColumn:(NSTableColumn *)column
row:(NSInteger)row;
- (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 *)outlineViewPrior to Leopard, it would get called constantly, which is not needed.
validateDrop:(id <NSDraggingInfo>)info
proposedItem:(id)item
proposedChildIndex:(NSInteger)index;
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;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.
- (BOOL)isHidden;
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
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;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.
- (void)moveNodes:(NSArray *)nodes toIndexPath:(NSIndexPath *)startingIndexPath;
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:
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 OS X v10.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
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).Notes specific to MacOS X 10.4
New AppKit features in Tiger
The following are some of the new features in Tiger.- New UI element NSDatePicker
- New UI element NSTokenField
- New UI element NSLevelIndicator
- New animation timing classes NSAnimation and NSViewAnimation
- New class NSPersistentDocument, a CoreData-based NSDocument subclass
- New class NSTreeController
- New class NSColorSpace (Added since WWDC)
- New classes for list and table support in NSTextView
- Improved HTML import/export support in NSTextView
- Restructured NSFont and NSFontDescriptor classes
- Base writing direction support in text and controls
- Support for resolution independent UI
- NSDocument support for autosaving and better error handling
- NSResponder-based error presentation (Added since WWDC)
- Support in NSEvent for native tablet events
- Improvements in accessibility
- NSBitmapImageRep support for alternate bitmap formats and types
- NSTableView autoresizing, custom tooltip, and variable row height support
- NSGraphicsContext bitmap support
- NSWindow optimized live resize support
- NSView drawing redirection
- Automatic page headers/footers in printouts (Added since WWDC)
- CoreImage API support (Added since WWDC)
Foundation:
- New class NSMetadataQuery for high level Spotlight metadata query support
- New set of classes for XML document management
- New class NSLocale
- New class NSIndexPath for representing sequence of indexes
- New methods in NSString to replace deprecated cString APIs and return NSErrors on read/write
- New methods in NSData to return NSErrors on read/write
- NSError enhancements, including richer set of user-presentable error messages (Added since WWDC)
- Key-Value Coding and Observing for Sets (Added since WWDC)
- Enhanced functionality in NSDateFormatter and NSNumberFormatter
- "sdef" file support in scripting
Some of the other new OS X APIs to be aware of:
- CoreData, new framework for object life cycle management (CoreData is part of the Cocoa umbrella framework)
- QTKit, containing Cocoa classes for QuickTime
- PDFKit, classes for PDF documents
- QuartzComposer, APIs for parameterized animations and visualizations
- CoreImage, APIs for GPU based image processing
- InstantMessage, presence APIs
NSDatePicker and NSDatePickerCell
AppKit has a new date/time control, whose API is declared in NSDatePicker.h and NSDatePickerCell.h. Currently this class pair provides two styles of date/time control: a "text field and stepper" style that is similar to Carbon's familiar "ClockDate" control, and a graphical "clock and calendar" variant like those that appear in the "Date & Time" System Preferences panel.A date picker's "objectValue" is an NSDate. The "-dateValue/-setDateValue:" accessor pair provides a type-specific equivalent to the inherited "-objectValue/-setObjectValue:" methods.
A date picker additionally has a "mode" attribute and a "timeInterval" attribute that are not currently used. These attributes exist to support the possibility of a "date range" control mode in the future. The time interval is not applicable, and always zero, when the control is in NSSingleDateMode (the only mode supported by the currently supplied control styles). In NSRangeDateMode, it will specify the duration of the range, which extends forward in time from the cell's dateValue.
An instance's "datePickerElements" attribute determines which components of its value it considers to be specified/specifiable. This setting is composed by bitwise-ORing together one or more of the "DatePickerElementFlag" values declared in NSDatePickerCell.h. It influences both display and editing behavior, as appropriate for the style of date control in use.
typedef unsigned int NSDatePickerElementFlags;
enum {
/* Time Elements */
NSHourMinuteDatePickerElementFlag = 0x000c,
NSHourMinuteSecondDatePickerElementFlag = 0x000e,
NSTimeZoneDatePickerElementFlag = 0x0010,
/* Date Elements */For a case where the "time" portion of the date value is not of interest, for example, this could be set to NSYearMonthDayDatePickerElementFlag. NSTimeZoneDatePickerElementFlag and NSEraDatePickerElementFlag have been declared for possible future use, and do not yet have any effect.
NSYearMonthDatePickerElementFlag = 0x00c0,
NSYearMonthDayDatePickerElementFlag = 0x00e0,
NSEraDatePickerElementFlag = 0x0100,
};
A date picker also has a "calendar", "locale", and "timeZone" that influence date display and editing. In Tiger, date picker functionality supports Gregorian format date display and editing only, but the calendar and locale settings will be used to support other calendars and date formats in the future.
A date picker has a "minDate" and a "maxDate" that can be used to impose a simple range constraint on the possible values the date picker can take. (Both default to nil, meaning that the date value is unconstrained.) Clients can impose more sophisticated constraints in addition to this basic range constraint by providing a delegate object that validates proposed changes to the cell's value. A date picker never allows itself to get into a state where its current value does not satisfy the constraints imposed by minDate, maxDate, and the delegate's validation method (if provided). Its -setObjectValue:/-setDateValue: and -setTimeInterval: accessors will likewise constrain their received parameters to valid values.
The signature of the optional delegate method is:
- (void)dateCell:(NSDatePickerCell *)aDatePickerCellIf an date picker has a delegate assigned to it, and the delegate responds to this selector, this method will be invoked each time the user attempts to make a change to the date picker's value, giving the delegate the opportunity to approve, modify, or reject the change.
validateProposedDateValue:(NSDate **)proposedDateValue
timeInterval:(NSTimeInterval *)proposedTimeInterval;
"proposedDateValue" points to the proposed new dateValue. "proposedTimeInterval" points to the proposed new timeInterval, which will always be zero if the cell is not in NSDateRangeMode. Implementors may find it helpful to consult the NSDatePickerCell's current dateValue and timeInterval for comparison, to determine which of these values (potentially one or both) the cell is proposing to change.
On entry to this delegate method, the start and end points of the proposed range are guaranteed to lie between the NSDatePickerCell's minDate and maxDate, and *proposedTimeInterval is guaranteed to be nonnegative. The delegate can leave *proposedDateValue untouched to accept it, or replace it with a pointer to another NSDate instance. (The replacement value should be autoreleased or otherwise not require a subsequent release.) Likewise, this method can leave the proposedTimeInterval untouched to accept it, or replace its value through the provided pointer.
NSTokenField and NSTokenFieldCell (Section updated since WWDC)
There is a new token field control that behaves like the address field in Mail.app. The new widget supports tokenizing based on a character set (comma by default) and on end editing. In addition, we join tokens for multiple selection using a comma if comma is in the tokenizing character set; Otherwise, we join with space. We invoke text completion after a specified delay.The tokens are draggable. By default, we put the tokens on the pasteboard using the NSStringPboardType (joined in the same way as the multiple token selection).
Use NSControl/NSCell's setObjectValue: method to set the token field's array of represented objects. If the array contains objects other than NSStrings, you must implement the tokenField:displayStringForRepresentedObject: delegate method.
Known NSTokenField Issues:
- NSTokenFieldCell are not fully functional inside of an NSTableView/Matrix.
- NSTokenField's cell must be a subclass of NSTokenField.
Please refer to NSTokenField.h and NSTokenFieldCell.h for the complete API.
NSLevelIndicator/NSLevelIndicatorCell
There is now a level indicator control and corresponding cell class. This control can display one of 4 styles of capacity, ranking, or relevancy. The capacity style can either be continuous or discrete. The ranking appearance is similar to the iTunes 'stars' ranking and the relevancy ranking can be used to display search ranking in a table.Depending on the style, there is different behaviour. For relevancy and continuous capacity, the bar is drawn to fill the cell frame and the min/max/value can be any value. For the discrete styles for capacity and rating level, all values are rounded to the nearest integer when displaying. For the rating level, the images are not stretched or shrunk. For the the discrete capacity, the cell will stretch each segment the same amount to fill the cell frame as much as possible. -cellSizeForBounds: will reflect this.
If the cell is editable, the value can be changed by having the user track using the mouse or using the up/down or left/right arrows though only the rating one should be used in that manner. Any of the styles can be marked editable or have tick marks though in most cases, it only the ranking style should be editable and the continuous capacity indictor needs tick marks.
By default, the cell image is nil but if -setImage:, it replaces the default stars with the custom image for the NSRatingLevelIndicatorStyle style. Setting the image has no effect on the other styles. The image is lightened for highlighted selection and the dots for empty spots are still drawn. The image is not stretched no space is added between images.
See the cell for descriptions of the control methods:
@interface NSLevelIndicator : NSControlHere is the cell declaration:
- (double)minValue;
- (void)setMinValue:(double)minValue;
- (double)maxValue;
- (void)setMaxValue:(double)maxValue;
- (double)warningValue;
- (void)setWarningValue:(double)warningValue;
- (double)criticalValue;
- (void)setCriticalValue:(double)criticalValue;
- (NSTickMarkPosition)tickMarkPosition;
- (void)setTickMarkPosition:(NSTickMarkPosition)position;
- (int)numberOfTickMarks;
- (void)setNumberOfTickMarks:(int)count;
- (int)numberOfMajorTickMarks;
- (void)setNumberOfMajorTickMarks:(int)count;
- (double)tickMarkValueAtIndex:(int)index;
- (NSRect)rectOfTickMarkAtIndex:(int)index;
@end
enum {
NSRelevancyLevelIndicatorStyle,
NSContinuousCapacityLevelIndicatorStyle,
NSDiscreteCapacityLevelIndicatorStyle,
NSRatingLevelIndicatorStyle
} NSLevelIndicatorStyle;
@interface NSLevelIndicatorCell : NSActionCell
- (id)initWithLevelIndicatorStyle:(NSLevelIndicatorStyle)levelIndicatorStyle;Create new cell with indicator style. Default for -init is NSRelevancyLevelIndicatorStyle. Default value and minimum value are 0, default maximum value depends on the style. For continuous styles, the maximum is 100.0. For discrete ones, it's 5.0
- (void)setLevelIndicatorStyle:(NSLevelIndicatorStyle)levelIndicatorStyle;Get/set display style. Will not affect values. Setting will notify enclosing control to update.
- (NSLevelIndicatorStyle)levelIndicatorStyle;
- (double)minValue;
- (void)setMinValue:(double)minValue;
- (double)maxValue;These are the same method names as NSSlider and set min/max values for ranking. Setting will notify enclosing control to update.
- (void)setMaxValue:(double)maxValue;
- (double)warningValue;
- (void)setWarningValue:(double)warningValue;
- (double)criticalValue;These set and get the 'warning' and 'critical' values where the indicator goes from green to yellow to red. The order of the values determines which side is green and which side is red. If the critical value is greater than the warning value, then the indicator is green below the warning, yellow above that but below the critical, and red above. If the critical value is less than the warning value, the indicator is red when the value is below the critical value, yellow up to the critical value, and then green to the maximum value. If the values are the same, the indicator is always green.
- (void)setCriticalValue:(double)criticalValue;
- (void)setTickMarkPosition:(NSTickMarkPosition)position;
- (NSTickMarkPosition)tickMarkPosition;
- (void)setNumberOfTickMarks:(int)count;
- (int)numberOfTickMarks;
- (NSRect)rectOfTickMarkAtIndex:(int)index;These methods for tick marks are identical to the NSSliderCell APIs with the same behaviour. Set the number of ticks to 0 to not have any. Default is 0. An exception is raised if index is out of range. Setting will notify enclosing control to update if necessary.
- (double)tickMarkValueAtIndex:(int)index;
- (void)setNumberOfMajorTickMarks:(int)count;We also allow larger or 'major' tick marks. The count must be less than or equal to the number of tick marks. Setting will notify enclosing control to update if necessary. The major tick marks will be drawn in place of the minor ones.
- (int)numberOfMajorTickMarks;
Resolution Independent UI
As an ongoing effort, we're adding "resolution independent UI." This enables users to choose between more detail or larger user interface without actually having to change physical screen resolution.In the AppKit we intend to concentrate on the "framework scaling" model (as described in the "Resolution Independent UI" release note), which by default causes windows to be scaled. One fundamental change in this mode is that when drawing to the screen, 1 point in the base window coordinate system is no longer necessarily the same as 1 pixel in the backing store of the window. Although we intend much of the support for this mode to be provided by the AppKit, there are some things applications will need to do themselves or watch out for, as outlined below.
For testing purposes developers can change the display resolution using the Quartz Debug application (located in the folder /Developer/Applications/Performance Tools). Note that because the work for supporting resolution independence in both Cocoa and Carbon is ongoing and not yet complete, there are various drawing problems when running with non-integral scale factors in Tiger. This is especially true when Quartz 2D Extreme acceleration is enabled.
Applying the scale factor
Windows are scaled using a transformation on the coordinate system of the top level view (the frameView). The dimensions of the frame of the frameView are equal to the dimensions of the window frame, as in the non-scaled case, but the dimensions of the bounds of the frameView are scaled by dividing the dimensions of the frame by the scaleFactor. For non-integral scaleFactors, the frame is kept integral but the bounds are allowed to have a fractional component. So, for example, a 100x100 window will have a frameView whose bounds is 80x80 for a userSpaceScaleFactor of 1.25. We consider the window frame to be in pixels, and the frameView bounds to be in points. Drawing within the window content is then done in points. Note that this implies that all views within the window are scaled, but we have decided that views whose only scaling is this base scaling for resolution independence purposes will return NO from -isRotatedOrScaledFromBase, so that scrolling, etc. will continue to go through the fast path.
Implications for view positioning and window sizing
Applications must not assume that the window frame and contained view frames use the same coordinate systems. For example, applications that use the window frame to position views will not get correct results. Likewise, applications that compute a change in window frame based on view size (eg. when adding an auxiliary view) will be incorrect. One mechanism for converting between coordinate systems correctly is -[NSView convertRect/Size/Point to/fromView:nil]. Another is -[NSWindow frameRectForContentRect:] and its inverse -[NSWindow contentRectForFrameRect:].
Images
Each imageRep that contains bitmap data indicates its own DPI, since it has both size in points and pixel width and height. An imageRep with 72 DPI has a 1-1 correspondence between points and pixels. An imageRep with 144 DPI has two times more pixels than points in both dimensions. We now create cachedImage reps with the scale factor of the destination window. For a window with a userSpaceScaleFactor of 1.25, a cachedImageRep of 100x100 points would report a size of 100x100, and a pixelWidth and pixelHeight of 125.
Compositing
Historically, compositing has been done in the base coordinate system, regardless of the destination coordinate system. To allow composite: to continue to work in a resolution independent environment, we define the base coordinate system of scaled windows to include the current scale factor.
Example 1 - compositing 72dpi 100x100 source image to view in 1.25x scaled window
A 72dpi 100x100 source image will contain 100x100 pixels. When composited into a 100x100 rect in a view in a scaled window, this image will be scaled to fill 125x125 pixels in the window using the appropriate interpolation algorithm. Any coordinate transforms on the destination view aside from window scaling will be ignored.
Example 2 - compositing 90dpi 100x100 source image to view in 1.25x scaled window
A 90dpi 100x100 source image will contain 125x125 pixels. When composited into a 100x100 rect in a view in a scaled window, this image will exactly fit 125x125 pixels in the window, so no interpolation will be needed. Any coordinate transforms on the destination view aside from window scaling will be ignored.
Example 3 - creating cached image rep from 72dpi 100x100 source image
A 72dpi 100x100 source image will contain 100x100 pixels. The cached image rep will be created with size 100x100, but will hold 125x125 pixels. The source image will be scaled to fit the pixel size of the cached image rep, using the appropriate interpolation. When the cached image rep is later drawn into a scaled window, this will be a 1 to 1 copy from cached image rep pixels to destination window pixels.
Dealing with non-integral view coordinates
In the past, view coordinates have been modified to land on integral boundaries in order to use an exact number of pixels. In a scaled window, any computation to put a view on integral boundaries should be done in the window coordinate system (pixels) then converted to the view coordinate system.
API for "application scaling" mode
The following API allow applications to use their own techniques to achieve resolution independence themselves. One use of this API might be to generate an inverse scaling on the bounds of a view that wants to draw in pixels rather than points.
This is the default scaling from user space to device space on the given screen:
@interface NSScreen : NSObjectSince the scale factor gets applied to individual windows, we also provide a method to ask a window for its scaling. By default, this scale factor will be equal to the scale factor of the NSScreen on which the window was created, or the highest scale factor of the available NSScreens if no screen was specified at creation time. (Note that for the foreseeable future the scale factor of all NSScreens will be equal at any given time):
...
- (float)userSpaceScaleFactor
...
@end
@interface NSWindow : NSResponderIt might also be necessary to allow creation of windows without a scale factor, especially for custom windows. You can create an unscaled window by specifying a styleMask of NSUnscaledWindowMask at creation time.
...
- (float)userSpaceScaleFactor
...
@end
An unscaled window would then return 1.0 for -userSpaceScaleFactor.
Impact on existing API
Both NSWindow and NSScreen define a -deviceDescription method, This method returns an NSDictionary containing a NSDeviceResolution key. NSDeviceResolution has historically contained an NSSize of (72.0, 72.0). On a scaled system, NSDeviceResolution will contain an NSSize of (72.0*userSpaceScaleFactor, 72.0*userSpaceScaleFactor).
NSAnimation
This base class implements timing for animation in Cocoa. There is one subclass available for view animation. The animation can run in the main event thread in blocking mode (i.e. not returning until done), in non-blocking mode so that events are still accepted and in an separate private thread.typedef enum {
NSAnimationEaseInOut, /* s-curve, default */
NSAnimationEaseIn,
NSAnimationEaseOut,
NSAnimationLinear
} NSAnimationCurve;
typedef enum {
NSAnimationBlocking,
NSAnimationNonblocking,
NSAnimationNonblockingThreaded
} NSAnimationBlockingMode;
typedef float NSAnimationProgress; // value in range 0..1
extern NSString *NSAnimationProgressMarkNotification; // has single entry in user info dictionary
extern NSString *NSAnimationProgressMark; // NSNumber(float) with NSAnimationProgress
@interface NSAnimation
- (id)initWithDuration:(NSTimeInterval)duration animationCurve:(NSAnimationCurve)animationCurve;
- (void)startAnimation;Starts and stops the animation. Doesn't reset the progress when stopped. If at a progress of 1.0, calling -startAnimation starts again at progress 0.0. You can play an animation with no view, target or action. If the mode is set to NSAnimationBlocking, then -startAnimation only returns after the animation has run. The delegate can still stop the animation while running if necessary. When -startAnimation is called, the animation retains itself and then is autoreleased on -stopAnimation.
- (void)stopAnimation;
- (BOOL)isAnimating;
- (NSAnimationProgress)currentProgress;Set/get the current progress (values 0.0...1.0). Can change while running. Out of range values are pinned to 0.0 or 1.0. The -setCurrentProgress method is called while playing to change the progress for the next frame. Subclasses should override to get the value and do their action. This action may be in a secondary thread if requested.
- (void)setCurrentProgress:(NSAnimationProgress)progress;
- (void)setDuration:(NSTimeInterval)duration;Set/get the duration of the effect. Duration is in seconds. Can change while running. Negative values raise an exception. If the duration set is past the current time and the animation is playing, the animation is ended.
- (NSTimeInterval)duration;
- (NSAnimationBlockingMode)animationBlockingMode;Set/get mode for running animation. Will take effect the next time the animation is started. Has no effect if animation already running. Default is NSAnimationBlocking. If set to NSAnimationBlocking, animation is run in main thread in custom run loop mode blocking UI. If animation is run NSAnimationNonblocking then animation is run in main thread in the common run loop modes or the ones specified in -runLoopModesForAnimating. NSAnimationNonblockingThreaded spawns a new thread that runs the animation.
- (void)setAnimationBlockingMode:(NSAnimationBlockingMode)animationBlockingMode;
- (void)setFrameRate:(float)framesPerSecond;Set/get the frame rate (updates/second) of the effect. The frame rate is not guaranteed. Can be changed while running and will be used at the next frame. Value must be positive. A value of 0.0 means as fast as possible (currently limited to 30 fps). Negative values raise an exception.
- (float)frameRate;
- (void)setAnimationCurve:(NSAnimationCurve)curve;Set/get the animation curve. Predefined curves are linear, ease in (slow down as we reach end), ease out (slowly speed up start), and ease in/outS-curve. This setting is ignored if the delegate implements -animation:valueForProgress:. Invalid values raise an exception.
- (NSAnimationCurve)animationCurve;
- (float)currentValue;This is the current value of the effect based on the current progress. It is derived from the animation curve or from the delegate. This is a read-only setting. A subclass can override this method to provide a custom curve. The current value can be less than 0.0 or larger than 1.0. For example, by allowing the size to be greater then 1.0, one could do a 'rubber effect' where temporarily, the size of the view is larger than the final.
- (void)setDelegate:(id)delegate;Set/get the delegate. This is a weak reference - the delegate is not retained.
- (id)delegate;
- (NSArray *)progressMarks;These set/get all the progress marks at once. Array contains a list of NSNumbers containing NSAnimationProgress (floats). If there are no progress marks set, -progressMarks returns an empty array. Passing in nil to -setProgressMarks: will clear all progress marks..
- (void)setProgressMarks:(NSArray *)progressMarks;
- (void)addProgressMark:(NSAnimationProgress)progress;These set and clear 'progress marks'. These are used to notify the delegate or post a notification that the particular progress point has been reached. They can be used to synchronize animations (e.g. starting a new animation when the first one has reached the half-way point.) The notifications are only sent if the animation is playing. They can be called during playing of the animation. The notification is sent as soon as the progress point is passed so the actual currentProgress may be different from the requsted mark point. Valid intervals are from 0.0 to 1.0. Both 0.0 and 1.0 marks will always be send. Multiple marks may be sent during a single frame if the times are close enough together..
- (void)removeProgressMark:(NSAnimationProgress)progress;
- (void)startWhenAnimation:(NSAnimation *)animation reachesProgress:(NSAnimationProgress)startProgress;
- (void)stopWhenAnimation:(NSAnimation *)animation reachesProgress:(NSAnimationProgress)stopProgress;
- (void)clearStartAnimation;This links another animation to this one. When the linked animation reaches a certain progress point, the animation starts and/or stops. You can only have one animation set as a start animation and one set as a stop animation. Setting a new will will clear out the old one. You can also clear out the old one using -clearStartAnimation or -clearStopAnimation.
- (void)clearStopAnimation;
- (NSArray *)runLoopModesForAnimating;By default, it returns nil. Custom subclass can override to return specific list run loop modes to run animation timer in. If it returns nil, the animation is run in any of default, modal, and event tracking modes. Ignored if animation mode isn't set to NSAnimationNonblocking.
Delegate Methods
- (BOOL)animationShouldStart:(NSAnimation *)animation;Called on start/stop animation and when animation reaches a progress vallue of 1.0. -animationShouldStart: can return NO to cancel the start. -animationDidStop: is called when the animation is explicitly stopped. -animationDidEnd: is called when it ends by reaching a progress value of 1.0. Only called if actual change occurs (i.e. won't call -animationShouldStart: if already playing)
- (void)animationDidStop:(NSAnimation *)animation;
- (void)animationDidEnd:(NSAnimation *)animation;
- (float)animation:(NSAnimation *)animation valueForProgress:(NSAnimationProgress)progress;Delegate can provide custom curve values. progress will always be from 0.0 to 1.0.
- (void)animation:(NSAnimation *)animation didReachProgressMark:(NSAnimationProgress)progress;Called when the animation reaches a previously marked progress value. The actual current progress may be past the one passed in. Can also use the NSAnimationProgressMarkNotification notification.
NSViewAnimation
This is the only public subclass of NSAnimation. It takes an array of dictionaries that are copied and parses the dictionary. The dictionary contains a target which is required and which can be a window or view. It takes an optional start and/or end frame which if not defined uses the current frame when the animation starts. It can optionally take an effect which will, fade in or out the view or window. If the target is a view and the effect is to fade out or the end frame is empty, the view is hidden at the end. If the effect is to fade in and the end frame is non-empty and the view starts hidden, it is unhidden at the end. If there is no effect, the view frame is changed while animating. If the target is a window, the window is similarly ordered in or out. The animation is non-blocking by default, a duration of 0.5 seconds and the ease in-out curve.APPKIT_EXTERN NSString *NSViewAnimationTargetKey; // NSWindow* or NSView* (required)
APPKIT_EXTERN NSString *NSViewAnimationStartFrameKey; // NSValue*(NSRect) (optional)
APPKIT_EXTERN NSString *NSViewAnimationEndFrameKey; // NSValue*(NSRect) (optional)
APPKIT_EXTERN NSString *NSViewAnimationEffectKey; // NSString*(effect strings)(optional)
APPKIT_EXTERN NSString *NSViewAnimationFadeInEffect;
APPKIT_EXTERN NSString *NSViewAnimationFadeOutEffect;
@interface NSViewAnimation
- (id)initWithViewAnimations:(NSArray *)viewAnimations;
- (NSArray *)viewAnimations;
- (void)setViewAnimations:(NSArray *)viewAnimations;
@end
AppKit Extensions to support use of CoreImage API (Section added since WWDC)
The following API has been added to facilitate more convenient use of CoreImage functionality by Cocoa applications.There is a new NSImageRep subclass called NSCIImageRep, which makes it possible to construct an NSImage that references a CIImage, as in the following code sample:
CIImage ciImage = [aCIFilter valueForKey:@"outputImage"];The resultant NSImage should be usable in any context where an NSImage is called for. CoreImage will automatically render the result on demand. Note that CIImage instances are immutable, so when a change is made to a CIFilter parameter that affects the filter's output image, a new "outputImage" must be requested from the filter, and a new NSCIImageRep constructed from it.
CGRect extent = [ciImage extent];
/* Be careful here. A CIImage can have infinite extent. The following is OK only if you know your CIImage is of finite extent. */
NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize(extent.size.width, extent.size.height)];
NSCIImageRep *ciImageRep = [NSCIImageRep imageRepWithCIImage:outputImage];
[image addRepresentation:ciImageRep];
NSCIImageRep.h also adds three new methods to CIImage via a category. The first enables clients to create a CIImage from an NSBitmapImageRep:
@interface CIImage (NSAppKitAdditions)The remaining two provide a convenient means to render all or part of a CIImage into the current NSGraphicsContext. They behave identically to the like methods in NSImage:
- (id)initWithBitmapImageRep:(NSBitmapImageRep *)bitmapImageRep;
- (void)drawInRect:(NSRect)rect fromRect:(NSRect)fromRect operation:(NSCompositingOperation)op fraction:(float)delta;NSGraphicsContext has a new method, -CIContext, that returns an associated CIContext that can be used to render into the NSGraphicsContext. The CIContext is created on demand, and remains in existence for the lifetime of its owning NSGraphicsContext. If desired, a CIContext can be asked to free the resources it holds by sending it a -reclaimResources or -clearCaches message.
- (void)drawAtPoint:(NSPoint)point fromRect:(NSRect)fromRect operation:(NSCompositingOperation)op fraction:(float)delta;
@end
New methods have been added to facilitate conversion between NSColor and CIColor types (the declarations are in NSColor.h):
@interface NSColor (NSQuartzCoreAdditions)
+ (NSColor *)colorWithCIColor:(CIColor *)color;
@end
@interface CIColor (NSAppKitAdditions)An NSColor can be converted to a CIColor as long as it isn't a pattern color. A CIColor can always be converted to an NSColor.
- (id)initWithColor:(NSColor *)color;
@end
See the Core Image documentation for additional information on the use of Core Image functionality.
New NSResponder-Based Error Presentation (Section added since WWDC)
A new mechanism has been added to Cocoa to enable user-friendly error alerts that are informative, take proper advantage of sheets, and are easily customizable. Cocoa affords customization by publishing an overridable NSResponder method and an NSApplication delegate method that can be implemented. Such a method will typically examine the passed-in NSError object and, using the NSError's domain and code to determine what kind of error is to be presented, return a different NSError object if appropriate. NSError's existing underlyingError attribute makes it feasible to replace one NSError with another that is more presentable without destroying any information about the original detected cause of the problem.Sometimes it is appropriate to present the user with error recovery options, and act accordingly after the user has chosen one of the options. For example, NSDocument can, when a document being saved is found to be locked, offer to override the lock and save anyway (it doesn't though, in Tiger). Cocoa supports this sort of functionality with the localizedRecoverySuggestion, localizedRecoveryOptions and recoveryAttempter attributes that have been added to Foundation's NSError class and that are honored by various AppKit classes that deal in NSErrors. See the "NSError" section of the Foundation release notes.
Three new methods have been added to the NSResponder class:
- (void)presentError:(NSError *)error modalForWindow:(NSWindow *)windowPresent an error alert to the user, as a document-modal panel. When the user has dismissed the alert and any recovery possible for the error and chosen by the user has been attempted, send the selected message to the specified delegate. The method selected by didPresentSelector must have the same signature as:
delegate:(id)delegate didPresentSelector:(SEL)didPresentSelector contextInfo:(void *)contextInfo;
- (void)didPresentErrorWithRecovery:(BOOL)didRecover contextInfo:(void *)contextInfo;The default implementation of this method always invokes [self willPresentError:error] to give subclassers an opportunity to customize error presentation. It then forwards the message, passing the customized error, to the next responder or, if there is no next responder, NSApp. NSApplication's override of this method invokes [[NSAlert alertWithError:theErrorToPresent] beginSheetModalForWindow:window modalDelegate:self didEndSelector:selectorForAPrivateMethod contextInfo:privateContextInfo]. When the user has dismissed the alert, the error's recovery attempter is sent an -attemptRecoveryFromError:optionIndex:delegate:didRecoverSelector:contextInfo: message, if the error had recovery options and a recovery delegate.
Errors for which ([[error domain] isEqualToString:NSCocoaErrorDomain] && [error code]==NSUserCancelledError) are a special case, because they do not actually represent errors and should not be presented as such to the user. NSApplication's override of this method does not present an alert to the user for these kinds of errors. Instead it merely invokes the delegate specifying didRecover==NO.
Between the responder chain in a typical application and various overrides of this method in AppKit classes, objects are given the opportunity to present errors in orders like these:
For windows owned by documents:
view -> superviews -> window -> window controller -> document -> document controller -> application
For windows that have window controllers but aren't associated with documents:
view -> superviews -> window -> window controller -> application
For windows that have no window controller at all:
view -> superviews -> window -> application
You can invoke this method to present error alert sheets. For example, Cocoa's own -[NSDocument saveToURL:ofType:forSaveOperation:delegate:didSaveSelector:contextInfo:] invokes this method when it's just invoked -saveToURL:ofType:forSaveOperation:error: and that method has returned NO.
You probably shouldn't override this method, because you have no way of reliably predicting whether this method vs. -presentError will be invoked for any particular error. You should instead override the -willPresentError: method described below.
- (BOOL)presentError:(NSError *)error;Present an error alert to the user, as an application-modal panel, and return YES if error recovery was done, NO otherwise. This method behaves much like the previous one except it does not return until the user has dismissed the alert and, if the error had recovery options and a recovery delegate, the error's recovery delegate has been sent an -attemptRecoveryFromError:optionIndex: message.
You can invoke this method to present error alert dialog boxes. For example, Cocoa's own [NSDocumentController openDocument:] invokes this method when it's just invoked -openDocumentWithContentsOfURL:display:error: and that method has returned nil.
You probably shouldn't override this method, because you have no way of reliably predicting whether this method vs. -presentError:modalForWindow:delegate:didPresentSelector:contextInfo: will be invoked for any particular error. You should instead override the -willPresentError: method described below.
- (NSError *)willPresentError:(NSError *)error;Given that the receiver is about to present an error (perhaps by just forwarding it to the next responder), return the error that should actually be presented. The default implementation of this method merely returns the passed-in error.
You can override this method to customize the presentation of errors by examining the passed-in error and if, for example, its localized description or recovery information is unsuitably generic, returning a more specific one. When you override this method always check the NSError's domain and code to discriminate between errors whose presentation you want to customize and those you don't. For those you don't just return [super willPresentError:error]. Don't make decisions based on the NSError's localized description, recovery suggestion, or recovery options because it's usually not a good idea to try to parse localized text.
NSDocument and NSDocumentController are not subclasses of NSResponder, and documents and the shared document controller are not in the responder chain, for historical reasons. NSDocument and NSDocumentController nonetheless implement the three new methods described above, and error presentation messages are by default forwarded as if documents and the shared document controller are in the responder chain. (NSWindowController also overrides the error presentation methods to help make this happen.)
In many applications it will be appropriate to override -willPresentError: in a subclass of NSWindowController, NSDocument, or NSDocumentController, but in some applications it will be easiest to customize some error presentation on a per-application basis. So that you don't have to subclass NSApplication to do so, a new application delegate method has been added:
- (NSError *)application:(NSApplication *)application willPresentError:(NSError *)error;Given that the application object is about to present an error, return the error that should actually be presented.
You can implement this delegate method to customize the presentation of any error presented by your application, as long as no code in your application overrides -presentError:modalForWindow:delegate:didPresentSelector:contextInfo: or -presentError: in a way that prevent errors from being passed down to the application object. Your implementation of this delegate method should follow the advice given for overriding of -[NSResponder willPresentError:], except that it should just return the passed-in error instead of [super willPresentError:error].
New Error Presentation Method in NSAlert (Section added since WWDC)
We may in the future add to NSError still more attributes that are meant to contribute to the presentation of the error to the user. In that case it would be ideal if NSErrors carrying such attributes were presented properly, so a new method has been added to NSAlert to reduce the need for NSErrors to be picked apart by code that is naive of future NSError additions:+ (NSAlert *)alertWithError:(NSError *)error;Given an NSError, create an NSAlert that can be used to present the error to the user. The error's localized description, recovery suggestion, and recovery options will be used to set the alert's message text, informative text, and button titles, respectively.
NSDocument/NSDocumentController Error Handling (Section updated since WWDC)
In previous releases of OS X Cocoa's NSDocument and NSDocumentController classes presented alerts that weren't informative, and it was very difficult to customize them. Both classes have been updated to take advantage of the NSError class that was added to Cocoa in Mac OS 10.3. Methods have been added to both classes, and methods have been deprecated. The new methods all consistently deal in NSURLs, replacing the melange of paths and URLs that previously existed.New NSDocument Methods for Error Handling (Section updated since WWDC)
- (id)initWithContentsOfURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError;Initialize a document located by a URL, of a specified type, and return it if successful. If not successful, return nil after setting *outError to an NSError that encapsulates the reason why the document could not be initialized. The default implementation of this method invokes [self init], [self readFromURL:absoluteURL ofType:typeName error:outError], [self setFileURL:absoluteURL], [self setFileType:typeName], and [self setFileModificationDate:theModificationDate].
This method replaces -initWithContentsOfFile:ofType: and -initWithContentsOfURL:ofType:, which are now deprecated. For backward binary compatibility -initWithContentsOfFile:ofType: is still invoked when appropriate if it is overridden. -initWithContentsOfURL:ofType: is never invoked from anywhere within Cocoa, as in Mac OS 10.3 and earlier.
- (BOOL)revertToContentsOfURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError;Discard all unsaved document modifications and replace the document's contents by reading a file or file package located by a URL, of a specified type, and return YES if successful. If not successful, return NO after setting *outError to an NSError that encapsulates the reason why the document could not be reverted. The default implementation of this method invokes [self readFromURL:absoluteURL ofType:typeName error:outError], [self setFileModificationDate:theModificationDate], [self updateChangeCount:NSChangeCleared], and, if the document has an undo manager, [[self undoManager] removeAllActions]. It also deletes autosaved contents files when they have become obsolete.
This method replaces -revertToSavedFromFile:ofType: and -revertToSavedFromURL:ofType: which are now deprecated. For backward binary compatibility, -revertToSavedFromFile:ofType is still invoked when appropriate if overridden. -revertToSavedFromURL:ofType: is never invoked from anywhere within Cocoa, as in Mac OS 10.3 and earlier.
- (BOOL)readFromURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError;Methods that are meant to be optionally overridden in the same manner as the non-NSError-returning methods that they replace. See the comments in <AppKit/NSDocument.h> for details.
- (BOOL)readFromFileWrapper:(NSFileWrapper *)fileWrapper ofType:(NSString *)typeName error:(NSError **)outError;
- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError;
- (BOOL)writeToURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError;
- (NSFileWrapper *)fileWrapperOfType:(NSString *)typeName error:(NSError **)outError;
- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError;
The replaced methods are -readFromFile:ofType:, -loadFileWrapperRepresentation:ofType:, -loadDataRepresentation:ofType:, -writeToFile:ofType:, -fileWrapperRepresentationOfType:, and -dataRepresentationOfType:. They are all now deprecated. For backward binary compatibility, the old methods are still invoked when appropriate if overridden. Also deprecated are -readFromURL:ofType: and -writeToURL:ofType:, which are never invoked from anywhere within Cocoa, as in Mac OS 10.3 and earlier.
- (BOOL)writeSafelyToURL:(NSURL *)absoluteURL ofType:(NSString *)typeName
forSaveOperation:(NSSaveOperationType)saveOperation
error:(NSError **)outError;
- (BOOL)writeToURL:(NSURL *)absoluteURL ofType:(NSString *)typeName
forSaveOperation:(NSSaveOperationType)saveOperation
originalContentsURL:(NSURL *)absoluteOriginalContentsURL
error:(NSError **)outError;
- (NSDictionary *)fileAttributesToWriteToURL:(NSURL *)absoluteURL ofType:(NSString *)typeNameMore methods that are meant to be optionally overridden in the same manner as the non-NSError-returning methods that they replace. See the comments in <AppKit/NSDocument.h> for details.
forSaveOperation:(NSSaveOperationType)saveOperation
originalContentsURL:(NSURL *)absoluteOriginalContentsURL
error:(NSError **)outError;
The replaced methods are -writeWithBackupToFile:ofType:saveOperation:, -writeToFile:ofType:originalFile:saveOperation:, and -fileAttributesToWriteToFile:ofType:saveOperation:. They are all now deprecated. For backward binary compatibility, the old methods are still invoked when appropriate if overridden.
- (void)setFileURL:(NSURL *)absoluteURL;Accessors for the location of the document's on-disk representation. The set method doesn't actually rename the document, it's just for recording the document's location during initial opening or saving. The default implementation of -setFileURL: just records the URL so that the default implementation of -fileURL can return it. The default implementation of -fileURL returns whatever was stored by a previous invocation of the default implementation of -setFileURL:.
- (NSURL *)fileURL;
As part of the parallel effort to use NSURLs consistently, these methods replace -setFileName: and -fileName, which are now deprecated. For backward binary compatibility the old methods are still invoked when appropriate if overridden.
- (void)saveToURL:(NSURL *)absoluteURL ofType:(NSString *)typeNameSave the contents of the document to a file or file package located by a URL, formatted to a specified type, for a particular kind of save operation. When saving is completed, regardless of success or failure, send the message selected by didSaveSelector to the delegate, with the contextInfo as the last argument. The method selected by didSaveSelector must have the same signature as:
forSaveOperation:(NSSaveOperationType)saveOperation
delegate:(id)delegate didSaveSelector:(SEL)didSaveSelector contextInfo:(void *)contextInfo;
- (void)document:(NSDocument *)document didSave:(BOOL)didSaveSuccessfully contextInfo:(void *)contextInfo;The default implementation of this method first makes sure that any editor registered using Cocoa Bindings' NSEditorRegistration informal protocol has committed its changes (except for autosave operations), then invokes [self saveToURL:absoluteURL ofType:typeName forSaveOperation:saveOperation error:&anError] and, if NO is returned, presents the error to the user in a document-modal panel before messaging the delegate.
As part of the parallel effort to use NSURLs consistently, this method replaces -saveToFile:saveOperation:delegate:didSaveSelector:contextInfo:, which is now deprecated. For backward binary compatibility the old method is still invoked when appropriate if overridden.
New NSDocumentController Methods for Error Handling (Section updated since WWDC)
- (id)openUntitledDocumentAndDisplay:(BOOL)displayDocument error:(NSError **)outError;Methods that are meant to be optionally overridden in the same manner as the non-NSError-returning methods that they replace. See the comments in <AppKit/NSDocumentController.h> for details.
- (id)makeUntitledDocumentOfType:(NSString *)typeName error:(NSError **)outError;
- (id)openDocumentWithContentsOfURL:(NSURL *)absoluteURL display:(BOOL)displayDocument error:(NSError **)outError;
- (id)makeDocumentWithContentsOfURL:(NSURL *)absoluteURL ofType:(NSString *)typeName error:(NSError **)outError;
The replaced methods are -openUntitledDocumentOfType:display:, -makeUntitledDocumentOfType:, -openDocumentWithContentsOfFile:display:, and makeDocumentWithContentsOfFile:ofType:. They are all now deprecated. For backward binary compatibility, the old methods are still invoked when appropriate if overridden. Also deprecated are -openDocumentWithContentsOfURL:display: and -makeDocumentWithContentsOfURL:ofType:, which are never invoked from anywhere within Cocoa, as in Mac OS 10.3 and earlier.
- (id)documentForURL:(NSURL *)absoluteURL;Given a URL, return the open document whose file or file package is located by the URL, or nil if there is no such open document. The default implementation of this method queries each open document to find one whose URL matches, and returns the first one whose URL does match.
As part of the parallel effort to use NSURLs consistently, this method replaces -documentForFileName:, which is now deprecated. For backward binary compatibility the old method is still invoked when appropriate if overridden.
-fileNamesFromRunningOpenPanel: is also deprecated. It is no longer invoked by -openDocument: unless it is overridden. The existing -URLsFromRunningOpenPanel: method is now used instead.
Finally, -setShouldCreateUI: and -shouldCreateUI are deprecated, because as used by NSDocumentController in the past they are not useful. -shouldCreateUI is still invoked by the also-deprecated -openUntitledDocumentOfType:display:, -openDocumentWithContentsOfFile:display:, and -openDocumentWithContentsOfURL:display: methods, and also the new -openUntitledDocumentAndDisplay:error: and -openDocumentWithContentsOfURL: display:error: methods, but only for backward compatibility. If your NSDocument subclass is sending itself a -makeWindowControllers message during initialization, and you're using -setShouldCreateUI: to keep NSDocumentController from sending another -makeWindowControllers message, you should considering fixing your NSDocument subclass, because NSDocuments should never need to invoke -makeWindowControllers during initialization.
Use of NSSaveAsOperation Instead of NSSaveOperation in NSDocument
-saveDocumentWithDelegate:didSaveSelector:contextInfo: now invokes -runModalSavePanelForSaveOperation:delegate:didSaveSelector:contextInfo: with NSSaveAsOperation instead of NSSaveOperation when a save panel is going to be presented. Checking saveOperation==NSSaveOperation without also checking [self fileName] or [self fileURL] is now valid, for apps that require Tiger.Change to NSDocument's Standard Save Panel Accessory View
The File Format popup in the standard save panel accessory view installed by NSDocument no longer includes export-only file type items for NSSaveOperation and NSSaveAsOperation. Those items were always disabled, but because the user could not possibly cause them to become enabled by doing something in the save panel it was not appropriate to include them.Change to NSDocumentController's Presentation of Document Opening Errors
In previous releases of OS X NSDocumentController would present an error alert if a document selected by the user with the open panel could not be opened, but it would not present the same alert when the document opening was attempted as a result of the user double-clicking on the document's icon in the Finder, or dragging of the document's icon onto the application's icon. This has been fixed. NSDocumentController now consistently presents an error alert whenever document opening fails. For backward binary compatibility, NSDocumentController does not do this in applications linked against Mac OS 10.3 or earlier, because several applications have worked around the problem by presenting error alerts of their own (and two error alerts are not better than one).New NSDocument Initializer Just For New Documents (Section updated since WWDC)
In previous releases of OS X there was no NSDocument initializer that would be invoked when a new document was created but not when a document was being opened. Such an initializer has been added:- (id)initWithType:(NSString *)typeName error:(NSError **)outError;Initialize a new empty document of a specified type, and return it if successful. If not successful, return nil after setting *outError to an NSError that encapsulates the reason why the document could not be initialized. The default implementation of this method just invokes [self init] and [self setFileType:typeName].
You can override this method to perform initialization that must be done when creating new documents but should not be done when opening existing documents. Your override should typically invoke super, or at least it must invoke -init, NSDocument's designated initializer, to initialize NSDocument's private instance variables.
New NSDocument Methods for File Modification Dates (Section updated since WWDC)
New NSDocument methods have been added to track the modification date of the document's file on disk:- (void)setFileModificationDate:(NSDate *)modificationDate;The method -fileModificationDate returns the last known modification date of the document's on-disk representation. -setFileModificationDate: is invoked by the default implementations of -initWithContentsOfURL:ofType:error:, -initForURL:withContentsOfURL:ofType:error:, -revertToContentsOfURL:ofType:error:, and -saveToURL:ofType:forSaveOperation:error:. In a future release -saveDocumentWithDelegate:didSaveSelector:contextInfo: may test the file modification date and warn the user when they may be about to overwrite modifications by something other than the current application.
- (NSDate *)fileModificationDate;
New NSDocument Method for All Document Saving
A new method has been added to NSDocument so that you can override it to do things before and after any save operation:- (BOOL)saveToURL:(NSURL *)absoluteURL ofType:(NSString *)typeNameSave the contents of the document to a file or file package located by a URL, formatted to a specified type, for a particular kind of save operation, and return YES if successful. If not successful, return NO after setting *outError to an NSError that encapsulates the reason why the document could not be saved.
forSaveOperation:(NSSaveOperationType)saveOperation error:(NSError **)outError;
The default implementation of this method invokes [self writeSafelyToURL:absoluteURL ofType:typeName forSaveOperation:saveOperation error:outError]. If that returns YES, it also invokes some combination of -setFileModificationDate:, -setFileType:, -setFileURL:, -updateChangeCount:, and -setAutosavedContentsFileURL:, as appropriate for the kind of save operation. It also updates information that -saveDocumentWithDelegate:didSaveSelector:contextInfo: uses to check for modification, renaming, moving, deleting, and trashing of open documents, and deletes autosaved contents files when they have become obsolete. Because this method does several diffferent things, and because the things are likely to change in future releases of OS X, your override of this method should virtually always invoke super, merely adding new behavior before or after the saving.
Document Autosaving
Support for autosaving of documents has been added to Cocoa. The support takes the form of optional periodic autosaving behavior, new autosaving behavior at application-quitting time, and new NSDocument and NSDocumentController methods that you can invoke and override to customize the behavior.New NSDocumentController Methods for Autosaving
Two new methods have been added to NSDocumentController so that you can enable periodic autosaving and control how often documents are periodically autosaved:- (void)setAutosavingDelay:(NSTimeInterval)autosavingDelay;The time interval in seconds for periodic autosaving. A value of 0 indicates that periodic autosaving should not be done at all. NSDocumentController will use this number as the amount of time to wait between detecting that a document has unautosaved changes and sending the document an -autosaveDocumentWithDelegate:didAutosaveSelector:contextInfo: message. The default value is 0. You can change it to enable periodic autosaving.
- (NSTimeInterval)autosavingDelay;
Two new methods that are invoked when reopening autosaved documents have been added to NSDocumentController:
- (BOOL)reopenDocumentForURL:(NSURL *)absoluteDocumentURLReopen a document located by a URL by reading the contents for the document from another URL, present its user interface, and return YES if successful. If not successful, return NO after setting *outError to an NSError that encapsulates the reason why the document could not be reopened. The default implementation of this method determines the type of document being reopened, sends a -makeDocumentForURL:withContentsOfURL:ofType:error: to instantiate it, then invokes -addDocument: to record its opening. It then sends the document -makeWindowControllers and -showWindows messages.
withContentsOfURL:(NSURL *)absoluteDocumentContentsURL error:(NSError **)outError;
- (id)makeDocumentForURL:(NSURL *)absoluteDocumentURL withContentsOfURL:(NSURL *)absoluteDocumentContentsURLInstantiate a document located by a URL, of a specified type, but by reading the contents for the document from another URL, and return it if successful. If not successful, return nil after setting *outError to an NSError that encapsulates the reason why the document could not be instantiated. The default implementation of this method invokes -documentClassForType: to find out the class of document to instantiate, allocates a document object, and initializes it by sending it an -initForURL:withContentsOfURL:ofType:error: message.
ofType:(NSString *)typeName error:(NSError **)outError;
New NSDocument Methods and Enumerators for Autosaving
New methods have been added to NSDocument. New NSDocumentChangeTypes and a new NSSaveOperationType have been added too:- (BOOL)hasUnautosavedChanges;Return YES if the document has changes that have not been autosaved, NO otherwise, as determined by the history of previous invocations of -updateChangeCount:. The default implementation of this method returns NO immediately after invocation of -updateChangeCount:NSChangeCleared or -updateChangeCount:NSChangeAutosaved. It will then return YES if a different number of -updateChangeCount:NSChangeDone and -updateChangeCount:NSChangeUndone invocations have been done since. (-updateChangeCount:NSChangeReadOtherContents has no effect on what the default implementation of this method returns.)
- (void)autosaveDocumentWithDelegate:(id)delegate didAutosaveSelector:(SEL)didAutosaveSelector contextInfo:(void *)contextInfo;Autosave the document's contents at an appropriate location, and then send the message selected by didAutosaveSelector to the delegate, with the contextInfo as the last argument. The method selected by didAutosaveSelector must have the same signature as:
- (void)document:(NSDocument *)document didAutosave:(BOOL)didAutosaveSuccessfully contextInfo:(void *)contextInfo;If any error occurs while autosaving, it must be reported to the user, typically in a document-modal alert panel, before the delegate is messaged with succeeded:NO.
The default implementation of this method figures out where the autosaved document contents should go and invokes [self saveToURL:autosavedDocumentContentsURL ofType:[self autosavingFileType] forSaveOperation:NSAutosaveOperation delegate:inDelegate didSaveSelector:inDidAutosaveSelector contextInfo:inContextInfo].
- (NSString *)autosavingFileType;Return the document type that should be used for an autosave operation. The default implementation just returns [self fileType], so for never-been-saved documents this method will return the first type in the array returned by [[self class] writableTypes], or nil if the array is empty. You can override this method and return nil in your override to completely disable autosaving of individual documents (NSDocumentController will not send -autosaveDocumentWithDelegate:didAutosaveSelector:contextInfo: to a document that has no autosaving file type.) You can also override it if your application defines a document type that is specifically designed for autosaving. For example, one that efficiently represents document contents _changes_ instead of complete document contents.
- (void)setAutosavedContentsFileURL:(NSURL *)absoluteURL;The location of the most recently autosaved document contents. The default implementation of -setAutosavedContentsFileURL: records the URL and notifies the shared document controller that this document should be autoreopened if the application is quit by a fast logout (a feature not in Tiger), or crashes before the document is saved. The default implementation of -autosavedContentsFileURL just returns whatever was stored by a previous invocation of the default implementation of -setAutosavedContentsFileURL:.
- (NSURL *)autosavedContentsFileURL;
For example, -saveToURL:ofType:forSaveOperation:error: invokes -setAutosavedContentsFileURL: to record when autosaving has been done, and both methods as part of deleting autosaved document contents when a regular Save or Save As operation has been done successfully. -revertToContentsOfURL:ofType:error: also invokes these as part of deleting autosaved document contents.
During autosaving a new NSSaveOperationType is used:
NSAutosaveOperationThe writing of a document's current contents to a file or file package that is separate from the document's current one, without changing the document's current one. Backward binary compatibility code in NSDocument ensures that this value is never passed to overrides of methods that take NSSaveOperationTypes. NSSaveToOperation is always used in those cases instead.
After successful autosaving -updateChangeCount: with a new NSDocumentChangeType must be invoked:
NSChangeAutosavedThe value to pass to indicate that the document's contents have been autosaved. For example, -saveToURL:ofType:forSaveOperation:error: uses this for a successful NSAutosaveOperation.
A new method that you can override to customize the reopening of autosaved documents has been added:
- (id)initForURL:(NSURL *)absoluteDocumentURL withContentsOfURL:(NSURL *)absoluteDocumentContentsURLInitialize a document located by a URL, of a specified type, but by reading the contents for the document from another URL, and return it if successful. If not successful, return nil after setting *outError to an NSError that encapsulates the reason why the document could not be initialized. The default implementation of this method invokes [self readFromURL:absoluteDocumentContentsURL ofType:typeName error:outError], [self setFileURL:absoluteURL], [self setAutosavedContentsFileURL:absoluteDocumentContentsURL], [self setFileType:typeName], and [self setFileModificationDate:theModificationDate]. It also invokes [self updateChangeCount:NSChangeReadOtherContents] if the two URLs aren't identical, so that -isDocumentEdited will always return YES until the user saves or reverts the document.
ofType:(NSString *)typeName error:(NSError **)outError;
To ease the adoption of the autosaving feature introduced in Mac OS 10.4, the default implementation of this method invokes [self initWithContentsOfFile:[absoluteDocumentContentsURL path] ofType:typeName] if -initWithContentsOfFile:ofType: is overridden and the URL uses the "file:" scheme. It still invokes [self setFileModificationDate:theModificationDate] and [self updateChangeCount:NSChangeReadOtherContents] in this situation. It still also invokes [self setFileURL:absoluteURL], to overwrite the incorrect invocation of -setFileName: that the override of -initWithContentsOfFile:ofType: likely did.
absoluteDocumentURL will be nil if the initializing is being done as part of the reopening of an autosaved document when the autosaved document had never been saved in the first place.
During autosaved document reopening -initForURL:withContentsOfURL:ofType:error: uses a new NSDocumentChangeType:
NSChangeReadOtherContentsThe value to pass to -updateChangeCount: to indicate that the document has been initialized with the contents of a file or file package other than the one whose location would be returned by -fileURL, and therefore can't possibly be synchronized with its persistent representation. For example, -initForURL:withContentsOfURL:ofType:error: uses this to indicate that an autosaved document is being reopened.
NSDocument/NSDocumentController Support for Dynamically-Enabled Document Types
Several interesting Cocoa applications have implemented dynamically-enabled document types, in which support for additional document types is provided by plugins. Because NSDocumentController and NSDocument access document type declarations in Info.plist in a non-public way, developers have resorted to accessing private NSDocumentController and NSDocument instance variables directly, which is discouraged. To obviate such discouraged behavior, new methods that you can override, in addition to overriding existing methods, have been added so you can safely implement your own dynamically-enabled document typing scheme.New NSDocumentController Methods for Dynamically-Enabled Document Types
- (NSString *)defaultType;Return the name of the document type that should be used when creating new documents. The default implementation of this method returns the first Editor type declared in the application's Info.plist, or returns nil if no Editor type is declared. You can override it to customize the type of document that is created when, for instance, the user chooses New in the File menu.
[NSDocumentController openUntitledDocumentAndDisplay:error:] for instance always invokes this method. -[NSDocumentController validateUserInterfaceItem:] also invokes this when deciding whether or not any UI item whose action is newDocument: (the New item in the File menu, typically) should be enabled.
- (NSString *)typeForContentsOfURL:(NSURL *)inAbsoluteURL error:(NSError **)outError;Given a URL, return the name of the document type that should be used when opening the document at that location, if successful. If not successful, return nil after setting *outError to an NSError that encapsulates the reason why the document's type could not be determined, or the fact that the document's type is just unrecognized. The default implementation of this method invokes -typeFromFileExtension:, possibly twice, passing an HFS file type string for the second invocation. The default implementation is of course subject to change however. You can override this to customize type determination for documents being opened.
[NSDocumentController openDocumentWithContentsOfURL:display:error:] and -[NSDocumentController reopenDocumentForURL:withContentsOfURL:error:] for instance always invoke this method.
- (NSArray *)documentClassNames;Return the names of NSDocument subclasses supported by this application. The default implementation of this method returns information derived from the application's Info.plist. You can override it to return the names of document classes that are dynamically loaded from plugins.
New NSDocument Method for Dynamically-Enabled Document Types
- (NSArray *)writableTypesForSaveOperation:(NSSaveOperationType)saveOperation;Return the names of the types to which this document can be saved for a particular kind of save operation. For every kind of save operation except NSSaveToOperation the returned array must only include types for which the the application can play the Editor role. For NSSaveToOperation the returned array may also include types for which the application can only play the Viewer role, and still more types that the application can merely export. The default implementation of this method returns [[self class] writableTypes] with, except during NSSaveToOperations, types for which +isNativeType returns NO filtered out.
You can override this method to limit the set of writable types when the documently currently contains data that is not representable in all writable types. For example, you can disallow saving to .rtf files when the document contains an attachment and can only be saved properly to .rtfd files. NSDocument currently uses this this method during save operations that present save panels, but it may be called at other times in future releases of OS X.
You can invoke this method when creating a custom save panel accessory view to easily present the same set of types that NSDocument would present in its standard file format popup menu.
Proper Invocation of Existing NSDocument Methods for Dynamically-Enabled Document Types
The implementations of the existing NSDocument class methods +readableTypes, +writableTypes, and +isNativeType: have not changed significantly, but they are now invoked when you would expect them to be invoked, so that it is useful to override them. For example, each NSDocument subclass whose name is returned by -[NSDocumentController documentClassNames] is now sent a +readableTypes messages to determine the list of types than can be opened. For another example, the class of a document that is being saved is sent a +writableTypes message to determine the list of of types than can be saved. In the case of an NSSaveOperation or NSSaveAsOperation +isNativeType: is sent for each type returned by +readableTypes, to filter out export-only types.New NSDocumentController Method to Control the Open Recents Menu
A new method has been added to NSDocumentController so that you can override it to control NSDocumentController's presentation of the standard Open Recents menu:- (unsigned int)maximumRecentDocumentCount;Return the maximum number of items that may be presented in the standard Open Recent menu. A value of 0 indicates that NSDocumentController will not attempt to add an Open Recent menu to your application's File menu, though NSDocumentController will not attempt to remove any Open Recent menu item if there is one already there. The default implementation returns a value that is subject to change and may or may not be derived from a setting made by the user in a System Preferences panel.
Changes in NSDocument's Handling of the 'Save' Scripting Command (Section added since WWDC)
The behavior of -[NSDocument(NSScripting) handleSaveScriptCommand:], the method that is typically invoked when a script sends a 'save' command to a document, is changed in Mac OS 10.4. It now implements the behavior described in <http://developer.apple.com/technotes/tn2002/tn2106.html>. Here's a comparison of the old and new behavior:When an 'in' argument is provided for an already-saved document
• 10.3: Acted like 'Save As...' unless the document class returned NO for +isNativeType:typeDerivedFromFileExtension, in which case acted like 'Save a Copy...' (also known as 'Save To...'). Invoked -writeWithBackupToFile:ofType:saveOperation: directly.
• 10.4: Acts like 'Save a Copy...' Invokes -saveToURL:ofType:forSaveOperation:delegate:didSaveSelector:contextInfo:.
When an 'in' argument is provided for a never-been-saved document
• 10.3: Acted like 'Save As...' unless the document class returned NO for + isNativeType:typeDerivedFromFileExtension, in which case acted like 'Save a Copy...' Invoked -writeWithBackupToFile:ofType:saveOperation: directly.
• 10.4: Acts like 'Save As...' Invokes -saveToURL:ofType:forSaveOperation:delegate:didSaveSelector:contextInfo:.
When no 'in' argument is provided for an already-saved document
• 10.3: Acted like 'Save.' Invoked -saveDocument:.
• 10.4: Acts like 'Save.' Invokes -saveDocumentWithDelegate:didSaveSelector:contextInfo:.
When no 'in' argument is provided for a never-been-saved document
• 10.3: Acted like 'Save As...' Invoked -saveDocumentAs:.
• 10.4: Acts like 'Save,' which in this case is indistinguishable from 'Save As...,' because a save dialog must be presented to the user. Invokes -saveDocumentWithDelegate:didSaveSelector:contextInfo:.
TextEdit, which is not NSDocument-based, has been updated to implement the same behavior.
Changes in NSDocument's Handling of Editors Registered via Cocoa Bindings' NSEditorRegistration Informal Protocol (Section added since WWDC)
In Mac OS 10.3 NSDocument implemented Cocoa Bindings' NSEditorRegistration informal protocol and would tell key-value binding editors to commit and discard their changes at certain times. In Mac OS 10.4 NSDocument's handling of key-value binding editors has been refined. These methods no long tell registered editors to commit their changes:-saveDocument:, -saveDocumentAs:, and -saveDocumentTo:, and -printDocument:.
-canCloseDocumentWithDelegate:shouldCloseSelector:contextInfo:.
-[NSDocumentController saveAll:].
Instead, these NSDocument methods now tell registered editors to commit their changes:
-saveDocumentWithDelegate:didSaveSelector:contextInfo:
-runModalSavePanelForSaveOperation:delegate:didSaveSelector:contextInfo:
-saveToURL:ofType:forSaveOperation:delegate:didSaveSelector:contextInfo: (except for NSAutosaveOperations)
-printDocumentWithSettings:showPrintPanel:delegate:didPrintSelector:contextInfo:
The end result is that any alert sheets that editors may present when asked to commit are now always presented at the right time.
-revertDocumentToSaved: now tells registered editors to discard after the user has seen the "do you want to revert?" panel and chosen "Revert," instead of before the panel is presented.
-close now tells editors to discard their changes.
-isDocumentEdited now returns YES whenever there are registered key-value binding editors.
For backward binary compatibility, NSDocument will send -commitEditingWithDelegate:didCommitSelector:contextInfo: messages to registered editors that implement the this new method, but will fall back to using the old (but not deprecated) -commitEditing message to those that don't.
Bug Fix for Instantation of NSDocumentController Subclasses (Section added since WWDC)
According to the documentation titled "Creating a Subclass of NSDocumentController:""The application will not ask for its shared document controller until after the applicationWillFinishLaunching: message is sent to its delegate. Therefore, you can create an instance of your sub-class of NSDocumentController in your application delegate's applicationWillFinishLaunching: method and that instance will be set as the shared document controller."
In Mac OS 10.0-10.3 this was not always true. Code within AppKit would invoke +[NSDocumentController sharedDocumentController] during the loading of your application's main nib, if it contained an Open Recent menu item, before the sending of -applicationWillFinishLaunching: to the application delegate, preventing the NSDocumentController instantiated by the application delegate from being returned by subsequent invocations of +sharedDocumentController. This was a bug and has been fixed in Mac OS 10.4. (The bug did not affect applications that have no Open Recent menu item in their main nib and let NSDocumentController add one under the first item that had a nil target and an openDocument: action, like Sketch and Property List Editor.)
Bug Fix in -[NSDocument displayName]
In Mac OS 10.3, NSDocuments began sending themselves -displayName messages for the purpose of determining whether or not the document was untitled. NSDocument's implementation of -displayName would return nil in this situation. This interfered with overrides of -displayName that invoked [super displayName] and assumed that a non-nil value would be returned (a valid assumption). This bug has been fixed. -[NSDocument displayName] no longer ever returns nil.Removal of Obsolete Method Declarations from NSDocument.h and NSDocumentController.h
Four NSDocument methods were marked as obsolete of as Mac OS 10.0, having been superseded by methods whose behavior in the presence of multiple sheets is markedly better:-canCloseDocument: was replaced by -canCloseDocumentWithDelegate:shouldCloseSelector:contextInfo:
-fileNameFromRunningSavePanelForSaveOperation: was replaced by -saveDocumentWithDelegate:didSaveSelector:contextInfo:
-runModalSavePanel:withAccessoryView: was replaced by -runModalSavePanelForSaveOperation:delegate:didSaveSelector:contextInfo:
-shouldCloseWindowController: was replaced by -shouldCloseWindowController:delegate:shouldCloseSelector:contextInfo:
Two NSDocumentController methods were likewise marked as obsolete at the same time:
-closeAllDocuments: was replaced by -closeAllDocumentsWithDelegate:didCloseAllSelector:contextInfo:
-reviewUnsavedDocumentsWithAlertTitle:cancellable: was replaced by -reviewUnsavedDocumentsWithAlertTitle:cancellable:delegate:didReviewAllSelector:contextInfo:
The declarations for these six methods have been removed from NSDocument.h and NSDocumentController.h. You should stop using or overriding them in your applications and start using or overriding their replacements. However, for backward binary compatibility their implementations remain in Cocoa, in categories named NSDocument(NSCompatibility) and NSDocumentController(Compatibility). If you are not able to migrate to the newer methods, you can do away with any compiler warnings caused by the changes in NSDocument.h and NSDocumentController.h by adding the following declarations to the relevant source code files in your project:
@interface NSDocument(NSDeprecatedLongAgo)
- (BOOL)canCloseDocument;
- (NSString *)fileNameFromRunningSavePanelForSaveOperation:(NSSaveOperationType)saveOperation;
- (int)runModalSavePanel:(NSSavePanel *)savePanel withAccessoryView:(NSView *)accessoryView;
- (BOOL)shouldCloseWindowController:(NSWindowController *)windowController;
@end
@interface NSDocumentController(NSDeprecatedLongAgo)
- (BOOL)closeAllDocuments;
- (BOOL)reviewUnsavedDocumentsWithAlertTitle:(NSString *)title cancellable:(BOOL)cancellable;
@end
Summary of Deprecated NSDocument Methods
Search in the above release notes for information about why each has been deprecated, and when it is still invoked.- (NSData *)dataRepresentationOfType:(NSString *)type;
- (NSDictionary *)fileAttributesToWriteToFile:(NSString *)fullDocumentPath
ofType:(NSString *)documentTypeName saveOperation:(NSSaveOperationType)saveOperationType;
- (NSString *)fileName;
- (NSFileWrapper *)fileWrapperRepresentationOfType:(NSString *)type;
- (id)initWithContentsOfFile:(NSString *)absolutePath ofType:(NSString *)typeName;
- (id)initWithContentsOfURL:(NSURL *)absoluteURL ofType:(NSString *)typeName;
- (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)type;
- (BOOL)loadFileWrapperRepresentation:(NSFileWrapper *)wrapper ofType:(NSString *)type;
- (void)printShowingPrintPanel:(BOOL)flag;
- (BOOL)readFromFile:(NSString *)fileName ofType:(NSString *)type;
- (BOOL)readFromURL:(NSURL *)url ofType:(NSString *)type;
- (BOOL)revertToSavedFromFile:(NSString *)fileName ofType:(NSString *)type;
- (BOOL)revertToSavedFromURL:(NSURL *)url ofType:(NSString *)type;
- (int)runModalPageLayoutWithPrintInfo:(NSPrintInfo *)printInfo;
- (void)saveToFile:(NSString *)fileName saveOperation:(NSSaveOperationType)saveOperation
delegate:(id)delegate didSaveSelector:(SEL)didSaveSelector contextInfo:(void *)contextInfo;
- (void)setFileName:(NSString *)fileName;
- (BOOL)writeToFile:(NSString *)fileName ofType:(NSString *)type;
- (BOOL)writeToFile:(NSString *)fullDocumentPath ofType:(NSString *)documentTypeName
originalFile:(NSString *)fullOriginalDocumentPath saveOperation:(NSSaveOperationType)saveOperationType;
- (BOOL)writeToURL:(NSURL *)url ofType:(NSString *)type;
- (BOOL)writeWithBackupToFile:(NSString *)fullDocumentPath ofType:(NSString *)documentTypeName
saveOperation:(NSSaveOperationType)saveOperationType;
Summary of Deprecated NSDocumentController Methods
Search in the above release notes for information about why each has been deprecated, and when it is still invoked.- (id)documentForFileName:(NSString *)fileName;
- (NSArray *)fileNamesFromRunningOpenPanel;
- (id)makeDocumentWithContentsOfFile:(NSString *)fileName ofType:(NSString *)type;
- (id)makeDocumentWithContentsOfURL:(NSURL *)url ofType:(NSString *)type;
- (id)makeUntitledDocumentOfType:(NSString *)type;
- (id)openDocumentWithContentsOfFile:(NSString *)fileName display:(BOOL)display;
- (id)openDocumentWithContentsOfURL:(NSURL *)url display:(BOOL)display;
- (id)openUntitledDocumentOfType:(NSString*)type display:(BOOL)display;
- (void)setShouldCreateUI:(BOOL)flag;
- (BOOL)shouldCreateUI;
NSPersistentDocument
NSPersistentDocument is a subclass of NSDocument that enables easily creating document types which are made persistent by the CoreData framework. NSPersistentDocument instances have an NSManagedObjectContext (and thus a NSPersistentStoreCoordinator - the default implementation creates a separate stack of context, coordinator and persistent store for each document, but that can be re-configured by overriding). The coordinator is created with [self model].NSPersistentDocument by default stores data in an XML file (an XML persistent store), but that can be reconfigured to SQL or binary formats by overriding the configurePersistentStoreCoordinatorForURL:ofType:error: method.
Summary of how various document operations work:
- Open: Invokes the configure... method with the file URL, adds a store of the default type (XML). Objects will be loaded from the persistent store on demand through the document's context.
- Save: On a new document, will add a store of the default type with the user's chosen URL, then invokes save: on the context. For an already open document, just invokes save: on the context.
- Save As: For a new document, falls back to Save:. For an already open document, migrates the persistent store to the new URL and invokes save: on the context.
- Save To: Not supported in Tiger.
- Revert: Reverts or resets the context. Objects will be loaded again from the persistent store just like the Open: case.
File package support (where potentially multiple persistent stores are inside a file wrapper) is left to subclasses where managing different stores can be done by hand through the coordinator.
NSApplication Support for the Enhanced Print Apple Event
A new Enhanced Print Apple event was defined by Mac OS 10.3 (http://developer.apple.com/technotes/tn2002/tn2082.html) but Cocoa did not support it properly. In Tiger mechanisms have been added to retrofit print event support to existing applications, but they do not work in all cases. To allow you to easily create non-NSDocument-based applications that handle Print Apple events reliably, a new NSApplication delegate has been added, and one deprecated:- (NSApplicationPrintReply)application:(NSApplication *)application printFiles:(NSArray *)fileNamesGiven that the application has been asked to print a group of files, print them. printSettings is a dictionary containing NSPrintInfo-compatible print job attributes. Your application should add them to any NSPrintInfo used to print the files, typically by invoking [[thePrintInfo dictionary] addEntriesFromDictionary:printSettings]. showPrintPanels is a flag indicating whether or not a print panel should be presented for each file being printed. If it is NO, no print panels should be presented (but print progress panels should still be presented). See -[NSPrintOperation setShowsPrintPanel:] below. Implementations of this method must return one of the following:
withSettings:(NSDictionary *)printSettings showPrintPanels:(BOOL)showPrintPanels;
typedef enum NSApplicationPrintReply {
NSPrintingCancelled = 0,
NSPrintingSuccess = 1,
NSPrintingFailure = 3,
NSPrintingReplyLater = 2
} NSApplicationPrintReply;
Return NSPrintingReplyLater if the result of printing cannot be returned immediately, if printing will cause the presentation of a sheet for instance. If your method returns NSPrintingReplyLater it must always invoke -[NSApplication replyToOpenOrPrint:] when the entire print operation has been completed, successfully or not.This delegate method replaces -application:printFiles:, which is now deprecated. If your application delegate only implements -application:printFiles: it will still be invoked, and NSApplication will use private functionality to arrange for the print settings to take effect despite the fact that an implementation of -application:printFiles: can't possibly know about them.
NSPrintInfo Support for the Enhanced Print Apple Event
Support for attributes corresponding to fields in the Enhanced Print Apple event's "with properties" parameter has been added to NSPrintInfo:NSString *NSPrintPagesAcross; // an NSNumber containing the number of logical pages to be placed across a physical sheetBug: NSPrintTime has no effect in Mac OS 10.4.
NSString *NSPrintPagesDown; // an NSNumber containing the number of logical pages to be placed down a physical sheet
NSString *NSPrintTime; // an NSDate containing the time at which printing should begin
NSString *NSPrintDetailedErrorReporting; // an NSNumber containing a boolean value
NSString *NSPrintFaxNumber; // an NSString containing a fax number
NSString *NSPrintPrinterName; // an NSString containing the name of a printer
NSPrintOperation Support for the Enhanced Print Apple Event
Because presentation of the print panel during handling of the Print event is optional, but the print progress panel should always be presented, new methods have been added to NSPrintOperation to give you individual control over the two kinds of panels:- (void)setShowsPrintPanel:(BOOL)flag;These methods replace -setShowPanels: and -showPanels, which are now deprecated. The old methods function as they did in Mac OS 10.3, but the return value of -showPanels: is ambiguous (implementation-wise, it returns the same value as -showsPrintPanel).
- (BOOL)showsPrintPanel;
- (void)setShowsProgressPanel:(BOOL)flag;
- (BOOL)showsProgressPanel;
New NSDocument Methods for the Enhanced Print Apple Event
A new Enhanced Print Apple event was defined by Mac OS 10.3 (<http://developer.apple.com/technotes/tn2002/tn2082.html>) but Cocoa did not support it properly. In Tiger mechanisms have been added to retrofit print event support to existing applications, but they do not work in all cases. To allow you to easily create document-based applications that handle Print Apple events properly, two new methods have been added to NSDocument, and methods have been deprecated:- (void)printDocumentWithSettings:(NSDictionary *)printSettings showPrintPanel:(BOOL)showPrintPanelPrint the document. If showing of the print panel is specified, present it first, and print only if the user OKs the panel. The NSPrintInfo attributes in the passed-in printSettings dictionary should be added to a copy of the document's print info, and the resulting print info should be used for the operation. When printing is completed or has been canceled, send the message selected by didPrintSelector to the delegate, with the contextInfo as the last argument. The method selected by didPrintSelector must have the same signature as:
delegate:(id)delegate didPrintSelector:(SEL)didPrintSelector contextInfo:(void *)contextInfo;
- (void)document:(NSDocument *)document didPrint:(BOOL)didPrintSuccessfully contextInfo:(void *)contextInfo;The default implementation of this method first makes sure that any editor registered using Cocoa Bindings' NSEditorRegistration informal protocol has committed its changes, then invokes [self printOperationWithSettings:printSettings error:&anError]. If nil is returned it presents the error to the user in a document-modal panel before messaging the delegate. Otherwise it invokes [thePrintOperation setShowsPrintPanel:showPrintPanel] then [self runModalPrintOperation:thePrintOperation delegate:delegate didRunSelector:didPrintSelector contextInfo:contextInfo]. Your are unlikely to need to override this method.
This method replaces -printShowingPrintPanel:, which is now deprecated. For backward binary compatibility the old method is still invoked when appropriate if it is overridden. When this is done Cocoa uses private functionality to arrange for 1) the print settings to take effect despite the fact that the override of -printShowingPrintPanel: can't possibly know about them, and 2) getting notified when the print operation has been completed, so it can message the delegate at the correct time. Correct messaging of the delegate is necessary for correct handling of the Print Apple event.
- (NSPrintOperation *)printOperationWithSettings:(NSDictionary *)printSettings error:(NSError **)outError;Create a print operation that can be run to print the document's current contents, and return it if successful. If not successful, return nil after setting *outError to an NSError that encapsulates the reason why the print operation could not be created. The NSPrintInfo attributes in the passed-in printSettings dictionary should be added to a copy of the document's print info, and the resulting print info should be used for the operation. The default implementation of this method does nothing. You must override it to enable printing in your application.
-runModalPageLayoutWithPrintInfo: is now deprecated, because it can only present an application-modal panel, which is a poor user interface in this case. Use -runModalPageLayoutWithPrintInfo:delegate:didRunSelector:contextInfo: instead. -runModalPageLayoutWithPrintInfo: is never invoked from anywhere within Cocoa, as in Mac OS 10.3 and earlier.
NSView Page Header/Footer Printing API (Section added since WWDC)
Tiger adds API that makes it easier for a Cocoa application to include page headers and footers in its printed output. This feature is off by default, but can be enabled using NSPrintInfo's new NSPrintHeaderAndFooter attribute:APPKIT_EXTERN NSString *NSPrintHeaderAndFooter;AppKit also recognizes an identically-named user default, so this feature can be easily tested with an existing application executable by launching the application with the default set to YES.
For example:
MyApp.app/Contents/MacOS/MyApp -NSPrintHeaderAndFooter YES
When this feature is enabled, NSView's -drawPageBorderWithSize: method, which previously did nothing, prints a header and footer on each page. (Thus any override of -drawPageBorderWithSize: will need to invoke super's implementation if it wants to inherit this behavior.) The header and footer content are provided by two new NSView methods:
- (NSAttributedString *)pageHeader;NSView's implementations of these methods provide a default header that includes the print job title (which typically is the same as the document or window title) and the date, and a default footer that includes the page number and page count. A printable view class can override one or both of these methods to substitute its own content in place of these defaults.
- (NSAttributedString *)pageFooter;
Each of these methods is invoked once per page during printing, offering implementors the chance to provide potentially page-specific header/footer content (including an appropriate page number, and perhaps offering different content or alignment for even vs. odd pages, for example). Implementors will typically consult NSPrintOperation's printInfo object to obtain data of interest to include in the header/footer text.
Note that the use of NSAttributedString as the return type for these methods provides for a great deal of power and flexibility. Content can appear both horizontally centered and at the page corners through use of left/center/right aligned tab stops, and images can be included as text attachments.
It is left up to the client application to expose this feature in its user interface, if desired. A simple checkbox in a print sheet accessory view, or the application's Preferences panel, would suffice. An application that provided for user customization the header/footer content could opt to go further.
NSView Drawing Redirection API (Section added since WWDC)
Tiger adds three new methods to NSView that enable an application to ask a view subtree to draw into a specified NSBitmapImageRep or NSGraphicsContext. This feature can be used to take a snapshot of the drawing done by a view and its descendants, either as a cache for computationally expensive view drawing, or for use in some subsequent graphics operation. It does not currently support capture of views that render to hardware-accelerated surfaces (i.e. NSOpenGLViews and NS/QTMovieViews), but otherwise works with all Quartz-based view drawing. The views to be drawn can, but need not, reside in a window. A client can request drawing of a view's entire content, or some rectangular subregion.Clients of this API should begin by obtaining an NSBitmapImageRep of appropriate dimensions and color depth for capturing the desired view content. Such a bitmap can be obtained using the new NSView method:
- (NSBitmapImageRep *)bitmapImageRepForCachingDisplayInRect:(NSRect)rect;A client wishing to capture only the parts of the receiver that are not clipped by ancestor views should pass the receiver's "visibleRect" as the NSRect parameter. It is also possible to snapshot parts of a view that are currently clipped (due to an enclosing NSScrollView, for example), by specifying a rectangle that is outside the receiver's visibleRect, but still within its bounds. The -bitmapImageRepForCachingDisplayInRect: method returns a new, autoreleased NSBitmapImageRep instance that is suitable for caching a copy of the specified view area.
Once a suitable target bitmap has been obtained, the client should initialize it as desired before asking the view subtree to draw into it. To initialize the bitmap to fully transparent, for example, one could use the following code:
NSGraphicsContext *bitmapGraphicsContext = [NSGraphicsContext graphicsContextWithBitmapImageRep:cacheBitmapImageRep];The desired view subtree area can then be drawn into the bitmap using the new method:
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:bitmapGraphicsContext];
[[NSColor clearColor] set];
NSRectFill(NSMakeRect(0, 0, [cacheBitmapImageRep size].width, [cacheBitmapImageRep size].height));
[NSGraphicsContext restoreGraphicsState];
- (void)cacheDisplayInRect:(NSRect)rect toBitmapImageRep:(NSBitmapImageRep *)bitmapImageRep;The "rect" parameter should be of the same size as that passed to bitmapImageRepForCachingDisplayInRect:. This method creates an NSGraphicsContext for drawing into the NSBitmapImageRep, translates its coordinate system to place the requested rectangle at the origin, and then asks the view subtree to draw into the NSGraphicsContext using another new NSView method:
- (void)displayRectIgnoringOpacity:(NSRect)aRect inContext:(NSGraphicsContext *)context;Clients that want to set up a different transform or other graphics context parameters may also send this message directly instead of cacheDisplayInRect:toBitmapImageRep:, but will otherwise usually find the latter approach more convenient.
NSView.h contains a declaration for an additional new method:
- (BOOL)lockFocusIfCanDrawInContext:(NSGraphicsContext *)context;This is not yet used in Tiger, but may be made functional in the future.
NSView -registeredDraggedTypes API
Tiger adds a -registeredDraggedTypes accessor to NSView. This method can be used to query a view for the set of drag types for which the view has been registered via -registerForDraggedTypes. The elements in the returned array are in no particular order, but the array is guaranteed not to contain duplicate entries.NSView -drawPageBorderWithSize: fix (Section added since WWDC)
On previous versions of OS X, locking focus to draw in an override of NSView's -drawPageBorderWithSize: method applies a transform and clip that are unsuitable for drawing in the page border area. A workaround for this issue is discussed in Cocoa : Printing : Providing a Custom Pagination Scheme. On Tiger, the workaround is no longer necessary, but will cause no harm in applications that employ it.NSView +new Behavior Change
For applications linked on or after Tiger, NSView's +new is equivalent to [[ViewClass alloc] init], which is more consistent with the usual meaning of +new than the previous behavior of invoking [[ViewClass alloc] initWithFrame:...].Thus, if an NSView subclass overrides -init, +new will invoke the subclass's -init implementation, whereas it would have been bypassed in favor of -initWithFrame: before.
NSView Transform Fixes
NSView's -convertRect:fromView: and -convertRect:toView: methods have been fixed in Tiger. Previously, these methods could return a rect of insufficient width or height under some transformations.NSApplication
In Tiger, we changed the behavior of NSApplication for some modal panels. It is now possible to hide an application while a modal panel is open. It is also possible to quit an application while the open panel is up.We now show the menuBar earlier in application launch - right after sending the applicationWillFinishLaunching notification. If your application wants to hide the menuBar on launch, you should call +[NSMenu setMenuBarVisible:NO] during applicationWillFinishLaunching.
Applications will no longer be told to open an untitled document if launched as a login item or if launched as a service provider.
The following problems were fixed:
- an application delegate that implemented only application:openFiles: (and not application:openFile:) would not be able to open recent documents.
- NSWorkspaceWillPowerOffNotification was sent whenever an application received a 'quit' AppleEvent, whether from loginwindow, the dock, or an AppleScript. This notification is now only sent for logout, restart, or shutdown.
- drawers and NSStatusItems could prevent an application whose delegate returned YES from -applicationShouldTerminateAfterLastWindowClosed: from terminating when the last window was closed.
Dragging (Section updated since WWDC)
In Tiger, we fixed a problem where the offset passed to -[NSView dragImage:at:offset:event:pasteboard:source:slideBack:] was ignored when positioning the dragged image onscreen, but not ignored when passing the drag location to the source in draggedImage:endedAt:operation:. This offset is now universally ignored, because it no longer makes sense with the underlying implementation. This fix applies only to applications built on Tiger or later for compatibility reasons.We added a method to the NSDraggingDestination informal protocol to allow dragging destinations to specify that they are not interested in periodic -draggingUpdated messages. On Panther and previous, the dragging code sends periodic -draggingUpdated messages to a destination even if there is no change to the drag (the mouse hasn't moved and modifier flags haven't changed), as documented. We have added a new method to allow dragging destinations to override this behavior.
@interface NSObject(NSDraggingDestination)-wantsPeriodicDraggingUpdates will be sent to the dragging destination view or window if it responds. If the destination responds and returns NO, -draggingUpdated messages will be sent only when the mouse moves or a modifier flag changes. Otherwise, the default behavior will be to continue to send periodic dragging updated messages even if nothing is changing.
...
- (BOOL)wantsPeriodicDraggingUpdates;
...
@end
We added the ability to specify an HFS promise drag without using dragPromisedFilesOfTypes:fromRect:source:slideBack:event:. A dragging source may now add the NSFilesPromisePboardType to the pasteboard directly, and should call setPropertyList:forType: to write the array of fileTypes as the data for this type. The object passed in the source argument of dragImage:at:offset:event:pasteboard:source:slideBack: must implement namesOfPromisedFilesDroppedAtDestination: to provide the file names when the drag is dropped.
We fixed a bug where hidden views could be the target of a drag.
NSEvent (Section updated since WWDC)
In Tiger, we added a constant, NSDeviceIndependentModifierFlagsMask, to be used in a bitwise-and with -[NSEvent modifierFlags] to retrieve only the device independent modifer flags. This allows applications to mask off the device dependent modifierFlags, include event coalescing information. Eg. an application could detect if any device independent modifier flags are present in theEvent using:if (([theEvent modifierFlags] & NSDeviceIndependentMofidierFlagsMask) != 0) ...We fixed a long standing bug where the event passed to NSApplication and NSWindow -discardEventsMatchingMask:beforeEvent: was ignored, and all events in the queue matching the mask were discarded. For applications built on Tiger or later, we now check event times against the event passed in, and discard only those events whose event time is earlier. For applications built on Panther or earlier, we continue to discard all events in the queue matching the mask.
NSScrollWheel events (Section added since WWDC)
In order to support finer-grained scrolling required by new devices, the NSScrollWheel event and the handling of NSScrollWheel events within NSScrollView has changed. For scrollWheel mice, the delta fields may now contain fractional components. These fractional values are made available to applications built on Tiger or later. Applications built on Tiger or later which do their own handling of the NSScrollWheel event need to be prepared to deal with fractional values, for example by rounding the scroll amount computed from the event to an integral value before scrolling. Applications built on Panther or earlier will receive integral values compatible with the values received on previous releases.In addition, new devices may send the NSScrollWheel event much more frequently, in some cases with both deltaX and deltaY of 0. These scroll events contain device dependent scroll information which allow NSScrollView to do continuous scrolling, so should be passed to super if your subclass allows NSScrollView to do the scrolling. If your subclass is doing custom scrolling, you should be prepared to ignore NSScrollWheel events whose deltaX and deltaY are both 0. This change affects applications built on Tiger and on previous releases.
Tablet event support
In Panther, tablet drivers started generating native tablet events in addition to mouse events with embedded tablet data. In Tiger, we have added API to define the tablet event types, to provide accessors for tablet event data from native tablet events and mouse events with tablet data, and to allow tablet events to be sent through the responder chain.Tablet drivers generate tablet events in two ways. In some cases (dual-puck tablet point events, "modern" proximity events), the driver generates pure tablet events. These event have been added to NSEvent.h:
typedef enum _NSEventType { /* various types of events */
...
NSTabletPoint = 23,
NSTabletProximity = 24,
...
} NSEventType;
Along with the corresponding masks:enum { /* masks for the types of events */
...
NSTabletPointMask = 1 << NSTabletPoint,
NSTabletProximityMask = 1 << NSTabletProximity,
...
};
In other cases (single-puck tablet point events, compatibility proximity events), the driver generates mouseDown/Up/Dragged/Moved events with additional tablet data. These events can be identified by their subtype. We have published the mouse event subtypes so applications can tell when an event has tablet information. These subtypes are declared in terms of constants defined in IOLLEvent.h:enum { /* event subtypes for mouse events */
NSMouseEventSubtype = NX_SUBTYPE_DEFAULT,
NSTabletPointEventSubtype = NX_SUBTYPE_TABLET_POINT,
NSTabletProximityEventSubtype = NX_SUBTYPE_TABLET_PROXIMITY
};
The NSPointerType enumeration declares the possible values returned by -pointerType. These values are declared in terms of constants that will be defined in IOLLEvent.h:/* pointer types for NSTabletProximity events or mouse events with subtype NSTabletProximityEventSubtype*/The following enumeration declares possible values to be included in the bitwise-or returned by -buttonMask. These values are declared in terms of constants defined in IOLLEvent.h:
typedef enum {
NSUnknownPointingDevice = NX_TABLET_POINTER_UNKNOWN,
NSPenPointingDevice = NX_TABLET_POINTER_PEN,
NSCursorPointingDevice = NX_TABLET_POINTER_CURSOR,
NSEraserPointingDevice = NX_TABLET_POINTER_ERASER
} NSPointingDeviceType;
/* button masks for NSTabletPoint events or mouse events with subtype NSTabletPointEventSubtype */The -pressure and -subtype messages are now valid for new event types as described in the comments:
enum {
NSPenTipMask = NX_TABLET_BUTTON_PENTIPMASK,
NSPenLowerSideMask = NX_TABLET_BUTTON_PENLOWERSIDEMASK,
NSPenUpperSideMask = NX_TABLET_BUTTON_PENUPPERSIDEMASK
};
/* This message is valid for all mouse down/up/drag events */
/* This message is also valid for mouse events with subtype NSTabletPointEventSubtype and
for NSTabletPoint events on 10.4 or later */
- (float)pressure;
/* this message is valid for kit, system, and app-defined events */In addition, we have added accessors for the fields of the tablet events:
/* this message is also valid for mouse down/up/drag/move events on 10.4 or later */
- (short)subtype;
/* this message is valid for mouse events with subtype NSTabletPointEventSubtype or
NSTabletProximityEventSubtype, and for NSTabletPoint and NSTabletProximity events */
- (unsigned int)deviceID; // system-assigned unique device ID
/* these messages are valid for mouse events with subtype NSTabletPointEventSubtype, and for NSTabletPoint events */
- (int)absoluteX; // absolute x coordinate in tablet space at full tablet resolution
- (int)absoluteY; // absolute y coordinate in tablet space at full tablet resolution
- (int)absoluteZ; // absolute z coordinate in tablet space at full tablet resolution
- (unsigned int)buttonMask; // mask indicating which buttons are pressed.
- (NSPoint)tilt; // range is -1 to 1 for both axes
- (float)rotation; // device rotation in degrees
- (float)tangentialPressure; // tangential pressure on the device; range is -1 to 1
- (id)vendorDefined; // NSArray of 3 vendor defined shorts
/* these messages are valid for mouse events with subtype NSTabletProximityEventSubtype, and for NSTabletProximity events */Lastly, we have added NSResponder methods for the pure tablet event types:
- (unsigned int)vendorID; // vendor defined, typically USB vendor ID
- (unsigned int)tabletID; // vendor defined tablet ID
- (unsigned int)pointingDeviceID; // vendor defined ID of pointing device
- (unsigned int)systemTabletID; // system assigned unique tablet ID
- (unsigned int)vendorPointingDeviceType; // vendor defined pointing device type
- (unsigned int)pointingDeviceSerialNumber; // vendor defined serial number of pointing device
- (unsigned long long)uniqueID; // vendor defined unique ID
- (unsigned int)capabilityMask; // mask representing capabilities of device
- (NSPointingDeviceType)pointingDeviceType; // type of pointing device
- (BOOL)isEnteringProximity; // YES - entering; NO - leaving
@interface NSResponder : NSObject <NSCoding>
...
- (void)tabletPoint:(NSEvent *)theEvent;
- (void)tabletProximity:(NSEvent *)theEvent;
...
@end
NSWindow (Section updated since WWDC)
In Tiger, ColorSync has added the ability to update the ColorSync profile attached to a window context. On Panther and previous, the ColorSync profile was defined by the profile of the main display at window server initialization time (=login time) and was cached and used for all new window contexts. In other words, windows could not pick up an updated ColorSync profile until the user logged out and back in. Even new windows created after a ColorSync profile change were not affected by the change. In Tiger, we will maintain this behavior for windows that do not adopt the new API. The profile of the main display will continue to be cached and used for any new window context that does not indicate otherwise. Maintaining this compatibility is important for correctness in apps that are doing cached drawing, and desirable for performance reasons in less sophisticated apps.We have exposed the new ColorSync support to windows that want to adopt it. Exposing the functionality implies updating the display profile attached to the window CGContextRef, and notifying the window when the display profile has changed.
@interface NSWindow : NSResponderIf a window returns YES for -displaysWhenScreenProfileChanges, AppKit will call CGWindowContextUpdateDisplayInfo for the window context then tell the window to display, under the following conditions:
...
- (void)setDisplaysWhenScreenProfileChanges:(BOOL)flag;
- (BOOL)displaysWhenScreenProfileChanges;
...
@end
- the majority of the window has moved to a different screen and the display profile for the current screen is different than that of the previous screen
- we receive a notification from color sync that the display profile has changed for the screen containing the majority of the window
- the window context is first created (although in this case the window would not need to be told to redisplay)
The default is NO.
We have also added a notification of when the display profile has changed, to be sent after AppKit has called CGWindowContextUpdateDisplayInfo and before the window is told to display, so that the window can update any offscreen caches:
APPKIT_EXTERN NSString *NSWindowDidChangeScreenProfileNotification;The window delegate can automatically register for this notification by implementing a new delegate method:
@interface NSObject(NSWindowNotifications)Adding a child window using -addChildWindow:ordered: now causes the child window to be ordered onscreen in the correct relative location (above or below) if the receiver is onscreen.
...
- (void)windowDidChangeScreenProfile:(NSNotification *)notification;
...
@end
We added support for a new window appearance where the background from the titlebar continues into the toolbar, making them appear unified. There is a new styleMask NSUnifiedTitleAndToolbarWindowMask to specify this window appearance.
The toolbar button is now optional:
- (void)setShowsToolbarButton:(BOOL)show;
- (BOOL)showsToolbarButton;
NSWindow -disableScreenUpdatesUntilFlush API (Section added since WWDC)
When a view that renders to a hardware surface (such as an OpenGL view) is placed in an NSScrollView or NSSplitView, there can be a noticeable flicker or lag when the scroll position or splitter position is moved. This happens because each move of the hardware surface takes effect immediately, before the remaining window content has had the chance to draw and flush.To enable applications to eliminate this visual artifact, Tiger AppKit provides a new NSWindow message, -disableScreenUpdatesUntilFlush. This message asks a window to suspend screen updates until the window's updated content is subsequently flushed to the screen. This message can be sent from a view that is about to move its hardware surface, to insure that the hardware surface move and window redisplay will be visually synchronized. The window responds by immediately disabling screen updates via a call to NSDisableScreenUpdates(), and setting a flag that will cause it to call NSEnableScreenUpdates() later, when the window flushes. It is permissible to send this message to a given window more than once during a given window update cycle; the window will only suspend and re-enable updates once during that cycle.
A view class that renders to a hardware surface can send this message from an override of -renewGState (a method that is is invoked immediately before the view's surface is moved) to effectively defer compositing of the moved surface until the window has finished drawing and flushing its remaining content.
- (void)renewGState {
NSWindow *window = [self window];
if ([window respondsToSelector:@selector(disableScreenUpdatesUntilFlush)]) {
[window disableScreenUpdatesUntilFlush];
}
[super renewGState];
}
In the above example, a -respondsToSelector: check has been used to provide compatibility with previous system releases. On pre-Tiger systems, where NSWindow has no -disableScreenUpdatesUntilFlush method, the -renewGState override will have no effect.Coalesced Updates (Section added since WWDC)
In Tiger the "coalesced updates" feature of the graphics subsystem improves drawing performance and eliminates visual tearing. However, it also causes applications which flush to the screen faster than 60 fps to stall while drawing. In order to maintain backward compatibility, there are compatibility checks in place where pre-Tiger applications which stall due to over-flushing are reverted back to Panther flushing behavior.In order to guarantee best performance, applications should refrain from animating at rates above 60 fps. And as before, it's a good idea to use NSView's setNeedsDisplay: and related methods rather than the immediate display method whenever possible, since setNeedsDisplay: enables coalescing drawing updates from different parts of the window and reduces unnecessary flushing to the screen.
The Quartz Debug application provides debugging facilities that can help determine if an application is over-flushing.
We expect to publish a tech note or a Q&A on this topic.
NSWindow live resize optimization
In Tiger, we added content preservation for windows and views during live resize. Prior to this change, the top level view in a window marked itself as needing display in its entire bounds when the window resized. When the top level view was drawn, all subviews were drawn as well, so all views completely redrew during live resize. This isn't always necessary, since in some cases the content of the view is only changing in the newly exposed area. For example in a simple window with left to right orientation, the left side window titlebar buttons and other portions of the window titlebar do not need to redraw.When this optimization is enabled, any view whose upper left corner moves relative to the upper left of the window is assumed to need complete redraw, because in general view content is position dependent (eg. when you move the splitView bar in the Mail viewer window, the content in the MessageTextView changes relative to the upper left corner of the window). However, any view whose upper left corner remains stationary with respect to the upper left of the window has the opportunity to preserve its content.
On Tiger, content preservation during live resize is enabled by default. However, we provide windows with a way to opt-out of this optimization. If there are no views that can take advantage of it, the window would pay an unneeded expense for preserving the old content.
@interface NSWindow : NSResponder
...
-(void)setPreservesContentDuringLiveResize:(BOOL)flag;
flag - YES to indicate that window should preserve content on live resize, NO otherwise. Default is YES.
- (BOOL)preservesContentDuringLiveResize;Content preservation at the view level is disabled by default for compatibility with views that should redraw completely when resized. A view can adopt content preservation by implementing the following to return YES:
...
@end
@interface NSView : NSResponderA view that returns YES for -preservesContentDuringLiveResize is responsible for invalidating its own dirty rects during live resize. Some views may find it convenient to implement setFrameSize: and do their own computation. Other views will want a way to get information about what has changed. We are adding a method that returns the rect representing the previously drawn content, and another method that returns rects representing the minimal area to dirty in the new bounds. These methods can be called during live resize, any time after setFrameSize: has been called on the view for the live resize operation (that is, any time after NSView's implementation of setFrameSize: has been called for that view) and before recursive display of the view hierarchy begins.
...
- (BOOL)preservesContentDuringLiveResize;
...
@end
@interface NSView : NSResponder
...
/* the rect returned from -rectPreservedDuringLiveResize indicates the rect the view previously occupied,
in the coordinate system of the current bounds. This may be smaller or larger than the current bounds,
depending on whether the view grew or shrunk. The result of this method is an empty rect for a view that
returns NO from -preservesContentDuringLiveResize or a view in a window that returns NO from
-preservesContentDuringLiveResize
*/
-(NSRect)rectPreservedDuringLiveResize;
/* exposedRects - on return from this method, exposedRects contains a list of rects (at most 4)
indicating the parts of the view that are newly exposed. If the view decreased in both height and width,
this list will be empty. If the view increased in both height and width and stayed anchored in the
upper left corner (currently always true but may change in the future), this list will include a
vertical component and a horizontal component indicating the exposed "L".
count - on return, the number of rectangles in the exposedRects list. Can be 0.
The result of this method is the entire view bounds for a view that returns NO from
-preservesContentDuringLiveResize or a view in a window that returns NO from -preservesContentDuringLiveResize
*/
-(void)getRectsExposedDuringLiveResize:(NSRect[4])exposedRects count:(int *)count;
...
@end
NSWindow graphicsContext
A new API graphicsContext is added. It returns an NSGraphicsContext instance associated with the receiver for the calling thread.New Behavior in -[NSWindow print:]
In Mac OS 10.0-10.3.x -[NSWindow print:] made a copy of [NSPrintInfo sharedPrintInfo], set some print parameters in the copy, and used the copy when creating a print operation. One of the print parameters that was set was orientation, to match the window's orientation. This was inappropriate, because it overrode whatever orientation the user had already set in the shared print info using a page setup panel. This has been fixed. -[NSWindow print:] no longer sets the printing orientation except, for backward binary compatibility, in applications linked against Mac OS 10.3 or earlier.Window menu (Section added since WWDC)
We have added an "Arrange in Front" alternate menu item for the "Bring All to Front" menu item in the Window menu. Applications should not hard code the indices of items in the Window menu, as the addition of an alternate menu item, or any other Window menu changes, will change the indices of other menu items. We have limited this change to applications built on Tiger or later to preserve binary compatibility.Metal window background
If you use the background color from a metal window in another window, it will not archive and unarchive correctly, and the unarchived color will lose its dynamic scaling capability. You can avoid this in a metal window by using -[NSWindow backgroundColor] or -[NSColor controlColor] to draw the metal background in custom controls.NSWindow key view loop
Methods have been added to allow a window to automatically recalculate the key view loop after views have been added.- (void)setAutorecalculatesKeyViewLoop:(BOOL)flag;If the autorecalculatesKeyViewLoop is YES, then whenever a view is added to the window, the key view loop is dirtied and recalculated automatically when -[NSWindow selectKeyViewFollowingView] or -[NSWindow selectKeyViewPrecedingView] are called. If autorecalculatesKeyViewLoop is NO, then adding views will not dirty the key view loop and the loop will not be recalculated. You can always call explicitly recalculateKeyViewLoop to recalculate the loop and clear the dirty key view loop flag. The method is also called if the key view loop needs to be calculated when the window is first ordered in. The loop computed is the same as the default one if initialFirstResponder is not set. It is based on the geometric order of the views in the window.
- (BOOL)autorecalculatesKeyViewLoop;
- (void)recalculateKeyViewLoop;
Keyboard Navigation
A window's toolbar and drawers are now reachable using the keyboards <tab> navigating mechanism. The keyboard navigation system expects separate keyboard loops in the toolbar, window content, and each drawer content. For example, the keyboard loop within a window's contentView should be a complete loop (following nextKeyView/previousKeyView) which does not leave the confines of the -contentView. Keyboard navigation is able to reach other loops (for example, travel from the window content to the toolbar), via -nextValidKeyView/-previousValidKeyView. When appropriate, next/previousValidKeyView will return key views in other key loops.NSAlert
In Tiger, we changed the instance variable names of NSAlert to be prefixed by underscores to be consistent with the AppKit naming conventions. This should not have any binary compatibility implications, but may require source changes in any NSAlert subclass that references instance variables by name. (Of course, since unless otherwise specified instance variables of Cocoa classes are private, applications should not be accessing instance variables.)NSScreen (Section added since WWDC)
+[NSScreen screens] will no longer return multiple screens when mirroring is enabled.Text
Text system's backing store string (as obtained by the string methods on NSTextView or NSTextStorage) now do better argument checking for applications linked on Tiger.Turns out initWithRTF:documentAttributes:, initWithDocFormat:documentAttributes:, and initWithRTFD:documentAttributes: could cause a crash if the document failed to open and the message was sent to NSTextStorage. This has been fixed in Tiger and in 10.3.3, but in earlier 10.3.x releases the only reasonable workaround is to avoid these messages if the data being provided might fail to open. You can use the read method, or call these on NSAttributedString.
NSTypesetter
The set of API introduced as part of NSATSTypesetter in OS X v10.3 Panther is moved to NSTypesetter. Applications can now implement a concrete subclass with a custom layout engine by overriding the layoutParagraphAtPoint: method.NSSimpleHorizontalTypesetter is deprecated and its interface declaration is moved to a new header NSSimpleHorizontalTypesetter.h.
In addition to the interface migrated from NSATSTypesetter, NSTypesetter has additional new API:
paragraphCharacterRange and paragraphSeparatorCharacterRange return ranges in the character space corresponding to their glyph version counterparts. attributesForExtraLineFragment is for accessing style information that should be used for the extra line fragment. actionForControlCharacterAtIndex: is used for determining actions triggerd from a control character at index.
The new API -getLineFragmentRect:usedRect:emainingRect:forStartingGlyphAtIndex:proposedRect:lineSpacing:paragraphSpacingBefore:paragraphSpacingAfter: supercedes -lineFragmentRectForProposedRect:remainingRect: in NSATSTypesetter.
NSATSTypesetter
lineFragmentRectForProposedRect:remainingRect: is deprecated. Refer to a new API provided by the base NSTypesetter class getLineFragmentRect:usedRect:emainingRect:forStartingGlyphAtIndex:proposedRect:lineSpacing:paragraphSpacingBefore:paragraphSpacingAfter:.NSTextView Multiple Selection (Section updated since WWDC)
The concept of selection in NSTextView has been extended in order to allow the selection of multiple discontiguous ranges at once. The two new primitives for selection are- (void)setSelectedRanges:(NSArray *)ranges affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)flag;parallel to the existing setSelectedRange:affinity:stillSelecting and selectedRange methods. The existing single-range methods setSelectedRange:, setSelectedRange:affinity:stillSelecting, and selectedRange are now defined in terms of these new methods. The affinity and stillSelectingFlag arguments retain their current meanings unaltered. The ranges argument is required to be a non-nil, non-empty array of objects responding to - (NSRange)rangeValue. The selectedRanges method is guaranteed to return an array satisfying the same criteria, and in addition its elements will be sorted ascending by location, and discontiguous (i.e. if r1 appears before r2 then NSMaxRange(r1) < r2.location), and furthermore if there is more than one element then no element will have length zero.
- (NSArray *)selectedRanges;
There are several additional new NSTextView methods:
- (void)setSelectedRanges:(NSArray *)ranges;The first is a convenience method parallel to the existing setSelectedRange:. The next three are parallel to the existing rangeForUserTextChange, rangeForUserCharacterAttributeChange, and rangeForUserParagraphAttributeChange methods, obeying the same restrictions described above for selectedRanges, except that they will return nil in case the appropriate change is not permitted, in the situations in which the existing single-range methods now return (NSNotFound, 0). The last is similar to the existing shouldChangeTextInRange:replacementString:; the replacementStrings array should either be nil, or else contain one element for each range in affectedRanges.
- (NSArray *)rangesForUserTextChange;
- (NSArray *)rangesForUserCharacterAttributeChange;
- (NSArray *)rangesForUserParagraphAttributeChange;
- (BOOL)shouldChangeTextInRanges:(NSArray *)affectedRanges replacementStrings:(NSArray *)replacementStrings;
The existing methods returning a single range will now return the first subrange where there is a multiple-range selection. This means that code that uses the existing single-range APIs will continue to operate, acting on the first selected subrange when there are multiple selections. This is in fact the appropriate behavior for many purposes, and is expected to remain a reasonable default. Most AppKit methods have been updated where appropriate to act on all of the subranges of a multiple selection, particularly methods that modify attributes.
There are two new NSTextView delegate methods:
- (NSArray *)textView:(NSTextView *)textViewin which the arguments satisfy criteria like those of the return value of selectedRanges. In the first, the return value must satisfy criteria like those for the ranges argument to setSelectedRanges:affinity:stillSelecting:.
willChangeSelectionFromCharacterRanges:(NSArray *)oldSelectedCharRanges
toCharacterRanges:(NSArray *)newSelectedCharRanges;
- (BOOL)textView:(NSTextView *)textView
shouldChangeTextInRanges:(NSArray *)affectedRanges
replacementStrings:(NSArray *)replacementStrings;
If a delegate implements the old delegate method
- (NSRange)textView:(NSTextView *)textViewand not the new one, then multiple selection will effectively be disallowed; attempts to set the selected ranges will call the old delegate method with the first subrange, and afterwards only a single selected range will be set. If a delegate implements the new method, then the old one will be ignored.
willChangeSelectionFromCharacterRange:(NSRange)oldSelectedCharRange
toCharacterRange:(NSRange)newSelectedCharRange;
If the delegate implements the old delegate method
- (BOOL)textView:(NSTextView *)textViewthen it will be called with an appropriate range and string. If a delegate implements the new method, then the old one will be ignored.
shouldChangeTextInRange:(NSRange)affectedCharRange
replacementString:(NSString *)replacementString;
There are several means provided for users to make a multiple selection. First, command-selection can be used in a text view with the usual meaning--it selects new portions of the text, or unselects already selected portion. Second, option-selection can be used in a text view to make a rectangular selection; there is a distinctive cursor provided for this mode. A default is provided, NSProhibitMultipleTextSelectionByMouse, to control this behavior; if the default is set to YES, then command- and option-selection in text will not have their new special meanings, but will behave as they did in Panther. This mechanism allows the new means of selection to be turned off for applications in which they may not be desired.
In addition, two new NSFindPanelActions are provided, NSFindPanelSelectAll and NSFindPanelSelectAllInSelection, to allow selection of all instances of a particular find string; the standard find panel uses these if the control key is held down. Finally, the styles panel supports selection of all text having a particular style--this could be used, for example to select all text in a given font, then subsequently convert it to a different font, or to select all underlined text and subsequently italicize it.
Removal of Override of -attributedSubstringFromRange: in NSTextStorage Category
In Mac OS 10.0-10.3.x there was an override of -[NSAttributedString attributedSubstringFromRange:] in a private category of NSTextStorage. The override returned an instance of NSSubTextStorage, a private subclass of NSTextStorage (and therefore NSAttributedString) used by Cocoa's scripting subsystem. This NSSubTextStorage object responded to all NSAttributedString messages, but its value would change with the value of the "parent" NSAttributedString, which was not correct and caused several difficult-to-diagnose bugs. This override has been removed. NSTextStorages now return nonmutating NSAttributedStrings when sent the -attributedSubstringFromRange: message.NSParagraphStyle
NSParagraphStyle features new methods hyphenationFactor and tighteningFactorForTruncation. Corresponding writer methods, setHyphenationFactor: and setTighteningFactorForTruncation: are added to NSMutableParagraphStyle.NSParagraphStyle Header Level
NSParagraphStyle now defines a new attribute, the header level, an integer which should either be 0 or else in the range 1-6. This is currently used only for HTML import/export, defining whether a particular paragraph is a header or not and if so of what level. The relevant methods are- (int)headerLevel;on NSParagraphStyle and
- (void)setHeaderLevel:(int)level;on NSMutableParagraphStyle.
Base writing direction
Accessor methods for the base writing direction, baseWritingDirection and setBaseWritingDirection:, have been added to NSCell, NSControl, and NSText classes.Also, NSTextView and NSMutableAttributedString now have setBaseWritingDirection:range: API.
NSStringDrawing
New methods have been added to NSString and NSAttributedString categories defined in NSStringDrawing.h. drawWithRect:options: and boundingRectWithSize:options: offer more precise string rendering/sizing functionality.NSFont and NSFontDescriptor
The NSFont and NSFontDescriptor classes have been restructured. NSFontDescriptor is now promoted to be the primary font reference in AppKit replacing Postscript names. Also, new functionalities such as font matching, font substitution, and font cascading are built into the NSFontDescriptor class.NSGlyphInfo (Section added since WWDC)
The NSGlyphInfo class now conforms to the NSCopying protocol.NSTextView Typing Attributes Delegate Method
In addition to the existing notification, NSTextView now has a new delegate method allowing the delegate to intervene to allow, prevent, or modify changes to typing attributes:- (NSDictionary *)textView:(NSTextView *)textView
shouldChangeTypingAttributes:(NSDictionary *)oldTypingAttributes
toAttributes:(NSDictionary *)newTypingAttributes;
NSTextView undo coalescing (Section added since WWDC)
NSTextView now exposes a new method to control undo coalescing:- (void)breakUndoCoalescing;This is useful for instance as a way to get an app to prevent successive typing operations before and after a save from being coalesced into a single undoable item by the text view.
New Responder Methods
NSResponder now defines two new action methods that are implemented in NSTextView. The first is intended to insert a line break (as distinguished from a paragraph break). The NSTextView implementation inserts an NSLineSeparatorCharacter. The second is intended to insert a container break (typically a page break). The NSTextView implementation inserts an NSFormFeedCharacter.- (void)insertLineBreak:(id)sender;
- (void)insertContainerBreak:(id)sender;
Text Tables (Section updated since WWDC)
The Cocoa text system now supports tables. The basic object involved is a new class, NSTextBlock, which represents a block of text that is to be laid out in a sub-region of the text container. The most important subclass is NSTextTableBlock, which represents a block of text that appears as a cell in a table. The table itself is represented by a separate class, NSTextTable, which is referenced by all of its NSTextTableBlocks and which controls their sizing and positioning.Text blocks appear as attributes on paragraphs, as part of the paragraph style. An NSParagraphStyle now may have an array of text blocks, representing the nested blocks containing the paragraph, in order from outermost to innermost. For example, if block1 contains four paragraphs, the middle two of which are also in the inner block2, then the text blocks array for the first and fourth paragraphs will be (block1), while the text blocks array for the second and third paragraphs will be (block1, block2).
The methods implementing this are
- (NSArray *)textBlockson NSParagraphStyle, and
- (void)setTextBlocks:(NSArray *)arrayon NSMutableParagraphStyle.
In addition, there are convenience methods on NSAttributedString for determining the range covered by a block or a table (or (NSNotFound, 0) if the given location is not in the specified block or table):
- (NSRange)rangeOfTextBlock:(NSTextBlock *)block atIndex:(unsigned)location;During layout, the typesetter proposes a large rect within which a particular block should fit. For the outermost block, this will be determined by the text container; for inner blocks, it will be determined by the containing block. The block object then decides what subrect of this it should actually take up. There are actually two rects that are determined: first, the layout rect, within which text in the block is to be laid out; second, the bounds rect, which also contains space for padding, borders, border decoration, margins, and so forth. The layout rect is determined immediately before the first glyph in a particular block is laid out, because it is needed for all subsequent layout of text in the block. It will often be quite tall, because at this point the height of the text laid out in the block has not yet been determined. The bounds rect is determined immediately after the last glyph in the block has been laid out, and it is based on the actual used rect for the text within the block.
- (NSRange)rangeOfTextTable:(NSTextTable *)table atIndex:(unsigned)location;
The methods on NSTextBlock that are called by the typesetter are
- (NSRect)rectForLayoutAtPoint:(NSPoint)startingPoint inRect:(NSRect)rectAn NSTextTableBlock will call upon its NSTextTable to perform these calculations, using its methods
textContainer:(NSTextContainer *)textContainer characterRange:(NSRange)charRange;
- (NSRect)boundsRectForContentRect:(NSRect)contentRect inRect:(NSRect)rect
textContainer:(NSTextContainer *)textContainer characterRange:(NSRange)charRange;
- (NSRect)rectForBlock:(NSTextTableBlock *)block layoutAtPoint:(NSPoint)startingPoint inRect:(NSRect)rectThe typesetter stores the results of these methods in the layout manager. The new NSLayoutManager methods are:
textContainer:(NSTextContainer *)textContainer characterRange:(NSRange)charRange;
- (NSRect)boundsRectForBlock:(NSTextTableBlock *)block contentRect:(NSRect)contentRect inRect:(NSRect)rect
textContainer:(NSTextContainer *)textContainer characterRange:(NSRange)charRange;
- (void)setLayoutRect:(NSRect)rect forTextBlock:(NSTextBlock *)block glyphRange:(NSRange)glyphRange;The first two methods are used by the typesetter to store the information obtained from text blocks. The others are used during layout when it is necessary to determine what space has been taken up by previously laid blocks. The last three are variants of existing methods, that have the option of not causing additional layout; when they are called during layout, they must be called in such a way as not to force further layout so as to avoid an infinite recursion. For the same reason, the ...RectForTextBlock methods cause glyph generation but not layout; if no rect has been set, they return NSZeroRect. The layout rect should be set immediately before the first glyph in the block is laid out; the bounds rect should be set immediately after the last glyph in the block has been laid out. Under some circumstances, the bounds rect may be adjusted subsequently, as additional blocks in the same table are laid out.
- (void)setBoundsRect:(NSRect)rect forTextBlock:(NSTextBlock *)block glyphRange:(NSRange)glyphRange;
- (NSRect)layoutRectForTextBlock:(NSTextBlock *)block glyphRange:(NSRange)glyphRange;
- (NSRect)boundsRectForTextBlock:(NSTextBlock *)block glyphRange:(NSRange)glyphRange;
- (NSRect)layoutRectForTextBlock:(NSTextBlock *)block atIndex:(unsigned)glyphIndex effectiveRange:(NSRangePointer)effectiveGlyphRange;
- (NSRect)boundsRectForTextBlock:(NSTextBlock *)block atIndex:(unsigned)glyphIndex effectiveRange:(NSRangePointer)effectiveGlyphRange;
- (NSRect)lineFragmentRectForGlyphAtIndex:(unsigned)glyphIndex effectiveRange:(NSRangePointer)effectiveGlyphRange
withoutAdditionalLayout:(BOOL)flag;
- (NSRect)lineFragmentUsedRectForGlyphAtIndex:(unsigned)glyphIndex effectiveRange:(NSRangePointer)effectiveGlyphRange
withoutAdditionalLayout:(BOOL)flag;
- (NSTextContainer *)textContainerForGlyphAtIndex:(unsigned)glyphIndex effectiveRange:(NSRangePointer)effectiveGlyphRange
withoutAdditionalLayout:(BOOL)flag;
At display time, the text is drawn as usual, but the text block is called upon to draw any background or border decoration while glyph backgrounds are being drawn, using the following method:
- (void)drawBackgroundWithFrame:(NSRect)frameRect inView:(NSView *)controlViewand again NSTextTableBlocks call upon their NSTextTable for this using its method:
characterRange:(NSRange)charRange layoutManager:(NSLayoutManager *)layoutManager;
- (void)drawBackgroundForBlock:(NSTextTableBlock *)block withFrame:(NSRect)frameRectThe sizing and positioning model for text blocks involves a number of dimensions for each block, each of which may either have an absolute value in points or else be expressed as a percentage of the containing block. These dimensions include width, height, minimum and maximum width and height, and margin, border, and padding widths for each of the four sides. The default in each case will be 0, meaning no margin/border/padding and the natural width and height. Certain portions of the block will have their own colors, the default being nil for no color.
inView:(NSView *)controlView characterRange:(NSRange)charRange
layoutManager:(NSLayoutManager *)layoutManager;
- (void)setValue:(float)val type:(NSTextBlockValueType)type forDimension:(NSTextBlockDimension)dimension;
- (float)valueForDimension:(NSTextBlockDimension)dimension;
- (NSTextBlockValueType)valueTypeForDimension:(NSTextBlockDimension)dimension;
- (void)setWidth:(float)val type:(NSTextBlockValueType)type forLayer:(NSTextBlockLayer)layer edge:(NSRectEdge)edge;
- (void)setWidth:(float)val type:(NSTextBlockValueType)type forLayer:(NSTextBlockLayer)layer; // Convenience method
- (float)widthForLayer:(NSTextBlockLayer)layer edge:(NSRectEdge)edge;
- (NSTextBlockValueType)widthValueTypeForLayer:(NSTextBlockLayer)layer edge:(NSRectEdge)edge;
- (void)setBackgroundColor:(NSColor *)color;NSTextTableBlock and NSTextTable instances have additional methods specific to table cells and to tables. For NSTextTableBlock:
- (NSColor *)backgroundColor;
- (void)setBorderColor:(NSColor *)color forEdge:(NSRectEdge)edge;
- (void)setBorderColor:(NSColor *)color; // Convenience method sets all edges at once
- (NSColor *)borderColorForEdge:(NSRectEdge)edge;
- (id)initWithTable:(NSTextTable *)table startingRow:(int)row rowSpan:(int)rowSpanFor NSTextTable:
startingColumn:(int)col columnSpan:(int)colSpan; /* Designated initializer */
- (NSTextTable *)table;
- (int)startingRow;
- (int)rowSpan;
- (int)startingColumn;
- (int)columnSpan;
- (unsigned)numberOfColumns;
- (void)setNumberOfColumns:(unsigned)numCols;
- (NSTextTableLayoutAlgorithm)layoutAlgorithm;
- (void)setLayoutAlgorithm:(NSTextTableLayoutAlgorithm)algorithm;
- (BOOL)collapsesBorders;
- (void)setCollapsesBorders:(BOOL)flag;
- (BOOL)hidesEmptyCells;
- (void)setHidesEmptyCells:(BOOL)flag;
Text Lists
The Cocoa text system now supports lists. The basic object involved is a new class, NSTextList, which represents a section of text that forms a single list. The visible elements of the list, including list markers, will still appear in the text as they do for lists created by hand today. The list object, however, will allow the list to be recognized as such by the text system, so that markers and spacing can be automatically created for the user, and so that lists can be properly noted when saving in various rich text formats.Text lists appear as attributes on paragraphs, as part of the paragraph style. An NSParagraphStyle now may have an array of text lists, representing the nested lists containing the paragraph, in order from outermost to innermost. For example, if list1 contains four paragraphs, the middle two of which are also in the inner list2, then the text lists array for the first and fourth paragraphs will be (list1), while the text lists array for the second and third paragraphs will be (list1, list2).
The methods implementing this are
- (NSArray *)textListson NSParagraphStyle, and
- (void)setTextLists:(NSArray *)arrayon NSMutableParagraphStyle.
In addition, there are convenience methods on NSAttributedString for determining the range covered by a list and the ordinal position within a list of a particular item:
- (NSRange)rangeOfTextList:(NSTextList *)list atIndex:(unsigned)location;The NSTextList object itself describes the format of lists markers:
- (int)itemNumberInTextList:(NSTextList *)list atIndex:(unsigned)location;
- (id)initWithMarkerFormat:(NSString *)format options:(unsigned)mask;Here "marker" refers to the computed value for a specific ordinal position in the list, and "marker format" refers to a generic string used to specify the format for all markers in the list. The marker format is interpreted as a constant string, except for a numbering specifier, which will take the form "{<keyword>}". The currently supported values for <keyword> include decimal, lower-roman, upper-roman, lower-alpha, and upper-alpha. Thus "({decimal})" would be the format for a list number (1), (2), (3)... The only option value currently defined is NSTextListPrependEnclosingMarker, signaling that a nested list should include the marker for its enclosing super-list before its own marker.
- (NSString *)markerFormat;
- (unsigned)listOptions;
- (NSString *)markerForItemNumber:(int)itemNum;
Text Panels (Section updated since WWDC)
NSTextView now defines several action methods that are intended to bring forward panels to allow the user to manipulate paragraph spacing, links, lists, and tables in the text. TextEdit now has a menu item corresponding to each of these action methods:- (void)orderFrontSpacingPanel:(id)sender;There are now several means provided for users to add and manipulate links in text. Links can be created, manipulated, or removed using the link panel; they can also be dragged and dropped or copied and pasted to or from other applications. In addition, if a link is already present in the text, then control-clicking on the link will bring up a contextual menu with several options related to the link.
- (void)orderFrontLinkPanel:(id)sender;
- (void)orderFrontListPanel:(id)sender;
- (void)orderFrontTablePanel:(id)sender;
There are also several means to manipulate lists. There is a popup in the ruler that allows the selection of most basic list styles, and gives access to the list panel, which allows more general configuration of the content of list markers. The positioning of list markers and text is controlled by tab stops, which can be manipulated as usual, and the attributes of marker text may also be manipulated like those of any other text.
There are also several means to manipulate tables. The table panel can be used to add or remove rows and columns, to nest lists, and to join or split cells, as well as to manipulate border colors and sizes and background colors. Cell sizes can be manipulated directly by dragging on cell borders.
Text Import/Export (Section updated since WWDC)
The Cocoa text system can now import the following formats: plain text, RTF, RTFD, SimpleText, doc format, WordML, HTML, and WebArchive. It can now export all of these except for SimpleText. Instead of adding new methods for each new format, NSAttributedString and NSMutableAttributedString now have general-purpose methods that allow the format to be specified as a parameter. The existing methods remain as conveniences.The new methods on NSAttributedString are:
- (id)initWithURL:(NSURL *)url options:(NSDictionary *)options documentAttributes:(NSDictionary **)dict error:(NSError **)error;The first two methods are similar to the existing methods, but with the addition of an error parameter. The last two methods likewise add an error parameter, but in addition they require that the document type be specified in the document attributes dictionary. The last method returns a directory file wrapper if appropriate for the document type, otherwise a regular file wrapper.
- (id)initWithData:(NSData *)data options:(NSDictionary *)options documentAttributes:(NSDictionary **)dict error:(NSError **)error;
- (NSData *)dataFromRange:(NSRange)range documentAttributes:(NSDictionary *)dict error:(NSError **)error;
- (NSFileWrapper *)fileWrapperFromRange:(NSRange)range documentAttributes:(NSDictionary *)dict error:(NSError **)error;
The new methods on NSMutableAttributedString are:
- (BOOL)readFromURL:(NSURL *)url options:(NSDictionary *)opts documentAttributes:(NSDictionary **)dict error:(NSError **)error;which again are similar to existing methods but with the addition of an error parameter.
- (BOOL)readFromData:(NSData *)data options:(NSDictionary *)opts documentAttributes:(NSDictionary **)dict error:(NSError **)error;
There are a number of new constants used in text import/export. There is a new document type
NSString *NSWebArchiveTextDocumentType;for the WebKit WebArchive type; the actual format used is the data representation of a WebArchive.
Constants have been defined for all of the existing document attributes and options keys; the values are still specified for existing keys for backward compatibility with previous system versions. (To use them in Panther systems, use the actual string value of the identifier, where provided in the comments.) In addition, some new keys have been added (see below) and some have slightly different usages. NSCharacterEncodingDocumentAttribute is now used for both import and export of plain text documents. NSCocoaVersionDocumentAttribute still returns an NSNumber, but now it may have a floating-point value; for Tiger and later, this value will be the NSAppKitVersionNumber for the AppKit version that created the file. Previous values should be interpreted as follows: values less than 100 are pre-OS X; 100 is OS X v10.0 and 10.1; 102 is OS X v10.2 and 10.3.
Various document attributes (author, subject, etc) can now be read and written in rich text files. Keywords for this have been defined in AppKit/NSAttributedString.h:
NSString *NSTitleDocumentAttribute;TextEdit's "Document Properties" panel demonstrates the use of some of these keywords.
NSString *NSCompanyDocumentAttribute;
NSString *NSSubjectDocumentAttribute;
NSString *NSAuthorDocumentAttribute;
NSString *NSKeywordsDocumentAttribute;
NSString *NSCommentDocumentAttribute;
NSString *NSEditorDocumentAttribute;
NSString *NSCreationTimeDocumentAttribute;
NSString *NSCopyrightDocumentAttribute;
HTML Import/Export (Section updated since WWDC)
The Cocoa text system now imports and exports HTML. HTML import has been completely revised to use WebKit in all cases, and to make use of the new text table and list support. For Tiger, the @"UseWebKit" document option will no longer be relevant, and the NSTimeoutDocumentOption, NSWebPreferencesDocumentOption, and NSWebResourceLoadDelegateDocumentOption will always be available.There is now an additional document option,
NSString *NSTextSizeMultiplierDocumentOption;which specifies a scale factor for font sizes, corresponding to WebView's textSizeMultiplier, the same value that is exposed in Safari through the "Make Text Bigger/Smaller" menu and toolbar items.
The NSCharacterEncodingDocumentAttribute is now available for use in HTML export. If it is not specified, the default encoding for HTML export will be UTF-8. Characters not directly representable in the specified encoding will be represented by character references--usually numeric character references, but character entity references may be used in certain special cases. (At present some encodings may not be fully supported.)
The type of HTML generated is controlled by a new document attribute,
NSString *NSExcludedElementsDocumentAttribute;This should be an NSArray containing strings (case-insensitive) that are names of HTML elements, plus several additional values: DOCTYPE (representing a doctype declaration) and XML (representing an XML declaration), and certain Apple-specific values described below. The elements contained in this list are those that will not be used in HTML generation; the text system will make use of any other HTML elements as it sees fit. By default, if no list is specified, the excluded elements will be those that are deprecated in HTML 4--namely APPLET, BASEFONT, CENTER, DIR, FONT, ISINDEX, MENU, S, STRIKE, and U--plus XML. When XML is on the excluded list, HTML forms will be used where there is a distinction; when it is removed from the list, XHTML forms will be used.
Clients specifying a list will have considerable control over the HTML generated, for example:
- Remove U from this list, and U will be used for underlined text (and the doctype will no longer be strict).
- Remove XML from this list, and an XML declaration will be generated, and XHTML forms will be used.
- Add STYLE to this list, and styles will be inlined rather than placed in the head.
- Remove FONT from this list, and FONT tags will be generated.
- Add B, I, SUB, and SUPER to this list, and CSS will be used for all styling.
- Conversely, add SPAN to this list, and no CSS at all will be used.
- Add A to this list, and all links will be suppressed.
- Add IMG and OBJECT to this list, and all attachments will be stripped.
- Add DOCTYPE to this list, and no doctype will be generated.
- Add META to this list, and all meta tags will be suppressed.
- Add HEAD to this list, and all head information will be suppressed (and styles will be inlined).
- Add BODY to this list, and the body tag will be omitted (but the body will still be generated).
- Add DOCTYPE, HTML, HEAD, and BODY to this list, and the result will be a bare HTML snippet (with styles inlined).
- Add DOCTYPE and every HTML tag except B and I, and the result will be a bare HTML snippet using only B and I.
The doctype generated depends on the list of elements. If XML is on the excluded list, then an HTML doctype will be used; otherwise an XHTML will be used. If any of the HTML 4 deprecated elements is not on the list, or if P is on the list, then a transitional doctype will be used; otherwise a strict doctype will be used. However, in all cases a doctype will be used only if the generated HTML actually conforms to it (for example, all of the relevant doctypes require a TITLE).
There are two Apple-specific values that may be added to the excluded elements array: Apple-converted-space and Apple-converted-tab. If these are absent from the array, then HTML export will add SPAN elements with these class names as a mechanism to preserve certain types of whitespace (tabs and multiple spaces) that otherwise have no faithful representation in HTML. If these are present in the array, then the generated HTML will not contain these somewhat unwieldy SPAN elements. There is a also a third recognized value, Apple-interchange-newline, but its use is not currently enabled. In the case of TextEdit, the presence of these values is controlled by the "Preserve white space" checkbox in the HTML portion of the open and save preferences pane.
The format of the HTML generated is also controlled by a new document attribute,
NSString *NSPrefixSpacesDocumentAttribute;This should be an NSNumber containing an integer (default 0) representing the number of spaces per level by which certain nested HTML elements (notably elements involved in representing lists and tables) should be indented from the start of the line. This automatic indentation makes the resulting HTML more readable.
RTF
Handling of \expnd was fixed to treat the argument as quarter-points rather than half-points. (Note that in practice this bug didn't have any impact as we always follow \expnd with \expndtw, which correctly specifies the value out in twips.)RTF files can now handle additional fractional sizes. (RTF spec allows only half-point precision.)
"No kerning" is now preserved in RTF files; it was not being written out correctly.
Command-Line Tool (Section added since WWDC)
There is now a tool, /usr/bin/textutil, that allows command-line access to the import/export facilities of the Cocoa text system. It can be used for inspecting properties of files, for altering attributes, and for conversion between any of the types of files that the text system supports. Examples might include: determining the format and metadata attributes of a file; adding or changing metadata such as author or title; converting between HTML and RTF; extracting the plain text from rich text documents; or converting plain text from one encoding to another. For more information, see "man 1 textutil".NSFontPanel modes
We added the following modes to NSFontPanel to allow disabling font effects using validModesForFontPanel. However these are actually not yet implemented:NSFontPanelUnderlineEffectModeMask = 1<<8,
NSFontPanelStrikethroughEffectModeMask = 1<<9,
NSFontPanelTextColorEffectModeMask = 1<<10,
NSFontPanelDocumentColorEffectModeMask = 1<<11,
NSFontPanelShadowEffectModeMask = 1<<12,
NSFontPanelAllEffectsModeMask = 0XFFF00
Font Effects Color Changes (Section added since WWDC)
In Panther, the font effects portion of the font panel used a private mechanism to handle color changes (foreground color, underline color, strikethrough color, and document background color). In Tiger, it uses the standard changeAttributes:/convertAttributes: mechanism for foreground color, underline color, and strikethrough color, and the standard changeDocumentBackgroundColor: method for document background color. This makes it easier for custom views to respond to these color changes. The previous private mechanism will still be supported, for those views that may have implemented it based on the Panther implementation, but changeAttributes:/convertAttributes: is recommended for future use.NSLayoutManager Screen Font Changes (Section added since WWDC)
In Panther, a layout manager would not use screen fonts if any text view associated with it was scaled or rotated, regardless of the setting of usesScreenFonts. In Tiger, a layout manager will use screen fonts if usesScreenFonts is set, regardless of the state of text views associated with it. The previous behavior remains in effect for applications linked on Panther or previous. Applications linked on Tiger or beyond will have to manually call setUsesScreenFonts: on their layout managers if they wish the use of screen fonts to change.NSLayoutManager Glyph Generator
NSLayoutManager now has methods for directly setting and getting its glyph generator:- (NSGlyphGenerator *)glyphGenerator;
- (void)setGlyphGenerator:(NSGlyphGenerator *)glyphGenerator;
NSSpellChecker Language Argument (Section added since WWDC)
Documentation has stated that the language argument to- (NSRange)checkSpellingOfString:(NSString *)stringToCheck startingAt:(int)startingOffsetshould be the empty string to specify that the user's currently selected spellchecker should be used. Actually, nil should be used instead; using the empty string would give inconsistent results on pre-Tiger system versions. For Tiger, the empty string will be handled as nil, but nil is still preferred. The same holds for the similar methods
language:(NSString *)language wrap:(BOOL)wrapFlag inSpellDocumentWithTag:(int)tag wordCount:(int *)wordCount;
- (int)countWordsInString:(NSString *)stringToCount language:(NSString *)language;
- (NSArray *)completionsForPartialWordRange:(NSRange)range inString:(NSString *)string
language:(NSString *)language inSpellDocumentWithTag:(int)tag;
NSFileWrapper Changes (Section added since WWDC)
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)atomicFlag updateFilenames:(BOOL)updateFilenamesFlag;This method was changed to be more efficient when overwriting existing directories. The change in behavior is that it tries to avoid creating new copies of any files which haven't changed.
NSToolbar (Section updated since WWDC)
NSToolbarItem menuFormRepresentation are now only validated when the toolbar is in label only mode. This is a performance win since these menuFormRepresentations are only used when the toolbar is in label only mode.A bug was discovered in Panther that affected layout of flexibly sized "view" items when in "Icon & Text" mode. In this case, the view portion of a toolbar item was much smaller than the space which was available to it. In essence, these items looked like they had too much empty space on the right and left side. They looked much farther away from adjacent items than desired (besides the fact that the view portion was smaller than it should be). In Panther, the view portion should never have excess spacing on the right and left unless the extent of the toolbar item and its label is larger than the item's maximum specified view size.
NSToolbar now indicates the selection status of items even if they are in the overflow menu. Prior to Tiger, items in the overflow menu did not indicate selection status. Selected items are indicated using the standard menu check mark.
NSToolbarItem's with valid actions and nil targets now work much better. Normally the receiver of a nil-targeted action is found by traversing the responder chain to find a responder that implements the action method. The traversal starts with the 'firstResponder' in the keyWindow. For applications linked on Tiger or later, toolbar item's now start their traversal with the firstResponder of toolbar item's window even if it is not key.
To understand why the old behavior is a problem, imagine the following: You have a nil-targeted "save" item whose action is saveContentsOfWindow:. There are 2 windows "A" and "B" are open with A being the key window. Each window implements saveContentsOfWindow:. Prior to Tiger, validation of window B's "save" item, would always go through the A's validation code. Further, if you cmd-clicked on B's "save" item (ie. perform the click without changing the key window), the action would be executed by A.
NSToolbar validates its toolbar items once before they are shown so that it is animating with up-to-date state.
A NSToolbarItem's menuFormRepresentation that is a submenu can now have an target/action associated with the top-level item. Such items behave like pulldown menu's whose top level item is clickable like a button. If the user mouses up before the pulldown is shown, the top-level item's action is sent. The pulldown menu is shown after a short delay, or if the user moves the mouse. The action methods 'sender' argument will be the top-level menu item. This new behavior is enabled for applications linked on Tiger and later. .
The resizing behavior of NSToolbarFlexibleSpaceItemIdentifier items has changed slightly. The purpose of a flexible spacer is two-fold. First it is used by users that want to right align certain items. Second, it is use to create visual separation. Flexible spacer items used to share an equal amount of the available space with other resizable items. Unfortunately, this steals valuable space from the items with actual content. To address this, flexible spacers no longer resize with an equal weighting.
NSToolbar's layout of resizable items has been improved. For applications linked on Panther or earlier, there is a bug in the layout of resizable items in icon and label mode. Often items that should have been the same size were different in size. In particular, if two items have the same minSize, when they are both wider than their label, yet smaller then their maxSize, they should be the same size. This bug has been fixed for applications linked on or after Tiger.
Prior to Tiger, changing the label of a resizable item could sometimes cause the item to resize on the screen when it was unnecessary to resize. This bug has been fixed for applications linked on or after Tiger.
Prior to Tiger, changing the label of a resizable item could sometimes cause the item to resize on the screen when it was unnecessary to resize. This bug has been fixed for applications linked on or after Tiger.
Layout changes - NSToolbar's margins have changed slightly.
Margins in aqua windows: The inset of the first and last item have changed, but the sum of those two values is the same as in Panther. However, the inter item spacing has changed by 1 pixel per item. This results in a bit more space for the layout algorithm, resulting in slightly larger flexibly sized items.
Margins in metal windows: The layout has the same changes that the aqua windows have, plus a small vertical margin change. Metal windows are now a vertically shorter, with margins that now match Safari and Finder. As a result, metal windows with toolbars will be a few pixels shorter for the same content size.
Drawing the baseline separator: For applications that want their toolbar to look more like the Finder and Safari, NSToolbar allows you to hide the baseline it draws between itself and the main window contents. Developers can also use this API to hide the base line so that they can draw their own custom looking separator. To turn on/off baseline drawing, call the following methods before you attach your toolbar to its window:
- (void)setShowsBaselineSeparator:(BOOL)flag;
- (BOOL)showsBaselineSeparator;
NSToolbarItem
AutovalidationNSToolbar relies on window update to drive its auto-validation mechanism to enable/disable NSToolbarItems. Unfortunately, window update may not happen at the exact moment a developer needs to update their UI. Also, window update happens very frequently, and can cause performance problems for validators that need to do a lot of work. Therefore, we now provide a way to turn off the default auto validation on a per-item basis.
/* By default NSToolbar automatically invokes its items validate method on a regular basis.Avoiding Overflow
To be in complete control of when the -validate method is invoked, you can disable automatic validation
on a per-item basis. In particular, if your validation code is slow, you may want to do this for performance reasons.
*/
- (void)setAutovalidates:(BOOL)autovalidates;
- (BOOL)autovalidates;
Some applications would like to suggest that certain toolbar items always be visible, never falling into the overflow menu. For instance, in Safari, the URL field is very important, and should generally always be visible (and not in the overflow menu). Users may find it very useful to define this sort of thing as well. For instance, In Xcode, I might want to suggest that the "build styles" popup always be visible. To accomplish this, we added the following API.
enum {
// The default visibility priority value. By default, all items have this priority
NSToolbarItemVisibilityPriorityStandard = 0,
// A good value to use for items which should be first to fall into the overflow menu
NSToolbarItemVisibilityPriorityLow = -1000,
// A good value to use for items you want to stay visible, allowing users to still have highest priority
NSToolbarItemVisibilityPriorityHigh = 1000,
// Value assigned to an item the user wants to "keep visible". You should only use values less than this
NSToolbarItemVisibilityPriorityUser = 2000
};
/* When a toolbar does not have enough space to fit all its items, it must push some into the overflow menu.A cautionary note: The toolbar is never meant to be the only way of doing a particular function. This API should not be used as a crutch. In fact, since users can override your suggestion of what should be visible, you are still never guaranteed that an item will always be visible.
Items with the highest visibility priority level are chosen last for the overflow menu. The default
visibilityPriority value is NSToolbarItemVisibilityPriorityStandard. To suggest that an item always
remain visible, give it a value greater than NSToolbarItemVisibilityPriorityStandard, but less than
NSToolbarItemVisibilityPriorityUser. In configurable toolbars, users can control the setting of any item,
and the value is rememeber by NSToolbar along with its other autosaved information. You should allow
user setting to have the highest priority.
*/
- (void)setVisibilityPriority:(int)visibilityPriority;
- (int)visibilityPriority;
NSStatusItem
You can now get and set a separate double click action on an NSStatusItem. This action is called for any even with a clickCount of greater than 1.- (SEL)doubleAction;
- (void)setDoubleAction:(SEL)aSelector;
NSMenu
A new method has been added to NSMenu that returns the menu bar height for the main menu. This method supersedes +[NSMenuView menuBarHeight]. The method returns the menu bar height if the menu is the application's current main menu and 0 otherwise.- (float)menuBarHeight;
Dock Menu
In Tiger, we added support for alternate menu items in dock menus. We also added support to invoke the NSMenu delegate methods if a menu delegate is set.NSSegmentedCell/NSSegmentedControl
A new method has been added to NSSegmentedCell and NSSegmentedControl that selects the cell given the tag. It will search for the item with the tag and then call -[NSSegmentedCell setSelectedSegment]. The function will return YES if the segment is found even if the cell isn't selectable because it's disabled. The control method calls the cell method.- (BOOL)selectSegmentWithTag:(int)tag;
NSPopUpButtonCell/NSPopUpButton
A new method has been added to NSPopUpButtonCell and NSPopUpButton that selects an item given the tag. It will search for the item with the tag and then call -[NSPopUpButtonCell selectItemAtIndex]. The method will return YES if the item is found. If the item isn't found, the function returns NO and does nothing. The control method calls the cell method.- (BOOL)selectItemWithTag:(int)tag;
NSFormCell
Placeholder string methods in NSTextFieldCell are also available in NSFormCell.- (void)setPlaceholderString:(NSString *)string;
- (NSString *)placeholderString;
- (void)setPlaceholderAttributedString:(NSAttributedString *)string;
- (NSAttributedString *)placeholderAttributedString;
NSButtonCell (Section updated since WWDC)
You can now set the background colour of a borderless button. This does not affect the color of bordered buttons such as push buttons.- (void)setBackgroundColor:(NSColor *)color;The drawing of the button background, image, and text has been broken out into 3 separate public methods that give a subclass an override point.
- (NSColor *)backgroundColor;
- (void)drawImage:(NSImage *)image withFrame:(NSRect)frame inView:(NSView *)controlView;These methods are called if the button has the appropriate setting. -drawBezelWithFrame:inView: is called if the button is bordered. -drawImage:withFrame:inView: and -drawTitle:withFrame:inView: will be called if the button has an image and title respectively. -drawTitle:withFrame:inView: will return the actual rectangle used to render the text (usually centered in the frame passed in.)
- (NSRect)drawTitle:(NSAttributedString *)title withFrame:(NSRect)frame inView:(NSView *)controlView;
- (void)drawBezelWithFrame:(NSRect)frame inView:(NSView *)controlView;
There are several new bezel styles available:
NSSmallSquareBezelStyle = 10The first is a simple square bezel that appears in the accounts preference pane in System Preferences (the '+' and '-' buttons). NSTexturedRoundedBezelStyle is used to create a button similar to the Finder's action (gear) button. It is similar in appearance to a single item segmented control.
NSTexturedRoundedBezelStyle = 11
Both have regular, pressed, and disabled appearances. The controlSize setting does not apply. NSSmallSquareBezelStyle can scale to any size and NSTexturedRoundedBezelStyle has only a single size and a fixed height.
NSRoundRectBezelStyle = 12This is a button with semicircular end caps. It has a gray shading from light to dark. It has regular, pressed, and disabled appearances. You should set the font to be Lucida Grande 12 point.
NSRecessedBezelStyle = 13This is a button with semicircular end caps. It appears to have a recessed appearance. It has regular, pressed, selected, and disabled appearances. You should set the font to be Lucida Grande 12 point. This button works when you set -setShowsBorderOnlyWhileMouseInside to YES.
NSRoundedDisclosureBezelStyle = 14This button is a square button that is the same as the disclosure button in the save panel. It has regular and selected states. It should not have text or an image set.
NSSearchFieldCell
A flag is available that will cause the search field cell's action to be sent without delay instead of grouping the changes together until the user pauses typing.- (BOOL)sendsSearchStringImmediately;
- (void)setSendsSearchStringImmediately:(BOOL)flag;
NSCell
The method -setControlView: is now available in NSCell and its subclasses. The implementation in NSCell does nothing. The implementation in NSActionCell saves the view.You can now set the cell (usually an NSTextFieldCell) to allow or disallow undo. By default, the field is set to allow undo. The undo stack is cleared when the cell begins editing.
- (void)setAllowsUndo:(BOOL)allowsUndo;You can set the line break mode of a cell for text drawing. This will allow truncation of text in the cell without needing to subclass. Calling -[NSCell setWraps:] will set the mode to NSLineBreakByWordWrapping and NSLineBreakByClipping for YES and NO respectively. -setWraps will return YES if the mode is NSLineBreakByWordWrapping or NSLineBreakByCharWrapping.
- (BOOL)allowsUndo;
- (void)setLineBreakMode:(NSLineBreakMode)mode
- (NSLineBreakMode)lineBreakMode
NSBox (Section added since WWDC)
In Tiger, we added an optimization to draw the background of an NSBox opaquely, if the NSBox is a top level view in the window and fits other criteria. Since we have discovered some applications whose nib layout involves views which overlap the NSBox rather than being completely contained, we have enabled this optimization only for applications built on Tiger or later.NSComboBox
Calling -[NSComboBox setUsesDataSource:] now always sets indexOfSelectedItem to -1. Prior to Tiger it didn't do this for setUsesDataSource:NO.copyWithZone: no longer loses font information in the table. Previously, copying a combo box cell would always result in a table using the default font size, even if the combo box cell was using something different like the smallSystemFont.
NSComboBox's popup menu now only shows the scroll bar if it is necessary.
NSComboBox popup window positioning has changed a bit. If the popup can not fit below the text field, NSComboBox now places the combo box above the text field only if there is more space above the combo box than there is below. In the past, NSComboBox didn't pay attention to which side had more space. Further, if there isn't enough room on either side, NSComboBox now shrinks the window to fit on screen.
NSComboBox has fixed a bug in -indexOfSelectedItems which caused the index to be incorrect when used from the NSComboBox action in certain situations.
NSComboBoxCell can now be used inside of an NSMatrix. To function properly, the matrix should use NSTrackModeMatrix for its -mode.
NSBrowser (Section updated since WWDC)
Calling setTitle:ofColumn: during column creation now works. Prior to Tiger, calling it was not possible to set the title of the column being created using this API from either of the delegate "creation" methods: -browser:numberOfRowsInColumn: or -browser:createRowsForColumn:inMatrix:.The titleFrame passed into drawTitleOfColumn:inRect: is now 2 pixels bigger on each side. In the past, NSBrowser used to inset value returned by titleFrameOfColumn: before calling drawTitleOfColumn:inRect:. This was the wrong thing to do, and also led to certain descenders (like "g", and "j") being clipped at the bottom. Note that the baseline of the text will not move. The new behavior applies unless you are using a subclass of NSBrowser linked before Tiger. For applications linked on or after Tiger, NSBrowserCell now defaults to using NSLineBreakByTruncatingTail for its line break mode. Set -[NSCell setLineBreakMode:] for more information on line break modes.
NSMatrix
When dirtying a cells contents, NSMatrix now pays attention to the cells -focusRingType. In the past, NSMatrix always assumed a -focusRingType of NSFocusRingTypeExterior, causing it to use [self setKeyboardFocusRingNeedsDisplayInRect:cellFrame] always, instead of just for cells that support focus rings.NSComboBoxCell can now be used inside of an NSMatrix. To function properly, the matrix should use NSTrackModeMatrix for its -mode.
NSOutlineView
-(BOOL)isItemExpanded:(id)item; now returns NO if 'item' is not found.NSOutlineView now returns valid results from rowForItem:, and levelForItem: when called with children of an item in progress of expanding. While a particular child item is being loaded, these methods will return valid results. Previously they returned -1 while an item was in the process of being loaded by a parent (for instance during an item expand). There is no reason this particular type of information should not be valid during load time, so it is now available.
NSOutlineView's data loading is much lazier now. Data is now requested from the data source on demand. In general, the data source will not be queried for an items data unless that item is visible, or an API is invoked which requires that data to be present.
The following methods, indicated as deprecated since before 10.0, have been removed: -(void)setAutoResizesOutlineColumn: (BOOL)resize; and - (BOOL)autoResizesOutlineColumn; If you are using these methods, switch to using: -(void)setAutoresizesOutlineColumn: (BOOL)resize; and - (BOOL)autoresizesOutlineColumn;
NSOutlineView's outline column now respects the minWidth specified in IB. In the past, NSOutlineView forced the minWidth to the a value large enough to always show the expansion cells, even if that was smaller than the minWidth you specified. This change takes effect for apps linked on or after Tiger.
Drag and Drop
For applications built on Tiger or later, NSOutlineView now allows row dragging to begin with a click in any column. Previously, dragging out was limited to the outline column. Applications built on Tiger and later, can keep the old behavior by simply overriding -(BOOL)canDragRowsWithIndexes:(NSIndexSet *)rowIndexes atPoint:(NSPoint)aPoint; to return YES only when 'aPoint' is in the outline column.
For more drag and drop changes, see NSTableView release notes.
ToolTips
For many reasons, it is difficult to add cell-level toolTips to an NSOutlineView using existing NSView API. To solve this, we are adding new API which will let your delegate supply the tooltip for a cell (column/row intersection) on demand. Note that there are no tooltip tracking tags associated with these tooltips. To take advantage of this new feature, you need to implement the delegate method:
/* When the user pauses over a cell, the value returned from this method will be displayed in a tooltip.
'point' represents the current mouse location in view coordinates. If you don't want a tooltip
at that location, return nil. On entry, 'rect' represents the proposed active
area of the tooltip. By default, rect is computed as [cell drawingRectForBounds:cellFrame].
To control the default active area, you can modify the 'rect' parameter.
*/
- (NSString *)outlineView:(NSOutlineView *)ov toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect
tableColumn:(NSTableColumn *)tc item:(id)item mouseLocation:(NSPoint)mouseLocation;
NSTableView
MiscFor applications linked on or after Tiger, NSTableView invalidates the table and header view only when the frame size or column sizes has changed. In the past, calls to -tile unnecessarily invalidated the entire table and header view.
For applications linked on or after Tiger, NSTableView's (and NSOutlineView) default data cell now defaults to using NSLineBreakByTruncatingTail for its line break mode. Set -[NSCell setLineBreakMode:] for more information on line break modes.
Deselecting the edited row now ends editing. Due to a bug, in Panther and earlier systems, deselecting sometimes ended editing and sometimes did not. This has been fixed.
Double clicking now only starts editing if the click was in the cellFrame area. Prior to Tiger, double clicking anywhere in the column/row rect started editing. In particular, this had an odd effect in NSOutlineView. So now, double clicking on the left side of the outline disclosure button no longer starts cell editing.
Menu validation has changed slightly. deselectAll: is now disabled if there is no selection in the table. selectAll: is now disabled if the table disallows multiple selections.
Changing the table background color, and grid color now cause a redisplay if needed.
NSTableView is now much more aggressive about making sure the dataCell and headerCell's controlView ivar is set. For example, in Panther and earlier systems, calling [self controlView] would normally return nil for a cell in a table. For applications linked on or after Tiger, this and other related problems have been fixed. In general, NSTableView tries to make sure calling -controlView will return a reference to itself for the cells it uses.
For applications linked on or after Tiger, removing a column from an NSTableView now results in the column's tableView reference to be set to nil.
The attributes of table column's default data cells have changed. In its -init method NSTableColumn now creates a default NSTextFieldCell data cell with the following changed attributes: setDrawsBackground=YES; font=systemFont with size systemFontSize (Lucida Grande, 13). Prior to Tiger, NSTableColumn used to set the default properties to setDrawsBackground=NO, and font=(Lucida Grande, 12). The difference used to present many problems. For instance, if you added columns to your table in code, they would have different font sizes than those from the archived nib. Further, if you chose to display the popular alternating row colors, those data cells whose setDrawsBackground was YES would obscure the blue on every other line. Finally, in most tables it is unnecessary for the cells to draw the background because the table is already drawing the background. The new attributes are only set for applications linked on Tiger and later, and only in NSTableColumn's init method. So, if you have archived nibs with the wrong font size, you should fix them by hand in IB or in code.
Also, in IB it was possible to create new columns that had different properties than the default columns you get when dragging a table from the palette.
Deprecated API
Wherever possible, NSTableView now uses NSIndexSet in its APIs. The following methods have been deprecated:
- (NSImage *)dragImageForRows:(NSArray *)dragRows event:(NSEvent *)dragEvent dragImageOffset:(NSPointPointer)dragImageOffset;... and replaced with NSIndexSet based versions:
- (BOOL)tableView:(NSTableView *)tv writeRows:(NSArray *)rows toPasteboard:(NSPasteboard *)pboard;
- (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows tableColumns:(NSArray *)tableColumnsAccessibility Fixes
event:(NSEvent *)dragEvent offset:(NSPointPointer)dragImageOffset;
- (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard;
Selecting a table row using accesibility APIs now properly consults NSTableView's shouldSelectRow: delegate API.
NSTableView now sets up more NSCell state before calling accessibilityPerformAction: when the action is NSAccessibilityPressAction. In particular, the cells controlView is set to the table, and the table's clickedRow and clickedColumn reflect the row and column of the cell receiving the NSAccessibilityPressAction. Finally, the selectedCell of the table will return the cell handling the NSAccessibilityPressAction. The idea is that NSAccessibilityPressAction should be able to be handled the same as if the user had clicked on the cell with the mouse.
Drag and Drop (Section updated since WWDC)
NSTableView has added API that lets you control where row dragging operations can begin. By default NSTableView allows drags to begin anywhere with a few exceptions: Clicks on a buttons, popup buttons, sliders and other similar controls will track the mouse instead of beginning a row drag. To customize this behavior, override the following method:
/* The return value indicates whether the table can attempt to initiate a row drag at 'mouseDownPoint'.NSTable/OutlineViews that are drag destinations now revalidate drags (validateDrop: API) if the modifier keys change.
Return NO to disallow initiating a row drag at the given location.
*/
- (BOOL)canDragRowsWithIndexes:(NSIndexSet *)rowIndexes atPoint:(NSPoint)mouseDownPoint;
You can now customize NSTableView's (and NSOutlineView) implementation of -draggingSourceOperationMaskForLocal without subclassing. NSTableView implements the dragging source method -draggingSourceOperationMaskForLocal: for you. By default it disallows dragging to destinations outside its application while allowing any type of drag within its application. There are two options for customizing this behavior. Developers can always subclass and override this method when decisions need to be dynamic. However, In Tiger, we have added an easier way to customize the behavior. Simply use the following new method:
/* Configures the default value returned from -draggingSourceOperationMaskForLocal:.Drag and Drop - File Promise Dragging
An isLocal value of YES indicates that 'mask' applies when the destination object is in the same application.
An isLocal value of NO indicates that 'mask' applies when the destination object in an application outside
the receiver's application. NSTableView will archive the values you set.
*/
- (void)setDraggingSourceOperationMask:(unsigned int)mask forLocal:(BOOL)isLocal;
NSTableView supports file promised drags via the new pasteboard type NSFilesPromisePboardType. To support file promise drags, clients simply add this type to the pasteboard in tableView:writeRowsWithIndexes:toPasteboard:. When a destination accepts the promise drag, it asks NSTableView to supply the files via -namesOfPromisedFilesDroppedAtDestination:. NSTableView passes the responsibility of the actual file creation to data source that specified the promise drag. It will be sent the following new data source method:
/* Returns an array of filenames for the created files (filenames only, not full paths). The URL representsNSOutlineView has its own version:
the drop location. For more information on file promise dragging, see documentation on the
NSDraggingSource protocol and -namesOfPromisedFilesDroppedAtDestination:.
*/
- (NSArray *)tableView:(NSTableView *)tableView namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
forDraggedRowsWithIndexes:(NSIndexSet *)indexSet;
- (NSArray *)outlineView:(NSOutlineView *)outlineViewToolTips
namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
forDraggedItems:(NSArray *)items;
For many reasons, it is difficult to add cell-level toolTips to an NSOutlineView using existing NSView API. To solve this, we are adding new API which will let your delegate supply the tooltip for a cell (column/row intersection) on demand. Note that there are no tooltip tracking tags associated with these tooltips. To take advantage of this new feature, you need to implement the delegate method:
/* When the user pauses over a cell, the value returned from this method will be displayed in a tooltip.
'point' represents the current mouse location in view coordinates. If you don't want a tooltip at that location, return nil.
On entry, 'rect' represents the proposed active area of the tooltip. By default, rect is computed as
[cell drawingRectForBounds:cellFrame]. To control the default active area, you can modify the 'rect' parameter.
*/
- (NSString *)tableView:(NSTableView *)tv toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect
tableColumn:(NSTableColumn *)tc row:(int)row mouseLocation:(NSPoint)mouseLocation;
Variable Row Heights
Until now, every row in a table view was required to be the same height as every other row. This is appropriate for the vast majority of table view's uses. However, in some cases, it is limiting. Sometimes it can be beneficial to specify the height of a row based on the data that row needs to display. In Tiger, we will allow table view rows to be specified per row if necessary with the following API:
/* If the delegate implements -tableView:heightOfRow:, this method immediately re-tiles the table view using row heights it provides.
*/
- (void)noteHeightOfRowsWithIndexesChanged:(NSIndexSet *)indexSet;
@interface NSObject (NSTableViewDelegate)
/* Optional - Variable Row Heights
Implement this method to support a table with varying row heights. The height returned by this method should not include
intercell spacing and must be >0. Performance Considerations: For large tables in particular, you should make sure that this
method is efficient. NSTableView may cache the values this method returns. So if you would like to change a row's height make
sure to invalidate the row height by calling -noteHeightOfRowsWithIndexesChanged:. NSTableView automatically invalidates its
entire row height cache in -reloadData, and -noteNumberOfRowsChanged.
*/
- (float)tableView:(NSTableView *)tableView heightOfRow:(int)row;
@end
@interface NSObject (NSOutlineViewDelegate)
/* Optional - Variable Row Heights
Implement this method to support an outline view with varying row heights. The height returned by this method should not include
intercell spacing and must be >0. Performance Considerations: For large tables in particular, you should make sure that this
method is efficient. NSTableView may cache the values this method returns. So if you would like to change a row's height make
sure to invalidate the row height by calling -noteHeightOfRowsWithIndexesChanged:. NSTableView automatically invalidates its
entire row height cache in -reloadData, and -noteNumberOfRowsChanged.
*/
- (float)outlineView:(NSOutlineView *)outlineView heightOfRowByItem:(id)item;
@endSome notes about variable row height table view's:
- The lineScroll of the table's scroll view is controlled by -rowHeight.
- The height of "filler" rows is controlled by -rowHeight. Ie. The distance between grid lines, and alternating row colors past the last table row.
Table Column Resizing - Better algorithm / New APIs
NSTableView currently supports two column autoresizing modes: "resize all" and "resize last".
We are changing behavior of the current "resize all" mode. Its behavior is simplistic and not useful. Currently, as the table resizes, it sets the width of each resizable column to (availableWidth / numResizableColumns), contraining to each min and max. This distribution algorithm doesn't take into account the current relative sizes of each column. This behavior will be replaced with an algorithm that uniformly distributes space while taking into account initial values. The current "resize last" resizes the last resizable column until it reaches its min or max width. This behavior will be retained.
In addition to the existing modes, we will be adding a few more column autoresizing modes to NSTableView. In particular, we are adding a mode which allow developers to turn off column autoresizing. This will be useful for those that want to implement their own column autoresizing algorithm.
Note: "autoresizing" refers to column resizing in response to a frame change. Think live resize. "User resizing" refers to resizing by dragging from the right edge of a column header.
Also note, NSTableView's -sizeToFit, and -sizeLastColumnToFit behavior has been fixed as part of this work to use the new styles and algorithms. During a live resizing operation, these methods use the specific resizing style you have choosen via -setColumnAutoresizingStyle:. When called outside a live resize operation, -sizeToFit resizes columns to fit the visible width using NSTableViewUniformColumnAutoresizingStyle, while -sizeLastColumnToFit resizes columns using NSTableViewLastColumnOnlyAutoresizingStyle.
/* The column auto resizing style controls resizing in response to a table view frame change.
Compatability Note: This method replaces -setAutoresizesAllColumnsToFit:.
*/
typedef enum {
// Turn of column autoresizing
NSTableViewNoColumnAutoresizing = 0,
// Autoresize all columns by distributing equal shares of space simultaeously
NSTableViewUniformColumnAutoresizingStyle,
// Autoresize each table column one at a time.
// Proceed to the next column when the current column can no longer be autoresized (when it reaches maximum/minimum size).
NSTableViewSequentialColumnAutoresizingStyle, // Start with the last autoresizable column, proceed to the first.
NSTableViewReverseSequentialColumnAutoresizingStyle, // Start with the first autoresizable column, proceed to the last.
// Autoresize only one table column one at a time.
// When that table column can no longer be resized, stop autoresizing.
// Normally you should use one of the Sequential autoresizing modes instead.
NSTableViewLastColumnOnlyAutoresizingStyle,
NSTableViewFirstColumnOnlyAutoresizingStyle
} NSTableViewColumnAutoresizingStyle;
- (void)setColumnAutoresizingStyle:(NSTableViewColumnAutoresizingStyle)style;
- (NSTableViewColumnAutoresizingStyle)columnAutoresizingStyle;
/* Deprecated in Mac OS 10.4. You should use setColumnAutoresizingStyle: instead.
To preserve compatibility, if flag is YES, This method calls setColumnAutoresizingStyle:NSTableViewUniformColumnAutoresizingStyle.
If flag is NO, this method calls setColumnAutoresizingStyle:NSTableViewLastColumnOnlyAutoresizingStyle.
*/
- (void)setAutoresizesAllColumnsToFit:(BOOL)flag;
- (BOOL)autoresizesAllColumnsToFit;
NSTableColumn's resizing characteristics now provide finer controls. We have added API which allows you to declare that the user can resize a column, but that it should not resize during live resize (autoresizing).
/* The resizing mask controls the resizability of a table column. Compatability Note: This method replaces setResizable.
*/
- (void)setResizingMask:(unsigned)resizingMask;
- (unsigned)resizingMask;
enum {
NSTableColumnNoResizing = 0, // Disallow any kind of resizing.
NSTableColumnAutoresizingMask = ( 1 << 0 ), // This column can be resized as the table is resized.
NSTableColumnUserResizingMask = ( 1 << 1 ), // The user can resize this column manually.
};
/* Deprecated in Mac OS 10.4. If flag is YES, calls
setResizingMask:(NSTableColumnUserResizingMask | NSTableColumnAutoresizingMask).
If flag is NO, calls setResizingMask:(NSTableColumnNoResizing).
*/
- (void)setResizable:(BOOL)flag;
- (BOOL)isResizable;
Bindings and Binding Options (Section updated since WWDC)
The names of all the bindings and options used in AppKit are now declared through explicit constants in NSKeyValueBinding.h.To debug problems with key-value coding and key-value observing related to bindings, you can now set a user default NSBindingDebugLogLevel to get more useful logs about the problems (instead of undefined key exceptions, for example). This may be useful when you try to find a misconfigured binding in a large nib file with many bindings. Set the default value to 1 to turn on logging, and to 0 to turn it off.
A new method for returning information about the current bindings of an object has been added to NSObject:
- (NSDictionary *)infoForBinding:(NSString *)binding;It returns a dictionary with information about an existing binding or nil if it is not bound. See NSKeyValueBinding.h for more details.
Several new bindings are available:
- NSTextView now has an attributedString binding (available if multiple fonts are allowed or not).
- NSView and subclasses now have a toolTip binding.
- NSWindow now has displayPatternTitle bindings to specify the window title through a pattern string with multiple values (and work the same way as the displayPatternValue bindings on text fields).
- NSBox now has a title and displayPatternTitle bindings (that work like the bindings on NSWindow).
- NSSearchField now has a predicate binding (and if bound exposes additional predicate2, predicate3, etc. bindings) - you specify a display name and a prediate format string through the binding's options and typically bind them to the filterPreciate of an array controller.
- NSTableView now has doubleClickArgument/doubleClickTarget bindings that work the same way as the argument/target bindings of buttons - the bindings are used to trigger a method invocation on a double-click in the table view.
- Selection widgets (NSPopUpButton/NSPopUpButtonCell and NSMatrix) now offer a contentObjects binding in addition to the content and contentValues bindings (the contentObjects binding becomes available only if content is bound). This allows you to bind the content of the widget to an array (content binding), the displayed values to a dependent array (contentValues binding - which needs to use a key path that is an extension to the one of the content binding), and the "represented" objects to be handled through the selectedObject/selectedObjects bindings to another depdendent array (contentObjects - which also needs to use a key path that is an extension to the one of the content binding). For example, if you have an array with dictionaries (that can be bound to a controller through the "selection.dictionaries" key) which each have values for a key "displayName" and a key "representedObject", you can bind content of a pop-up button to "selection.dictionaries", contentValues to "selection.dictionaries.displayName" and contentObjects to "selection.dictionaries.representedObject" - the selectedObject will then operate on the "representedObject" values, while the pop-up displays the "displayName" values in the user interface. Of course, if you do not use the contentObjects binding, the represented objects are still the values in the array to which content is bound.
An option for the value bindings on table columns has been added:
NSCreatesSortDescriptorBindingOptionThis option can be used to suppress the creation of sort descriptors on a column (binding) basis.
An option to force bindings to handle errors with alert panels instead of sheets has been added to varioius bindings:
NSAlwaysPresentsApplicationModalAlertsBindingOptionA variety of bugs have been fixed, some of the fixes caused a slight change in the behavior of bindings/controllers:
- For bindings with immediate validation turned on, the user interface now correctly reflects values immediately when coerced in the validateValue:forKey: method.
- Bindings on table columns with pop-up data cells and content, contentValues and selectedObject bindings now generate sort descriptors for the selected objects based on the display key (as determined from the contentValues binding).
- Unless explicitly specified, table view content, selectionIndexes and sortDescriptor bindings are automatically generated, derived from the common cases of bindings of table columns. These automatic table view bindings are now created by invoking the public -bind:toObject:withKeyPath:options: on the table view, so that subclasses can intercept the calls if needed.
- In the Panther implementation, when removing objects in the background from the array to which an NSArrayController content was bound (not removing through the controller), an index-out-of-bounds exception was sometimes raised. This situation has been fixed for Tiger.
- In Panther, copying the arrangedObjects array of an NSArrayController copied the items in the array as well. This situation has been fixed for Tiger, now the content objects are not copied any more.
- In Panther, collection values (NSArrays and NSDictionaries) read from an NSUserDefaultsController were returned as immutable collections, making it impossible to add or remove from them directly. The workaround was to use a value transformer to make mutable copies of the collections on read. For Tiger, NSUserDefaultsController now returns collections as mutable instances, which makes the use of a special value transformer obsolete (but you still usually need to turn on the NSHandlesContentAsCompoundValueBindingOption option if you bind the content of an NSArrayController or NSObjectController to a value provided through an NSUserDefaultsController).
- Interface Builder now correctly calls -exposedBindings to populate the Bindings inspector.
NSEditor, bindings error presentation as sheets (Section added since WWDC)
So that support for bindings in Cocoa's own views can present error alerts as sheets when appropriate, a new method has been added to key-value binding's NSObject (NSEditor) category:- (void)commitEditingWithDelegate:(id)delegate didCommitSelector:(SEL)didCommitSelector contextInfo:(void *)contextInfo;Given that the receiver has been registered with -objectDidBeginEditing: as the editor of some object, and not yet deregistered by a subsequent invocation of -objectDidEndEditing:, attempt to commit the result of the editing. When committing has either succeeded or failed, send the selected message to the specified object with the context info as the last parameter. The method selected by didCommitSelector must have the same signature as:
- (void)editor:(id)editor didCommit:(BOOL)didCommit contextInfo:(void *)contextInfo;If an error occurs while attempting to commit, because key-value coding validation fails for example, an implementation of this method should typically send the NSView in which editing is being done a -presentError:modalForWindow:delegate:didRecoverSelector:contextInfo: message, specifying the view's containing window.
Various Cocoa classes use this method whenever possible, instead of the existing -commitEditing method, to enable proper use of sheets when presenting errors. The existing -commitEditing method is not deprecated however, because there are situations in which the success or failure of commitment must be known immediately. Implementations of -commitEditing should typically use -presentError: if an error occurs.
The bindings and controller default implementation of the NSEditor methods will use the new NSResponder-based error presentation API, so that error handling can be customized by individual applications.
Also, due to the sheet based error handling, controller action methods like add:, remove:, fetch:, etc. will now execute their operations deferred, so when the methods return, the operation is not actually performed (it will be later, but programmatic invocations of the action methods cannot rely on the operation to have completed).
CoreData's NSManagedObjectContext does implement both the NSEditor and the NSEditorRegistration methods, like NSController does.
NSObjectController
NSObjectController (and thus the subclasses NSArrayController and NSTreeController) has the following new API to specify a managed object context and a description for how to fetch objects from it (by entity name and fetch predicate). The entity name (if specified) also determines how new objects are created (instead of using a class name).- (NSManagedObjectContext *)managedObjectContext;
- (void)setManagedObjectContext:(NSManagedObjectContext *)managedObjectContext;
- (NSString *)entityName;
- (void)setEntityName:(NSString *)entityName;
- (NSPredicate *)fetchPredicate;
- (void)setFetchPredicate:(NSPredicate *)predicate;
- (void)fetch:(id)sender;
NSTreeController
Tiger introduces a new controller class in Cocoa, NSTreeController, for managing hierarchical data structures. Much of the API of this class parallels that of the array controller. This class, in conjunction with the new NSIndexPath class in Foundation, enables displaying a tree of model objects in a NSOutlineView or NSBrowser.Much of the API in this class is either inherited from NSController and NSObjectController or is designed to be similar to the API of the NSArrayController. Many of the concepts are the same except that objects are addressed by NSIndexPath instead of int.
Content of the tree controller can be set to the root of the tree of model objects. Clients can then progressively traverse parts of the tree as necessary by getting model objects based on an NSIndexPath, which represents their depth-first location in the tree. This removes the need for us to pull the entire tree into memory up front. On the down side, this prevents us from offering selectObject: functionality since we don't want to pay the penalty of doing a depth first search for an object in an unordered tree.
The NSTreeController traverses its content tree by using key value coding to find the children of a model object. There are methods for setting the key to use to traverse the tree, as well as 2 other keys used as performance improvement opportunities. If a model object in the tree can report the number of children it has, we can use the countKey to find the number of children instead of getting the entire child array just to message it count. Likewise, we can stop traversing a sub-tree once we encounter an object that tells us it is a leaf node in the tree by return YES for the key set as the leafKey.
It should be noted that because of the nature of selections in unordered trees, implementation of the preservesSelection feature maybe pretty expensive for large, sparse selections combined with broad trees.
NSArrayController (Section added since WWDC)
NSArrayController now supports filtering with NSPredicates:- (void)setFilterPredicate:(NSPredicate *)filterPredicate;The filter predicate can be bound to the (enumerated) predicate bindings of NSSearchFields (use the NSDisplayNameBindingOption and NSPredicateFormatBindingOption options to configure the bindings) to automatically set up filtering in the user interface.
- (NSPredicate *)filterPredicate;
- (void)setClearsFilterPredicateOnInsertion:(BOOL)flag;
- (BOOL)clearsFilterPredicateOnInsertion;
NSArrayController also has a new mode to handle multiple selections:
- (void)setAlwaysUsesMultipleValuesMarker:(BOOL)flag;By default, the array controller will look at values of selected objects on a key-by-key basis and indicate selections of multiple different values through the NSMultipleValuesMarker, but provide the common value if all selected objects have the same value. If alwaysUsesMultipleValuesMarker is set to YES, it will use the NSMultipleValuesMarker for all selections with two or more objects (you will typically also set NSAllowsEditingMultipleValuesSelectionBindingOption option of the various value bindings for controls bound to the array controller's selection to NO). This new flag is very useful in applications with very large arrays of objects that don't want to allow editing multiple selected objects at all and also results in strong performance enhancements in certain situations with large arrays.
- (BOOL)alwaysUsesMultipleValuesMarker;
NSUserDefaultsController (Section added since WWDC)
NSUserDefaultsController has a new method to indicate whether there are unapplied changes:- (BOOL)hasUnappliedChanges;For example, you can bind the enabled state of buttons to this method.
NSGraphicsContext
The NSGraphicsContext class now has API to specify the compositing operation setting: compositingOperation and setCompositingOperation:. As with other graphics context state, the setting is saved/restore via saveGraphicsState and restoreGraphicsState methods. The setting has no effect with existing rendering API that take compositing operation as an argument such as -[NSImage compositeToPoint:operation:].The NSGraphicsContextDestinationAttributeName now allows an instance of NSBitmapImageRep as its value. Currently only non-planar NSBitmapImageRep instances are supported. A convenience factory method graphicsContextWithBitmapImageRep: is also added.
As an example of usage, the following code will resample the bitmap in srcRep to be width x height, with the result going to outputRep:
NSBitmapImageRep *outputRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
pixelsWide:width pixelsHigh:height bitsPerSample:8 samplesPerPixel:4
hasAlpha:YES isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace
bytesPerRow:0 bitsPerPixel:0];
NSGraphicsContext *ctxt = [NSGraphicsContext graphicsContextWithBitmapImageRep:bitmapImageRep];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:ctxt];
[[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];
[srcRep drawInRect:NSMakeRect(0, 0, width, height)];
[NSGraphicsContext restoreGraphicsState];
A factory method graphicsContextWithGraphicsPort:flipped: is added. You can instantiate NSGraphicsContext from any arbitrary CGContextRef using this method. The initialFlippedState argument determines the initial flippedness setting returned from the -isFlipped method.
The class now has the flipped setting accessible via the isFlipped method. The recommended way to determine the current flipped state of rendering coordinate system is now [[NSGraphicsContext currentContext] isFlipped] instead of [[NSView focusView] isFlipped]. Note, as with NSView's counterpart, this setting does not necessarily reflect the current coordinate system.
NSAffineTransform (Section added since WWDC)
The NSAffineTransform class implementation moved from AppKit to Foundation. -transformBezierPath:, -set, and -concat methods are now part of a category implemented in AppKit.NSBitmapImageRep (Section updated since WWDC)
You can now get and set custom gamma values for PNG files using the NSImageGamma property. If set in the file, the property is set in the bitmap image rep on reading and is used when saving the bitmap as PNG data.NSBitmapImageRep now supports alternate arrangements of alpha values and formats. The following enum and methods have been added:
typedef enum {
NSAlphaFirstBitmapFormat = 1 << 0, // 0 means is alpha last (RGBA, CMYKA, etc.)
NSAlphaNonpremultipliedBitmapFormat = 1 << 1, // 0 means is premultiplied
NSFloatingPointSamplesBitmapFormat = 1 << 2 // 0 means integer
} NSBitmapFormat;
- (id)initWithBitmapDataPlanes:(unsigned char **)planes
pixelsWide:(int)width pixelsHigh:(int)height bitsPerSample:(int)bps
samplesPerPixel:(int)spp hasAlpha:(BOOL)alpha isPlanar:(BOOL)isPlanar
colorSpaceName:(NSString *)colorSpaceName
bitmapFormat:(NSBitmapFormat)bitmapFormat bytesPerRow:(int)rBytes bitsPerPixel:(int)pBits;
- (NSBitmapFormat)bitmapFormat;Returns new bitmap image rep using the specified format. Invalid formats will cause the method to return nil. For example, setting a floating point format requires bits/sample to be 32 (though we may support double or so-called half floating point values in the future.). The bitmap format may also be set to non-zero when loading in images from files. For example, PNG files are now loaded in with a format of NSAlphaNonpremultipliedBitmapFormat.
Return format image is in either from loading file or via -initWithBitmapDataPlanes:... or -initWithFocusedViewRect... If created using the older APIs that don't take a bitmap format, the format defaults to 0 (alpha last, premultiplied, integer)
By default, the new formats will be returned for cases where the source of the bitmap data is in the new format (floating point, non-premutliplied, etc.). Pre-Tiger compiled apps will still get the old premultiplied RGBA format. You can also enable or disable it explicity by setting the NSOldBitmapFormatOnly default to YES or NO. -initWithFocusedViewRect: will continue to return bitmap format 0 images.
You can now specify NSJPEG2000FileType as the output format to write JPEG 2000 files.
You can now specify progressive JPEG saving via the bitmap property NSImageProgressive to output progressive JPEG images.
When reading or writing a JPEG file, you can include an NSDictionary as the NSImageEXIFData bitmap image property.
Methods are now available that set/get the color info for a pixel in an NSBitmapImageRep. If the image rep is part of an NSImage and the rep is cached by the NSImage then changing the pixel values may not show up when drawing the image. If you do place the rep inside an image and intend to modify it, use [image setCacheMode:NSImageCacheNever]. For -getPixel:atX:y: and -setPixel:atX:y: you must supply an array to match the rep's samples per pixel and the range of values are based on the rep's bits per sample (e.g. if bps is 4, then values returned are from 0 to 15). Setting values out of range are not defined. If the bitmap has floating point samples, the actual values returned are floats. Similarly, pixel order and premultiplication are assumed to match the rep's bitmap format.
- (void)setColor:(NSColor *)color atX:(int)x y:(int)y;If you do not pass in explicit planes and bytesPerRow values when creating an NSBitmapImageRep, the buffer pointer returned may no longer be the first byte of a malloc'ed block and the bytesPerRow may be padded with extra bytes for performance. If you traverse the bytes, do not assume that bytesPerRow = width * bitsPerSample / 8 otherwise the image may appear skewed.
- (NSColor *)colorAtX:(int)x y:(int)y;
- (void)getPixel:(unsigned int[]) atX:(int)x y:(int)y;
- (void)setPixel:(unsigned int[]) atX:(int)x y:(int)y;
If you modify the bits of an NSBitmapImageRep directly, you may find cases where the image does not draw the updated image because we now allow Quartz to cache the pixels (possibly in video memory). If you request the bitmap data pointer via -[NSBitmapImageRep getBitmapDataPlanes:] or -[NSBitmapImageRep bitmapData] then the cached information will be cleared and subsequent draws will draw the updated image. If your application just requests the bitmap data pointer once and then continuously modifys it, the image may not update.
NSImage and NSCachedImageRep caching behavior
The behavior of NSCachedImageRep has changed to improve performance when Quartz Extreme is enabled. These changes may affect your code. If the image rep is created via -[NSCachedImageRep initWithSize:depth:separate:alpha:] then after drawing, the image will be be copied from the offscreen window where it is stored and the window may be released. Do not rely on the window or the rectangle to be valid outside of NSImage -lockFocus/unlockFocus calls. If you need the window, calling -[NSCachedImageRep window] will return a valid new window and rectangle that can be used until the next time -[NSCachedImageRep draw] call is made. If the NSCachedImageRep is created via -[NSCachedImageRep: initWithWindow:rect:], after drawing, the contents of the window will be copied and may not reflect the current window contents when drawn later. If you want the imageRep to reflect the current window contents, calling -[NSCachedImageRep window] will release any cached information.NSImageView now supports cut:, copy:, paste:, and delete: (Section added since WWDC)
For Tiger, NSImageView has acquired action methods that enable it to automatically support cut, copy, and paste behavior. This can pose a compatibility problem for some applications, however, as an NSImageView that is not the firstResponder, but is in the responder chain by virtue of being an ancestor of the firstResponder view, may consume -cut:, -copy:, -paste:, and -delete: action messages that would previously have been passed up the responder chain to a different intended recipient.By default, NSImageView will provide the new behavior for applications built on Tiger, while suppressing it for compatibility for applications built on Panther and earlier. To allow applications to override this decision and disable or enable the new functionality on a per-instance basis, we have added a new "allowsCutCopyPaste" attribute, and the following corresponding accessor API, to NSImageView:
@interface NSImageView
- (BOOL)allowsCutCopyPaste;
- (void)setAllowsCutCopyPaste:(BOOL)flag;
@end
NSImageCell Animation Playback
Occasionally, the frames in an animaged GIF image will specify a playback duration of zero. On Panther, an animation-enabled NSImageCell plays such an animation back with no delay between frames (i.e. as fast as possible). On Tiger, NSImageCell clamps the frame duration to a minimum of 1/30 of a second, matching the way that Safari and Internet Explorer handle such GIFs.NSImageCell NSScaleToFit and Copying
On Panther and earlier, an NSImageCell set to NSScaleToFit mode would always make a copy of its assigned image, even when the NSImageCell was sized such that the image would be displayed at exactly its original size. On Tiger, NSImageCell only makes this copy when the image is being displayed at some size other than its exact original size.NSWorkspace Custom Icon Setting API
NSWorkspace has a new -setIcon:forFile:options: method that creates an icon from a given image and assigns it as the custom icon of a given file or folder:- (BOOL)setIcon:(NSImage *)image forFile:(NSString *)fullPath options:(unsigned)options;The "image" parameter specifies an arbitrary image, with or without transparency information (alpha), that will be automatically rescaled to generate the icon's representations. "fullPath" must specify an existing file or folder to which the user has write permissions. The "options" parameter is a bitwise combination of the following flags declared in NSWorkspace.h (it may be zero):
typedef unsigned int NSWorkspaceIconCreationOptions;The option flags provide control over the kinds of representations the custom icon will contain.
enum {
NSExcludeQuickDrawElementsIconCreationOption = 1 << 1,
NSExclude10_4ElementsIconCreationOption = 1 << 2
};
The "QuickDraw" format allows for icon representations up to 128x128 pixels, and is supported on OS X v10.0 through 10.4.
OS X v10.4 supplements this with a new class of icon representation, designed to support higher resolutions with better storage efficiency. Finder on OS X v10.4 does not yet make use of icons in this new format, but support for generating them is being provided in the -setIcon:forFile:options: API for forward compatibility. This new icon representation is safely ignored by Finder on OS X v10.3, but its presence will prevent display of the file's custom icon on pre-10.3 systems, even if representations in the "QuickDraw" format are also present. Due to this compatibility issue, and to avoid needlessly consuming additional storage, it is recommended that applications that use the -setIcon:forFile:options: API suppress generation of this new representation, pending its use by a future release of the system.
NSExclude10_4ElementsIconCreationOption suppresses generation of representations in the new compressed high-resolution format. NSExcludeQuickDrawElementsIconCreationOption suppresses generation of the QuickDraw-format representations that are understood and used by OS X v10.0 through 10.4. When neither flag is specified, the behavior will be to generate representations in both formats, resulting in a file or folder whose custom icon will be displayable on OS X v10.3 and 10.4.
NSWorkspace (Section added since WWDC)
If the method fullPathForApplication: failed to find an application using the standard LaunchServices database, it would fall back to doing a world-search in the standard places where applications many be found. In practice though the LaunchServices database does include those places, so a brute force search through these just makes the method call considerably slower.For apps linked on Tiger, this method will no longer do this secondary search.
The method selectFile:inFileViewerRootedAtPath: now consults a user default named "NSFileViewer", and if present, uses it as the bundle ID of the application to use as the file viewer to select the file in. If this default is not set, or there is no corresponding registered application, then Finder is used as normal. selectFile:inFileViewerRootedAtPath: is the method applications should use for "Reveal in Finder" functionality.
Since 10.0, the method getInfoForFile:application:type has been behaving differently than documented. The documentation claimed that the returned type is actually not the file type but one of a small set of predetermined identifiers indicating the kind of file; it turns out this method actually returned the type of the document. In Tiger, we continue with this preexisting behavior; the documentation will be fixed. The other documentation claim, that this method will return NO if the file doesn't exist, is true only when application info is asked for (argument to application: is non-NULL). So this method will return NO only if app info is asked for, and either the file doesn't exist, or LaunchServices does not have an application association for the document.
The NSWorkspaceDidUnmountNotification notification could be sent even for unsuccessful unmount attempts. It is now sent only if the volume is actually unmounted.
NSColor (Section added since WWDC)
Calibrated NSColors (those created with colorWithCalibratedRed:.., colorWithCalibratedHue:.., or colorWithCalibratedWhite:...) now use Quartz generic color spaces, rather than the "display" (aka "device") color spaces. For debugging purposes this behavior can be disabled with the NSUseGenericColorSpaceForCalibrated default. This is a debugging default and will be removed in a future update.In applications, the proper way to get the device color space behavior is to create colors with colorWithDeviceRed:..., colorWithDeviceWhite:..., etc.
In many cases, as appropriate, the underlying color for NSColors representing colors used in the user interface (for instance, methods such as -[NSColor alternateSelectedControlColor]) has been changed to device color space.
NSColorSpace (Section added since WWDC)
A new class, NSColorSpace, along with new API in NSColor, enables creating NSColor instances which refer to custom color spaces, including those created with ColorSync profiles.You can create NSColorSpace instances from CMProfileRef instances or NSDatas containing ICC profile data:
- (id)initWithICCProfileData:(NSData *)iccData;
- (id)initWithColorSyncProfile:(void * /* CMProfileRef */)prof;
- (NSData *)ICCProfileData;You can query the characteristics of color spaces with:
- (void * /* CMProfileRef */)colorSyncProfile;
- (int)numberOfColorComponents; // Does not include alphaNSColorSpace is intended to be toll-free bridge to CGColorSpaceRef. But this has not been implemented in Tiger.
- (NSColorSpaceModel)colorSpaceModel;
- (NSString *)localizedName; // Will return nil if no localized name
Note that the existing "color space" concept, where a small number of predefined color spaces are identified by their names, should not be confused with this new NSColorSpace class. Different types of NSColors have traditionally been distinguished by their "color space" (represented by colorSpaceName). This color space determines the primitive accessors for the color, and in general NSColor instances with different colorspaces do not compare equal; NSColor instances in the same colorspace compare equal if all their attributes are equal.
Existing color space names supported by NSColor are NSCalibratedWhiteColorSpace, NSCalibratedRGBColorSpace, NSDeviceWhiteColorSpace, NSDeviceRGBColorSpace , NSDeviceCMYKColorSpace, NSNamedColorSpace, and NSPatternColorSpace. NSColorSpace provides class methods to return instances which correspond to the applicable predefined color space names:
+ (NSColorSpace *)genericRGBColorSpace; // NSColorSpace corresponding to Cocoa colorspace name NSCalibratedRGBColorSpaceIn order to support custom NSColorSpaces, we enable creating NSColors with the color space name "NSCustomColorSpace". Such colors are created with:
+ (NSColorSpace *)genericGrayColorSpace; // NSColorSpace corresponding to Cocoa colorspace name NSCalibratedWhiteColorSpace
+ (NSColorSpace *)genericCMYKColorSpace;
+ (NSColorSpace *)deviceRGBColorSpace; // NSColorSpace corresponding to Cocoa colorspace name NSDeviceRGBColorSpace
+ (NSColorSpace *)deviceGrayColorSpace; // NSColorSpace corresponding to Cocoa colorspace name NSDeviceWhiteColorSpace
+ (NSColorSpace *)deviceCMYKColorSpace; // NSColorSpace corresponding to Cocoa colorspace name NSDeviceCMYKColorSpace
/* Create colors with arbitrary color space. The number of components in the provided array should matchand the attributes of such colors are accessed with:
the number dictated by the specified color space, plus one for alpha (1.0 for opaque colors);
otherwise an exception will be raised. If the color space is one which cannot be used with NSColors, nil is returned.
*/
+ (NSColor *)colorWithColorSpace:(NSColorSpace *)space components:(const float *)components count:(int)numberOfComponents;
/* For colors with custom color space; get the color space and individual floating point components, including alpha.One additional API added to NSColor allows converting colors between color spaces:
Note that all these methods will work for other NSColors which have floating point components.
They will raise exceptions otherwise, like other existing color space-specific methods.
*/
- (NSColorSpace *)colorSpace;
- (int)numberOfComponents;
- (void)getComponents:(float *)components;
/* colorUsingColorSpace: will convert existing color to a new color space and create a new color,colorUsingColorSpace: and the three methods above work not only on NSCustomColorSpace colors, but also on other floating component-based colors. Note that colorUsingColorSpace: does not guarantee to return a NSCustomColorSpace color; for instance, if -[NSColor colorUsingColorSpace:[NSColorSpace genericRGBColorSpace]] is sent to a NSCalibratedRGBColorSpace color, the same color might very well be returned. Of course such a color will respond with NSColorSpace genericRGBColorSpace] for its colorSpace, so the result is not that unexpected.
which will likely have different component values but look the same. It will return the same color
if the color space is already the same as the one specified. Will return nil if conversion is not possible.
*/
- (NSColor *)colorUsingColorSpace:(NSColorSpace *)space;
The existing method colorUsingColorSpaceName: is still the sure way to get a color with a certain colorSpaceName for another color (or nil if the conversion is not possible).
When colors with custom color spaces are archived into old (non-keyed) archives, the assumption is that such archives still need to be read on pre-Tiger systems, so the colors are written in a pre-Tiger compatible fashion, by being converted to one of the named colorspaces, without the NSColorSpace instance. it's possible to override this behavior with the NSWriteCustomColorSpacesToOldArchives default; setting this to YES means that custom color spaces will be written to non-keyed archives, making them incompatible with pre-Tiger systems.
NSColorWell (Section added since WWDC)
Removing a NSColorWell from its window now deactivates the color well.NSColorList (Section added since WWDC)
Non-editable color lists (identified by isEditable returning NO) now raise exceptions when an attempt is made to modify them. This is as documented.Accessibility Changes
The following method was added:- (BOOL)accessibilitySetOverrideValue:(id)value forAttribute:(NSString *)attribute;It allows you to override the value of an attribute or add a new attribute to a particular UI Element – i.e. an instance of NSObject conforming to the the NSAccessibility protocol. Previously, the only way to accomplish this was to use a custom a subclass for that UI Element and override the appropriate NSAccessibility protocol methods – e.g. accessibilityAttributeValue.
• This method only works on objects whose class already implements the NSAccessibility protocol.
• The return value indicates if the attempt to override was successful.
• If the specified attribute is already supported by the object, the value you specified wins - i.e. for this instance it will override the attribute value that would have been returned otherwise. This is done outside the NSAccessibility protocol – accessibilityAttributeValue won't get called when determining an overridden attribute's value.
• If the specified attribute does not exist it will be created. This is done outside the NSAccessibility protocol – accessibilityAttributeNames will still return the old list which does not contain the new attribute.
• Once again, overriding attributes is done outside the NSAccessibility protocol. Accessing attributes using accessibilityAttributeNames and accessibilityAttributeValue will not return attributes created by the override process nor will it return their overridden values.
• Overridden attributes are not settable. I.e. accessibilitySetValue:forAttribute will never be invoked for an overridden attribute. If you override a settable attribute it will no longer be settable. What's being referred to here is the ability of the assistive app to change the attribute's value. Calling accessibilitySetOverrideValue:forAttribute again will change the overridden value.
• The method, accessibilitySetOverrideValue:forAttribute:, should not be confused with accessibilitySetValue:forAttribute:. The latter method, which is a part of the NSAccessibility protocol, is invoked when an assistive application wants to change the value of an attribute - e.g. alter the setting of a slider.
• If you need to undo the effect of using this method, call it again passing nil for the value.
• You need to ensure you invoke this method on the actual object that represents the UI Element. E.g. in the case of NSButton you'd need to use the underlying NSButtonCell. The NSButton itself is ignored by accessibility. If you're unfamiliar with this concept see the documentation for the accessibilityIsIgnored method of the NSAccessibility protocol.
• This method works on an object representing a single UI Element. When there is no object underlying a UI Element then you won't be able to use it. A common case where this is a problem is when a single object represents multiple UI Elements (e.g. NSSegmentedCell has only a single object but it provides UI Elements for each segment).
NSString *NSAccessibilityRoleDescription(NSString *role, NSString *subrole);These functions were added to help with implementing the accessibility protocol – specifically for returning descriptions of standard roles and actions. E.g. if you implement a button widget that does not inherit from NSButton you should use NSAccessibilityRoleDescription to return a localized role description matching what is returned by standard buttons.
NSString *NSAccessibilityRoleDescriptionForUIElement(id element);
NSString *NSAccessibilityActionDescription(NSString *action);
• You should pass nil to NSAccessibilityRoleDescription if there is no sub-role.
• NSAccessibilityRoleDescriptionForUIElement is like NSAccessibilityRoleDescription, but it queries the element to get the role and sub-role. Obviously, NSAccessibilityRoleDescription is more efficient, but this function is useful for accessorizing base classes so that they properly handle derived classes – which may override the sub-role – or even the role.
NSOpenGL Pixel Format Attributes
Four new pixel format attribute constants have been added to NSOpenGL.h, to expose new framebuffer options provided by CGL.NSOpenGLPFAColorFloat = 58, /* color buffers store floating point pixels */See the OpenGL release notes for information regarding the correct usage of these features.
NSOpenGLPFAMultisample = 59, /* choose multisampling */
NSOpenGLPFASupersample = 60, /* choose supersampling */
NSOpenGLPFASampleAlpha = 61, /* request alpha filtering */
Services (Section added since WWDC)
In previous releases, AppKit's deferred Services menu building until the first time an application's menu was accessed. This produced a slight delay in the first menu pull-down, while making the key equivalent shortcuts for Services menu items unusable until the Services menu had been built. Both issues have been fixed in Tiger, making key equivalents for Services menu items usable immediately after app launch, while deferring initialization of the Services menu until it is actually needed.In Tiger, the Services facility names the communication ports that it uses based on the service provider's bundle identifier. (On previous releases, the process name was used instead.) This change in naming convention will be invisible to most service providers and clients. However, a ".service" bundle can optionally declare services that are provided by another bundle's executable, instead of containing its own executable. In such cases, the bundle that declares the service must specify the provider's bundle identifier as the service's NSPortName, to enable the Services facility to find and (if necessary) launch the provider. The provider bundle can be an ".app" bundle, or a ".service" bundle that has its own executable.
Bug Fix in Handling of NSURLPboardType (Section added since WWDC)
A bug was introduced in Mac OS 10.3 that sometimes prevented NSURLPboardType data from appearing on pasteboards provided by Carbon programs. For example, the invocation of +[NSURL URLFromPasteboard:] in the CocoaDragAndDrop example would incorrectly return nil when an image file was dragged from the Finder and dropped on one of the program's DragDropImageViews. This bug has been fixed.Delegation and Notification warning (Section added since WWDC)
With AppKit classes which provide delegation and notification (such as NSWindow), if you explicitly register for certain notifications which are also used to send delegate messages, and then you stop being a delegate, you are unregistered for those notifications. It's not clear how this will be addressed in the future, but you should be aware of this potentially unexpected behavior.Alert panel in Java
Bug where occurrences of "%" would disappear in alert panels in Cocoa Java applications has been fixed for applications linked on Tiger or later. A workaround for earlier applications is to double each "%" that occurs in the message string.Notes specific to MacOS X 10.3
Aqua Refinements
Panther brings along a bunch of Aqua refinements, which are meant to enhance the look of applications compatibly, without needing any changes to the applications. The changes to user interface elements preserve layout metrics, which helps maintain compatibility. Where appropriate, return values of various APIs (for instance, NSColors) have been changed to support the new look; applications using these APIs will get the new values automatically.Mini controls
A number of controls now support a control size NSMiniControlSize that is smaller than NSSmallControlSize. The controls that draw with this size are: radio buttons, checkboxes, push buttons, sliders, tab views, steppers, popup and pulldown menus, and comboboxes. Rounded text fields and search fields can now be sized smaller and will use a smaller rounded bezel depending on the height of the text field. NSStepperCell now supports both small and mini sizes. Mini-controls are designed to be used with LucidaGrande 9 pt as the text font.Bindings (aka Controller Layer)
A major new feature in Cocoa for Panther is the ability to bind UI objects to model objects through controllers. This technology allows Cocoa developers to get much more application implementation done directly in Interface Builder.The controller layer technology is based in the Model-View-Controller (MVC) design paradigm. Cocoa provides a rich set of view and model classes, but until now there was no powerful and generalized controller functionality. With the new controller layer, Cocoa developers finally have API level and Interface Builder level support for binding display values and characteristics of an application's UI to data held in the application's data model. Via the controller layer, data values and changes can be propagated live between UI elements and the application's data storage without developers having to write all of the glue code they had to before.
In Interface Builder, check out the Controllers palette for NSController classes that can be added to new and existing nibs. These controllers add functionality like selection tracking, propagating edits in the user interface, sorting, and handling input validation. The logic built into the NSUserDefaultsController, NSObjectController, and NSArrayController, which are provided to you by default, allow you to focus on designing the data model and user interface without having to write excessive amounts of glue code to get to a polished application. The controller classes provide the glue logic for you.
In Interface Builder, there is also a new inspector item: the Bindings Inspector. This inspector allows you to bind user interface elements like NSTextField, NSTableView, NSImageView, and NSTabView, to NSController instances in your nib (or controller instances to other controllers). This way you don't have to explicitly make outlet connections to your NSDocument or NSApplication file's owner. Various properties of the widgets (such as the source of a NSTextField's value, or the font and text color of the text field) can be controlled through a controller.
Relevant header files for this technology include <AppKit/NSKeyValueBinding.h>, <AppKit/NSController.h>, <AppKit/NSArrayController.h>, <AppKit/NSObjectController.h>, and <AppKit/NSUserDefaultsController.h>.
Key value coding has also been enhanced to support the bindings technology. Please refer to <Foundation/NSKeyValueCoding.h> and <Foundation/NSKeyValueObserving.h> for more information.
Note that nib files using the new controller layer (instances of NSController subclasses and bindings) can only be loaded and saved on the "10.2 and later" nib file format. Nib files in older formats have to be converted by opening them in Interface Builder and explicitly saving them in the new format.
Also, nib files created with pre-releases of OS X v10.3 may contain bindings that are not legal in the GM release. Those bindings typically result in key-value coding exceptions ("undefined key") at runtime and show up in the "Parameters" section of the Bindings inspector in Interface Builder. You should disconnect the bindings manually and replace them with a new binding (which is usually obvious to pick).
Bindings for menu items and other objects that auto-validate the enabled state (like toolbar items) are ignored unless auto-validation is turned off (in the NSMenu - which can be done in Interface Builder). For example, a menu item with a value binding, but no target/action set, will always be disabled unless auto-validation is turned off manually.
NSControllers ignore the options that can be passed to the -addObserver:forKeyPath:options:context:context: method. So even if you register observers with the NSKeyValueObservingOptionNew or NSKeyValueObservingOptionOld options, you will not receive the values in the change dictionary of the -observeValueForKeyPath:ofObject:change:context: calls. This is usually not a problem, but if you rely on receiving those values, a workaround may be to observe the model objects directly.
NSAlert
We added NSAlert as a new public class. This class provides the functionality previously available only through NSPanel C-based functions, and builds flexibility on top of that functionality. For example, it is now possible to specify a custom icon and assign application specific key equivalents or return values to the NSAlert buttons. It is also possible to use more than three buttons, although this should be done only when strictly necessary. We have also added API for inclusion of a help button on the alert panel.This API can be used for both modal panels and sheets.
Note that by default, the new return values --- NSAlertFirstButtonReturn, etc --- are easier to use and more flexible, but are not the same as the previous values --- NSAlertDefaultReturn, etc. This is an important point, as your alert return handlers will change behavior if you move your code over without paying attention to this. The following convenience method can be used for easy migration from uses of the C-based APIs, as it sets up compatible return values:
+ (NSAlert *)alertWithMessageText:(NSString *)messageThe return values can be customized with the setTag: methods, whose use in the alert panel is reserved for this purpose.
defaultButton:(NSString *)defaultButton
alternateButton:(NSString *)alternateButton
otherButton:(NSString *)otherButton
informativeTextWithFormat:(NSString *)format, ...;
Note that the alert panel also reserves the use of the target and the action.
Please refer to documentation for more detailed info on this new class.
NSSpeechRecognizer / NSSpeechSynthesizer
NSSpeechRecognizer and NSSpeechSynthesizer are two new AppKit classes which provide access to OS X's speech capabilities. There are examples of these classes in use in /Developer/Examples/Speech. In addition, documentation for these classes is available in the Application Kit reference.NSShadow
NSShadow is a new AppKit class, created for the purpose of holding the parameters of a drop shadow to be used when drawing. Shadows are always drawn in base space (also known as default user space). This means that rotations, translations and so on of the current transformation matrix (the CTM) don't affect the resulting shadow. Another way to think about this is that changes to the CTM don't move or change the light source.There are two positional parameters for a shadow, an x-offset and a y-offset of the shadow, expressed as a single NSSize, in default user space units, with positive values being up and to the right. There is one additional floating-point parameter, the blur radius, which specifies how much an object's image mask is blurred before it is composited onto the destination. A zero value means no blur, and larger values give correspondingly larger blurs, again in default user space units.
In addition, a shadow may have a color. If no color is set, then the shadow will be drawn using black with an alpha value of 1/3. If a color is set, then the shadow will be drawn using that color. Currently only colors convertible to RGBA are supported.
An NSShadow may currently be used in one of two ways. First, it may be set, like a color or a font, in which case it is applied to all drawing until another shadow is applied or until the next graphics state restore. It may also be used as the value for the new NSShadowAttributeName text attribute, in which case it will be applied to the glyphs corresponding to the characters bearing this attribute. See the section on additional text attributes for the definition of NSShadowAttributeName.
NSNib
NSNib is a new AppKit class representing a nib file. When an NSNib instance is created from a nib file, all of the data needed to instantiate the nib (the object graph as well as images and sounds that might be in the nib bundle) are read from the disk, however the nib is not instantiated until you call one of the instantiation methods. You can use NSNib to quickly reinstantiate a frequently accessed nib file, without loading the nib repeatedly from disk.NSSegmentedControl, NSSegmentedCell
NSSegmentedControl is a new control that implements a multi-part cell or 'segment' view. Each segment can contain an image, plain label, menu, tag, and tooltip. The segments are autosized unless a specific width is set. The class provides three tracking modes: Radio-like (NSSegmentSwitchTrackingSelectOne), toggling (NSSegmentSwitchTrackingSelectAny), and push-button (NSSegmentSwitchTrackingMomentary).NSSegmentedControl is a pass through for most of the calls to its cell. It also handles keyboard UI. NSSegmentedCell can be placed inside a matrix though usually it's inside an NSSegmentedControl.
Please refer to documentation for more info on these two classes.
(As an aside, this was the control for which we asked for naming suggestions during WWDC 2003. Many thanks for your cards and letters --- we got hundreds of suggestions!)
NSSearchField and NSSearchFieldCell
New subclasses of NSTextField and NSTextFieldCell have been added that create a standard UI for search fields like the ones in Mail, Safari, and Address Book. This includes a cancel button, search button with menu and the option to send the results while typing or when the user presses return. API for NSSearchField is minimal and forwards to NSSearchFieldCell. You should set the target and action of this control or its cell to the receiver that is interested in the search request. The border is a round text field.Please refer to documentation for more info on these two classes.
NSSlider
A new style of slider called a circular slider (i.e. dial) is available. You can get it by setting the slider type. You then get a fixed sized slider that goes from minValue to maxValue. minValue is at the top and the value increases as you rotate clockwise to just below maxValue (e.g. if you set min = 0, max = 360, you can get to 359.999). You can show tick marks and have values limited to just the tick marks the same as a regular slider. You can only have regular and small. There is no mini version.typedef enum {
NSLinearSlider = 0,
NSCircularSlider
} NSSliderType;
- (void)setSliderType:(NSSliderType)sliderType;
- (NSSliderType)sliderType;
New NSOpenPanel / NSSavePanel
Panther features a new user interface for open and save panels, while maintaining compatibility with the existing APIs.Due to the changes in the panel, the following constants (view tags) are no longer supported. The last three were already marked in NSSavePanel.h as deprecated:
NSFileHandlingPanelImageButtonAdded the following getter methods to match the existing setter methods:
NSFileHandlingPanelTitleField
NSFileHandlingPanelBrowser
NSFileHandlingPanelForm
NSFileHandlingPanelHomeButton
NSFileHandlingPanelDiskButton
NSFileHandlingPanelDiskEjectButton
- (id)delegate; // - (void)setDelegate:(id)delegate;Added the following delegate methods to allow accessory views to keep in sync with changes in the state of the panel:
- (BOOL)canSelectHiddenExtension; // - (void)setCanSelectHiddenExtension:(BOOL)flag;
- (void)panel:(id)sender directoryDidChange:(NSString *)path;Two methods have been added to allow providing a short message at the top of the panel:
- (void)panelSelectionDidChange:(id)sender;
- (NSString *)message;In Jaguar, the methods directory, filename, and URL were usable only after the panel was dismissed – and documented as so. This restriction has been lifted in Panther.
- (void)setMessage:(NSString *)message;
The use of -selectText: is deprecated. This method no longer does anything.
One known incompatibility is with applications which messaged the panel after it was released. This often worked before. If you notice that an application crashes after using the open or save panels, you can, as a debugging or temporary measure, set the NSDelayedSavePanelDeallocation user default to YES, which should avoid the problem. This default will be removed in the future.
NSSavePanel
Two methods have been added to NSSavePanel to support changing the label next to the filename edit field - which is normally labelled "Save as:":- (NSString *)nameFieldLabel;In Jaguar we supported a single required file type with the methods setRequiredFileType and requiredFileType. We now support a list of types:
- (void)setNameFieldLabel:(NSString *)label;
- (NSArray *)allowedFileTypes;The old and new calls interact as follows. Calling setRequiredFileType: is equivalent to calling setAllowedFileTypes: with an array of that one type. Calling requiredFileType will return the first element of the list of allowed types or nil if there are none. As was the case with setRequiredFileType: nil, setAllowedFileTypes:nil means allow any file type. Calling setAllowedFileTypes: with the empty array is not allowed.
- (void)setAllowedFileTypes:(NSArray *)types;
In Jaguar, if a user tried to use a filename with a recognized extension that did not match the required type they were given three options: cancel, replace their extension with the required one, or use both (e.g. foo.html.txt). There was no option to use the specified extension. Apps that needed to provide this option (e.g. TextEdit and Safari, so you could use .h or a .html as an alternative to .txt) had special code to work around this limitation.
To address this there are two new methods:
- (BOOL)allowsOtherFileTypes;As was the case in Jaguar, if the user tries to save a filename with a recognized extension that's not in the list of allowed types they will be presented with a dialog. However, if allowsOtherFileTypes is YES, then the dialog will present the option of using the extension the user specified. The default setting for allowsOtherFileTypes is NO, otherwise existing applications would start getting extensions they are not prepared to handle.
- (void)setAllowsOtherFileTypes:(BOOL)flag;
NSOpenPanel
The following new NSOpenPanel method allows modeless operation of the open panel:- (void)beginForDirectory:(NSString *)pathTwo methods have been added to allow open panels to have a "New Folder" button - which may be useful in open panels configured to allow folder selection:
file:(NSString *)name
types:(NSArray *)fileTypes
modelessDelegate:(id)delegate
didEndSelector:(SEL)didEndSelector
contextInfo:(void *)contextInfo;
- (void)setCanCreateDirectories:(BOOL)flag;
- (BOOL)canCreateDirectories;
NSMenu
NSMenuItems which have submenus now can have a target and action set and the item itself will be selectable. Calling -[NSMenuItem setSubmenu:] will no longer modify the action if it isn't NULL or @selector(submenuAction:). You can turn it off again by setting the action of the item to either NULL or @selector(submenuAction:).Menubar items will now display an image if you set it.
NSMenuItem has some new setter/getter API to add functionality found in Carbon menus:
- (void)setAlternate:(BOOL)isAlternate;This marks the item as an alternate to the previous menu item. If the item has the same key equivalent as the previous item but has different key equivalent modifiers then the items will be folded into a single visible item and the appropriate item will show while tracking the menu. You can have items with no key equivalent but different modifiers in which case, the only way to access the alternate items is via the mouse. You can have a number of items marked as alternate though if their key equivalents don't match, they may end up as separately visible items. Marking the first item as an alternate has no effect. This flag is archived.
- (BOOL)isAlternate;
- (void)setIndentationLevel:(int)indentationLevel;This sets the menu item indentation level from 0 to 15. Indentation levels greater than 15 are pinned to the maximum. Values less than 0 generate an exception. The default indentation level is 0. This value is archived.
- (int)indentationLevel;
You can now specify the font when displaying a context menu using the class method:
+ (void)popUpContextMenu:(NSMenu *)menuPassing in nil for the font uses the default font for menu.
withEvent:(NSEvent *)event
forView:(NSView *)view
withFont:(NSFont *)font;
You can pass in a custom string for a menu item by setting an attributed string. This will let you add styled text and an embedded image to a menu item string. If the text color is not set, it will be white on selection and grey on disabled. Any colored text will remain unchanged when higlighted. When you set the attributed title, the regular title is also set with the plain string value but when you clear the attributed string title, the title remains unchanged. This string is not archived in in the old nib format.
- (void)setAttributedTitle:(NSAttributedString*)string;You can set a help tag for a menu item. This includes items in the main menu bar. This string is not archived in the old nib format.
- (NSAttributedString*)attributedTitle;
- (void)setToolTip:(NSString*)toolTip;You can now register for a notification when menu tracking ends even if no action is sent. Register for the notification:
- (NSString*)toolTip;
NSString *NSMenuDidEndTrackingNotification;This notification is sent for the main menu bar ([NSApp mainMenu]) and for the root menu of a popup button.
NSMenu now has a delegate that you can use to populate a menu just before it is going to be drawn and to check for key equivalents without creating a menu item. NSMenu has two new methods:
- (void)setDelegate:(id)anObject;This delegate is not archived in in the old nib format.
- (id)delegate;
To populate the menu, the delegate should implement either:
- (void)menuNeedsUpdate:(NSMenu*)menu;Which is called when the menu is about to be displayed at the start of a tracking session. You can change the menu by adding, removing or modifying menu items. Any new items should have the proper enable state set.
Alternatively, if population is going to take some time, you can implement a pair of methods:
- (int)numberOfItemsInMenu:(NSMenu*)menu;The first method returns the number of items in the menu. If the value returned is positive, the menu is resized by either removing or adding items. If you return a negative value, the number of items is left unchanged and update method is not called. Newly created items are blank. Then the second method is repeatedly called for each item at which time, the menu title, image, etc. can be updated. If during the updating, the user does something so that the menu no longer needs to be displayed, then the shouldCancel paramter will be set to YES. You can ignore the flag or stop updating and save where you left off until the next time.
- (BOOL)menu:(NSMenu*)menu updateItem:(NSMenuItem*)item atIndex:(int)index shouldCancel:(BOOL)shouldCancel;
If the delegate implements the method:
- (BOOL)menuHasKeyEquivalent:(NSMenu*)menu forEvent:(NSEvent*)item target:(id*)target action:(SEL*)action;This method allows the delegate to return the target and action for a key down event. The method should return YES if there would be a valid and enabled item for the key event and return the target and action (both of which can be nil/ NULL to invoke the menu's target and action). If this method isn't defined in the delegate, the menu will be populated to find out if any items have a matching key equivalent. The delegate should return NO if there are no items with that key equivalent or the item would be disabled.
For applications built on Panther or later, menu tracking will now run the runloop in NSEventTrackingRunLoopMode rather than NSDefaultRunLoopMode. This is consistent with tracking in other controls, and fixes a problem where an application could cause menus to get stuck onscreen by intercepting the runloop unexpectedly, eg. by putting up a modal panel while the user was tracking in a menu. This change means that timers and other runloop sources added only for NSDefaultRunLoopMode will not fire during menu tracking. If you want your timer to continue to fire during menu tracking, you should also add it to the runloop for NSEventTrackingRunLoopMode. One easy way to do this is to use kCFRunLoopCommonModes, although that also has the effect of enabling your runloop source while in NSModalPanelRunLoopMode.
Delegation and Notification
With AppKit classes which provide delegation and notification (such as NSWindow), if you explicitly register for certain notifications which are also used to send delegate messages, and then you stop being a delegate, you are unregistered for those notifications. It's not clear how this will be addressed in the future, but you should be aware of this potentially unexpected behavior.NSColor
You can determine the current system control tint when rendering colors by using the NSColor class method.+ (NSControlTint)currentControlTint;This method will currently return either NSBlueControlTint or NSGraphiteControlTint.
The method selectedMenuItemColor now returns a pattern image based on the current appearance (blue or graphite) rather than a solid color.
-[NSColor set] no longer strips the transparency when the output is going to a device other than the screen (for instance, printer or file). Note that this change was introduced in Jaguar software update 10.2.3; and it is active only for applications linked on 10.2 or later. However, an app can choose to force the behavior one way or the other by registering the default NSAllowTransparencyWhenPrinting with YES or NO.
+[NSColor disabledControlTextColor] now returns 50% white rather than 53% white.
It is now possible to set the fill and stroke colors independently with NSColor, with the setFill and setStroke methods. The set method continues to set both. Going forward, all three should be treated as primitives --- that is, methods that need to be implemented by subclassers. For compatibility, there are implementations of setFill and setColor in NSColor, but they work by converting the color to RGB and filling or stroking it.
NSColor has introduced a new method which returns the standard list of alternating colors used by many applications, such as iTunes. NSTableView has added straightforward support for drawing its background using these colors. However, those implementing a custom row based controls may use this new API to draw an alternating background:
+ (NSArray *)controlAlternatingRowBackgroundColors;
NSColorPanel
The color panel can now display arbitrary copyright information for a color list. To provide copyright information, simply add the NSColorListCopyrightInfo key to your color lists strings file (eg. MyColorList.clr/English.lproj/MyColorList.strings).NSColorWell
Prior to Panther, sometimes just clicking on a color well would cause its action to be sent. This has been fixed. NSColorWell now only sends its action if the color it's holding has indeed changed.Calling activate: programatically now correctly shows the color panel if it is already not visible.
NSImage
The issue where rendering an NSImage into another lockFocus'ed NSImage could wipe out the graphics state is fixed.The limitation on the size of an NSCachedImageRep and thus an NSImage that you -lockFocus on has been raised from 10,000 to 32,767. Note that you images below this size may still fail because of memory limitations.
NSImageRep
The NSImageRepRegistryDidChangeNotification now contains the actual NSImageRep class that was added or removed instead of the receiver of the message, which was usually the base NSImageRep class.NSBitmapImageRep
You can now read and write 5 channel CMYKA images.If an animated GIF image contains information specifying the number of times to play the GIF, a new property is available as a read-only value.
NSString* NSImageLoopCount;If set, the extension had an explicit value specified. The loop count value will be between 0 and 65,535 with a value of 0 meaning loop forever. If the property is not present, it was not specified in the file and it will be up to the app to decide how many times to loop
PNG files now have the correct size and DPI set based in the 'pHYs' chunk in the file. This only applies to applications compiled after Jaguar (10.2.x).
NSImageView
NSImageView instances can now automatically play back animated GIF images. This functionality is controlled by the new accessor API:
- (void)setAnimates:(BOOL)flag;An NSImageView whose "animates" property is set to YES will automatically play any animated image that is assigned to it, with the timing and looping characteristics specified by the image data. When "animates" is set to NO, the NSImageView displays the first frame of the animation (consistent with the behavior on Jaguar and earlier). The default is YES for newly created NSImageView instances, NO for previously created NSImageView objects loaded from .nib files.
- (BOOL)animates;
This setting does not affect the display of ordinary still images.
Applications wanting more control over animation playback can use NSBitmapImageRep's animated image functionality to directly access the animation's individual frames and timing data.
NSView
On Jaguar and earlier, areas of a view that are marked dirty using -setNeedsDisplayInRect: are coalesced (via an NSUnionRect() operation) into a single "dirtyRect" that the view maintains. The drawing thus scheduled is done later at the end of the run loop cycle, when invalidated areas are propagated as needed to each view's ancestors and descendants and -drawRect: is invoked for each view that needs to draw some or all of its contents.In addition to requiring redrawing of more of a view's area than may be strictly necessary, this "coalescing" of invalidated rectangles into a single rectangle per view had the side effect of sometimes causing invalidation of otherwise "clean" views and view subtrees that happen to share a common container view (e.g. parent NSBox or NSView). An application's susceptibility to this problem depends on its UI layout and invalidation patterns.
On Panther, we maintain a more detailed representation of the invalidated parts of views that enables us to better avoid this problem. Applications automatically inherit most of the benefits of this enhancement automatically. However, there is also new API provided for implementors of NSView subclasses that wish to take advantage of the more detailed dirty area information that is now available to them.
-drawRect: remains the overridable "draw self" callback for view classes. However, the implementor of -drawRect: can now call back to self to request a more detailed description of the area to be drawn than the single NSRect parameter to -drawRect: provides. Specifically, one can request a list of rectangles that more closely approximates the area that needs drawing, via the new method:
- (void)getRectsBeingDrawn:(const NSRect **)rects count:(int *)count;On return from this method, *rects contains a pointer to the list of NSRect values, and *count is the number of rectangles in the list. Depending on its drawing strategy, a -drawRect: implementation can inspect this list directly to determine what to draw, or it can use the convenience method:
- (BOOL)needsToDrawRect:(NSRect)aRect;to test individual objects to be drawn one at a time against the list. -needsToDrawRect: returns YES if aRect intersects any of the rectangles in the list, NO otherwise. Use of this convenience method would be appropriate for a view that determines what to draw by simply iterating over a list or hierarchy of drawable objects. A view that can efficiently determine which of its elements needs to be drawn as a function of a given rectangle (such as a view that displays an image or images, or a regular grid of objects) may be better suited to inspecting the rect list directly. Note that the NSRect parameter that -drawRect: receives remains potentially useful as an overall bounding rectangle surrounding the area to be drawn. Intersection tests against this rectangle can be performed as a quick "trivial rejection" test, identifying objects that are clearly outside the area to be drawn. -needsToDrawRect: uses this strategy in its implementation.
To guarantee compatible drawing behavior for existing view classes, AppKit by default enforces clipping to the area that needs drawing. On Jaguar and earlier, clipping was enforced more loosely to the NSRect parameter of -drawRect:.
A view that does not want the default, AppKit-provided clipping (either because its -drawRect: implementation is very careful to draw only within the requested area, or because it sets up its own clipping) can refuse the default clipping by overriding the new -wantsDefaultClipping method to return NO:
- (BOOL)wantsDefaultClipping;The default implementation provided by NSView returns YES. Any view that returns NO for this method is responsible for setting up its own clipping, or for otherwise insuring that it does not draw outside the requested area. The view will inherit only whatever clipping is provided by its nearest ancestor that does not itself forego the default AppKit-provided clipping.
NSView has new API that allows for hiding individual views or entire view subtrees. A hidden view remains in its superview's list of subviews and participates in autoresizing, but is not displayed and does not receive input events. This facilitates replacing the old technique of removing a view from its superview in order to "hide" it, while avoiding the loss of autoresizing functionality from which that approach has always suffered.
Hiding of views is controlled by the following new NSView methods:
- (void)setHidden:(BOOL)flag;To hide a view, you send it the message -setHidden:YES. AppKit will mark the area the view occupies in its superview as needing display, and since the view is now hidden it will not be drawn when the next drawing pass happens, so the view will disappear. Any cursor rects, tooltip rects, or tracking rects that the view owns will be disabled during the time the view is hidden.
- (BOOL)isHidden;
- (BOOL)isHiddenOrHasHiddenAncestor;
If a view that has subviews is hidden, its subviews and their descendants will be effectively hidden as well, with the same consequences applying to their cursor/tooltip/tracking rects and ability to receive input events. Note however that -isHidden only returns YES for a view that has itself been explicitly hidden via the -setHidden: API. To ask the broader question of whether a view has become effectively hidden, whether by being explicitly hidden itself or as a consequence of having an ancestor that is now hidden, send the view an -isHiddenOrHasHiddenAncestor message.
If a -setHidden:YES message causes the view that is the window's current firstResponder to become effectively hidden, the nextValidKeyView is made the new first responder. A hidden view remains in the nextKeyView chain it was previously a part of, but is ignored for purposes of keyboard navigation.
To restore a hidden view, send it the message -setHidden:NO. Unless the view remains effectively hidden due to having a hidden ancestor view, AppKit will cause it to again be shown, and will restore any cursor rects, tooltip rects, and tracking rects that it owns.
On MacOS X versions prior to Panther, if an NSView's bounds are changed (via one of the -setBounds...: methods), the receiving view's ability to autoresize subviews is permanently disabled. An example of this behavior can be seen in TextEdit on Jaguar. If you enable "Format-->Wrap To Page" and then set the magnification to anything (even 100%), the NSClipView's bounds are set. If you then convert back to "Wrap to Window" mode, the NSClipView no longer automatically resizes its subviews -- stretching the window will reveal that the NSTextView is not automatically resized.
When bounds are set on an NSView, it creates a transform matrix to adjust view drawing. Since this transform matrix can result in a rotated view, the rules for knowing how to autoresize a subview are unknown, thus NSView simply turns off that behavior.
For applications linked on Panther, this behavior has been adjusted so that if the transform matrix is effectively returned to the "identity" matrix (no warping) autoresize subviews behavior becomes enabled again.
You can ask a view if it should become the key view based on the current keyboard UI mode (all controls/text field only). You should not override this method. Use -[NSView acceptsFirstResponder] for that case.
- (BOOL)canBecomeKeyView;
NSView / NSCell - Focus Ring Drawing API
NSView and NSCell have introduced API which allows developers to control focus ring drawing. Specifically, you can disable a view's focus ring drawing by overriding -focusRingType, or calling -setFocusRingType: with NSFocusRingTypeNone. You should only disable a view from drawing its focus ring in limited situations. Typically you might do so because you want to draw your own focus ring, or because there isn't sufficient space to display a focus ring in the default location. This setting is archived in old and new style nibs.NSCell
If you set the object value of a cell with a class that responds to the selector -attributedStringValue, then the cell will use that method to fetch the string to draw rather than using -stringValue.Control tint numbers for the blue (NSBlueControlTint) and graphite (NSGraphiteControlTint) tints have been added to the NSControlTint enum. You can use these in conjunction with +[NSColor currentControlTint] to determine the color to render your custom controls.
NSMiniControlSize, a new size of control that is smaller than NSSmallControlSize has been added to the NSControlSize enum. NSCell and its subclasses and NSProgressIndicator, NSScroller, and NSTabView will accept this new size.
NSButtonCell
Calling -[NSButtonCell setImageDimsWhenDisabled:] would be ignored and the image would always render dimmed. It now checks the flag and will not dim the image if the flag is set to NO.Several new button bezel styles have been added:
NSTexturedSquareBezelStyle will get you a bezel style to use that is appropriate for textured (metal) windows.
NSDisclosureBezelStyle supports the disclosure triangle like the one in NSOutlineView. You can create the disclosure triangle by setting the button bezel style to NSDisclosureBezelStyle and the button type to NSOnOffButton.
NSHelpButtonBezelStyle has been added to provide the standard help button look.
NSTextFieldCell
New API for NSTextFieldCell allows you to specify a string to draw if the string value of the text field cell is empty and the text field cell isn't editing. This string never appears as the cell's string value but is used at the drawing stages if the actual string value is nil or @"". The plain text string will be drawn in grey. This string is not archived in the old nib format. Setting the attributed string clears out the plain text string and vice versa.@interface NSTextFieldCell
- (void)setPlaceholderString:(NSString*)string;
- (NSString*)placeholderString;
- (void)setPlaceholderAttributedString:(NSAttributedString*)string;
- (NSAttributedString*)placeholderAttributedString;
@end
NSScrollView
Beginning with Panther, an NSScrollView can be asked to automatically hide its scrollers when they are not needed. Off by default, this behavior can be controlled via the following new API:- (BOOL)autohidesScrollers;Because showing and hiding of its scrollers causes an NSScrollView to retile, use of this feature in contexts where another mechanism competes for control of the document view's size is not recommended. In particular, when a document view's enclosing NSScrollView is set to automatically hide its scrollers, the document view should not be set to autoresize. AppKit will avoid potential recursions that might arise as a result, but this may prevent scroller autohiding from working properly for such views.
- (void)setAutohidesScrollers:(BOOL)flag;
A previous empty implementation of -toggleRuler: has been removed from NSScrollView. The stub implementation, which was intended to help ensure compatibility for pre-Mac apps that used a deprecated ruler view class, was removed to prevent it from blocking the responder chain for apps that wished to handle this message.
When using an NSClipView within an NSScrollView (the usual configuration for using an NSClipView), developers should issue messages that control background drawing state to the NSScrollView, rather than messaging the NSClipView directly. This recommendation applies to the following messages:
- (void)setBackgroundColor:(NSColor *)color;Although NSClipView provides the same set of methods, they are intended primarily for when the NSClipView is used independently of a containing NSScrollView. In the usual case, NSScrollView should be allowed to manage the background-drawing properties of its associated NSClipView.
- (NSColor *)backgroundColor;
- (void)setDrawsBackground:(BOOL)flag;
- (BOOL)drawsBackground;
Previous documentation did not make this clear, but note that there is only one set of background-drawing state per NSScrollView/NSClipView pair. The two objects do not maintain independent and distinct drawsBackground and backgroundColor properties; rather, NSScrollView's accessors for these properties largely defer to the associated NSClipView and allow the NSClipView to maintain the state. In Jaguar and earlier system versions, it may have appeared that an NSScrollView and its NSClipView did maintain separate state for the drawsBackground property, since NSScrollView maintained a cache of the last state it set for its NSClipView that could become out of sync if the NSClipView was sent a setDrawsBackground: message directly. This caching of state has been removed in Panther. However, it remains important to note that sending a setDrawsBackground: message with a parameter of NO to an NSScrollView, rather than directly to its enclosed NSClipView, has the added effect of sending the NSClipView a setCopiesOnScroll: message with a parameter of NO (as documented). The side effect of omitting this step is the appearance of "trails" (vestiges of previous drawing) in the document view as it is scrolled.
Thus the general recommendation is to send requests pertaining to background drawing state to an NSClipView's enclosing NSScrollView (when present) and allow the NSScrollView to manage the NSClipView's state.
NSMovie
If you explicitly create an NSMovie using the -initWithMovie: method, DisposeMovie will no longer be called on the movie when the NSMovie is deallocated. Movies created via a URL or pasteboard will still be disposed. This change will only affect applications compiled in Panther or later.NSStatusItem
For standard status bar items, you can now set an alternate image that is displayed when the item is highlighted on mouse tracking.- (void)setAlternateImage:(NSImage*)image;For custom view status bar items, there are two new methods to help emulate standard items. The first method will draw the menu background pattern in the status item custom view in regular or highlight pattern.
- (NSImage*)alternateImage;
- (void)drawStatusBarBackgroundInRect:(NSRect)rect withHighlight:(BOOL)highlight;This will display a menu under the custom status item.
- (void)popUpStatusItemMenu:(NSMenu*)menu;
NSOpenGLContext, NSOpenGLPixelFormat
NSOpenGLContext and NSOpenGLPixelFormat have new accessors that you can invoke to obtain the underlying CGL objects. You can then use the CGL API to work with these objects directly.NSOpenGLContext provides:
- (void *)CGLContextObj; /* cast the return value to a CGLContextObj */Similarly, NSOpenGLPixelFormat adds the accessor:
- (void *)CGLPixelFormatObj; /* cast the return value to a CGLPixelFormatObj */
NSWorkspace
NSWorkspace now has API for opening files and launching applications with more LS launch options. The following API has been added:- (BOOL)launchAppWithBundleIdentifier:(NSString *)bundleIdentifier
options:(NSWorkspaceLaunchOptions)options
additionalEventParamDescriptor:(NSAppleEventDescriptor *)descriptor
launchIdentifier:(NSNumber **)identifier;
- (BOOL)openURLs:(NSArray *)urlsThe NSWorkspaceLaunchOptions can be found in NSWorkspace.h. In addition, NSWorkspace now has API to get the absolute path from a bundle identifier:
withAppBundleIdentifier:(NSString *)bundleIdentifier
options:(NSWorkspaceLaunchOptions)options
additionalEventParamDescriptor:(NSAppleEventDescriptor *)descriptor
launchIdentifiers:(NSArray **)identifiers;
- (NSString *)absolutePathForAppBundleWithIdentifier:(NSString *)bundleIdentifer;In the NSWorkspaceDidLaunchApplicationNotification, a new constant NSApplicationBundleIdentifier has been added.
NSWorkspace now provides sleep notifications. NSWorkspaceWillSleepNotification and NSWorkspaceDidWakeNotification will be sent before the machine sleeps and after the machine wakes, respectively. An observer of NSWorkspaceWillSleepNotification can delay sleep for up to 30 seconds within the handling of the notification.
We added NSWorkspaceSessionDidBecomeActiveNotification and NSWorkspaceSessionDidResignActiveNotification notifications to NSWorkspace, for applications that need to be aware of session switching (aka "fast user switching"). For example, an application may decide to disable some processing when its user session is switched out, and reenable when that session gets switched back in. Such an application should register for these notifications.
Additionally, if an application is launched in an inactive session and registers for these notifications, NSWorkspace sends the NSWorkspaceSessionDidResignActiveNotification after sending NSApplicationWillFinishLaunching and before sending NSApplicationDidFinishLaunching.
NSProgressIndicator
We have added a new larger sized spinning progress indicator that corresponds to the NSRegularControlSize. The original smaller sized indicator now corresponds to NSSmallControlSize. If your application is linked against anything before Panther, we will always return the smaller size in order to ensure binary compatibility.NSPasteboard
NSPasteboard has automatically made "NeXT plain ascii pasteboard type" data available when Carbon applications have put 'TEXT' on a pasteboard, despite the fact that use of this type has not been publicly supported since before Mac OS 10.0. NSPasteboard no longer ensures that this type is available on pasteboards. Use NSStringPboardType instead.In Mac OS 10.2, NSPasteboard began to automatically provide Carbon kScrapFlavorTypeUnicode ('utxt') and kScrapFlavorTypeUnicodeStyle ('ustl') data when NSRTFPboardType items were put on the pasteboard. The 'utxt' that NSPasteboard provided always began with a Unicode byte order mark (BOM), which is valid. Unfortunately, many Carbon applications were not able to properly handle the BOM. Starting with 10.2.3, the 'utxt' that NSPasteboard automatically provides no longer begins with a BOM. However, because providing the BOM is ultimately the right thing to do, we do intend to bring it back in a future release.
NSPrintInfo
In Mac OS 10.2, -[NSPrintInfo setPaperSize:] began silently ignoring any paper size that didn't match any of the paper sizes supported by the current selected printer. This bug was fixed 10.2.3.In Mac OS 10.2, -[NSPrintInfo setPaperSize:] began silently ignoring any paper size that was the rotated variant of a paper size supported by the current selected printer. This bug was fixed 10.2.3.
The string returned by -[NSPrintInfo paperName] is usually not suitable for presenting to the user. For example, "na-letter" is very often returned when the user has specified "Letter" in an NSPageLayout. Because some applications needs to present paper names to the user, a new method has been added to NSPrintInfo:
- (NSString *)localizedPaperName;
NSDocument
-[NSDocument lastComponentOfFileName] now returns the exact same value that -displayName would. A valid value for a document's "name" property is now always returned to scripts.Name specifiers are now the default object specifier form for documents, instead of index specifiers. Index specifiers were not appropriate in many situations because the indices of documents in an application are so likely to change during the execution of a script that manipulates documents.
-[NSDocumentController openDocumentWithContentsOfFile:display:] is now invoked with a display: argument of YES instead of NO when a Print Documents Apple event is handled. For documents that present the print panel as a sheet (document-modally) there must be a visible document window to which the sheet can be attached. For documents that present the print panel as a dialog box (application-modally) the document window provides an indication of what will be printed when the user presses the OK button in the print panel.
In previous versions of Cocoa there was no easy public way to control which window of a multiwindow document should be used as the parent window of sheets presented by that document. Because there is a common need for this, a new method has been added to NSDocument:
- (NSWindow *)windowForSheet;In previous versions of Cocoa the numbering of untitled documents was done in such a way that it was common for unnecessarily large numbers to appear in the display names of untitled documents, e.g. "Untitled 78." NSDocument now names untitled documents in such a way that each new untitled document is given a number that is one larger than the largest number used in any currently open, untitled, document.
In previous versions of Cocoa -[NSDocument writeWithBackupToFile:ofType:saveOperation:] would fail when passed a document path string that contained colons. Because colons in POSIX-style paths are by convention the result of the user typing a slash in the user interface (either that of the app itself or the Finder), this bug would appear to the user as a failure to save documents whose names contained slashes. This has been fixed.
NSWindow
A local autorelease pool will now be installed and released during live resize for applications built on Panther or later. This allows autoreleased objects to be released during live resize, which improves memory usage but also changes the lifetime of some objects. This change is limited to applications built on Panther or later for compatibility reasons.We have introduced a delay for dragging from the document icon button. If you click and drag on the document icon button without pausing, the window will move. If you click and pause for 1/8 second, the document icon button will become a drag source. The document icon button will be highlighted to indicate when the drag delay has been satisfied. Additionally, the restriction preventing shift-drags from the document icon button has been lifted. The shift modifier is now ignored for document icon button drags.
Functionality of -setMiniWindowImage: has been reenabled for applications linked on Panther or later. Previously, for compatibility reasons, a special default was needed to enable this.
We now have a method to allow a window delegate to specify a custom sheet location. Most customization involves just the vertical offset , but some apps also want to control the horizontal location in the window. Some apps might want to control the width of the start of the sheet effect as well. For example it might make sense to make a sheet appear as if it was originating from a button or other widget. Note that this is typically a property of the parent window, but may also be dependent on the type of sheet being presented.
@interface NSObject(NSWindowDelegate)This method will be sent to the window's delegate before first animating the sheet, as well as any time window is resized while sheet is attached. The returned NSRect indicates the line at which the top edge of the sheet should be attached to the window, with the origin in window coordinates and the rect.size.width indicating the width of the initial animation. If rect.size.width is wider than the sheet, the sheet will slide out . If the rect.size.width is narrower than the sheet, the sheet will genie out from the given rect. The top of the sheet will be centered within the given rect. Note that rect.size.height will not currently impact that size of the animation, but will be used to determine the center of the rect.
- (NSRect)window:(NSWindow *)window willPositionSheet:(NSWindow *)sheet usingRect:(NSRect)rect;
...
@end
NSWindow now has methods that do size computations in terms of content rather than frame metrics. This allows specification of window content size which is especially useful in windows which may have a toolbar visible.
- (NSRect)frameRectForContentRect:(NSRect)contentRectThe following methods are preferred over the frame-based methods, but the frame-based methods will continue to work if the content-based methods are not used:
- (NSRect)contentRectForFrameRect:(NSRect)frameRect
- (void)setContentMaxSize:(NSSize)aSizeThe above method sets the maximum size to which the receiver's contentView frame can be sized to aSize. The maximum size constraint is enforced for resizing by the user as well as for the setFrame... methods other than setFrame:display:. Takes precedence over setMaxSize:.
- (NSSize)contentMaxSizeReturns the maximum size of the receiver's contentView frame.
- (void)setContentMinSize:(NSSize)aSizeSets the minimum size to which the receiver's contentView frame can be sized to aSize. The minimum size constraint is enforced for resizing by the user as well as for the setFrame... methods other than setFrame:display:. Takes precedence over setMinSize:.
- (NSSize)contentMinSizeReturns the minimum size of the receiver's contentView frame.
- (void)setContentAspectRatio:(NSSize )ratioSets the receiver's content size aspect ratio to ratio, constraining the size of its content rectangle to integral multiples of this size when the user resizes it. You can set the NSWindow's size to any ratio programmatically. Takes precedence over -setAspectRatio:.
- (NSSize)contentAspectRatioReturns the receiver's content size aspect ratio.
- (void)setContentResizeIncrements:(NSSize)incrementsSets the receiver's resize increments to increments, constraining the width and height of the content rectangle to change by integral multiples of increments.width and increments.height when the user resizes it. You can set the NSWindow's size to any width and height programmatically. Takes precedence over -setResizeIncrements:.
- (NSSize)contentResizeIncrementsThe above method returns the receiver's content resize increments.
The default time for sheet and window resize animation has been reduced, so the effects will occur more quickly. If you need to customize this animation to be slower (or faster) in a window subclass, you can override -animationResizeTime: and tweak the value obtained from the super.
We fixed -setIgnoresMouseEvents: so that it works more reliably for opaque windows that want to be transparent to mouse events, and also works for transparent windows that want to receive mouse events. In Jaguar, this API only somewhat worked for ignoring events, and did not work at all for receiving mouse events in transparent windows. This fix applies only to applications built on Panther or later.
We no longer ignore NSMiniaturizableWindowMask for nonActivating utility windows. If this styleMask is set on a nonActivating utility window in an application built on Panther or later, that window will get a minimize button. We will continue to prohibit a minimize button on other types of utility windows.
On Jaguar and previous systems, -[NSPanel orderFront:] would behave like -orderFrontRegardless:, meaning that an NSPanel would be brought to the front of its window level whether or not its owning application was active. In Panther, we have removed this distinction for non-modal NSPanels. NSPanel and NSWindow now behave the same in that orderFront: will order the panel or window behind the active window if the active window belongs to another application. For compatibility, this change applies only to applications built on Panther or later.
NSEvent
NSEvent instance methods now check that instance methods are sent to valid event types, for applications built on Panther or later. This is as documented, but has not previously been enforced. A method sent to the wrong event type will cause an exception to be raised.The delivery rule for NSScrollWheel events has been changed so that NSScrollWheel events are sent to the view under the mouse in a key or utility window. NSScrollWheel events are dropped if the mouse is not over a key or utility window. NSScrollWheel events may still be passed up the responder chain from the receiving view.In 10.2, the window server started setting an additional device-dependent modifier flag in user input events to indicate when event coalescing is disabled. Applications that check for modifierFlags using equality without masking off the device-dependent modifierFlags first can get unexpected behavior due to this change. In order to maintain compatibility for applications built on Jaguar and earlier systems, NSEvent by default strips this device-dependent modifier flag when creating the event from a windowServer event. For applications built on Panther or later systems, this will no longer be the default behavior. In order to override NSEvent's default behavior, an application can specify a value for the user default NSDeviceDependentModifierFlags. If this default is set to NO, the device-dependent modifier flags will be stripped.
NSApplication
We now ignore the user default NSSuppressReopenAppleEvent for applications built on Panther or later. This default was originally added as a workaround for applications that weren't ready to handle the 'rapp' event, but we would not expect any current applications to need this default.New delegate methods have been added for opening or printing multiple files at once:
@interface NSObject(NSApplicationDelegate)If the application delegate implements application:openFiles:, this method will be called instead of application:openFile:. Similarly, application:printFiles: will be called instead of application:printFile:.
...
-(void)application:(NSApplication *)app openFiles:(NSArray *)fileList
-(void)application:(NSApplication *)app printFiles:(NSArray *)fileList
...
@end
Note that the delegate may need to present one or more sheets while handling these methods. There may be error conditions as well. It is the delegate's responsibility to notify the user through an error panel or other means if appropriate. The delegate will also be responsible for indicating failure or user cancel to the sender of the AppleEvent which caused one of these methods to be sent. In order to make replying to the AppleEvent easy, we have provided a wrapper in NSApplication:
typedef enum NSApplicationDelegateReply {
NSApplicationDelegateReplySuccess = 0,
NSApplicationDelegateReplyCancel = 1,
NSApplicationDelegateReplyFailure = 2
} NSApplicationDelegateReply;
-(void)replyToOpenOrPrint:(NSApplicationDelegateReply)replyThe delegate must call this method in order to get the kit to manage error reporting in the reply AppleEvent. If this method is called with NSApplicationDelegateReplyCancel , userCanceledErr will be set in the reply AppleEvent. If this method is called with NSApplicationDelegateReplyFailure, errAEEventFailed is set in the reply AppleEvent. The delegate may also call this method to report success, although the current implementation does not do anything in response to this. Delegates that want to manage the Apple Event reply themselves should do so using NSAppleEventManager API, and should not call this method.
In 10.3, if you pass nil to -setApplicationIconImage:, we will now set the dock icon to the default image for the application. Previously this behavior was undefined.
The AppleEvent handler for the 'quit' event will now return userCanceledErr if there is an application modal panel open. -applicationShouldTerminate will not be invoked on the application delegate in this case. One consequence of this change is that an application with a modal panel open will now be reported as canceling logout. On previous versions of the system, such an application would block logout until the modal panel was dismissed.
System Preferences allows users to add applications to the Startup Items list, and to launch those applications hidden. In Panther, and since the WWDC seed, NSApplication now enforces the hidden-on-launch setting by adding windows ordered onscreen during application launch to a hidden list. This includes any non-modal windows ordered onscreen up to and including the time when -applicationDidFinishLaunching: is sent. Windows ordered onscreen after this time will cause the application to get unhidden. In order to maintain binary compatibility with applications that may not expect this behavior, the hidden-on-launch setting is only enforced for applications built on Panther or later. Applications built on earlier systems will probably see the old behavior, where a window shown during launch will temporarily flash onscreen, then get hidden with the app.
A new API -[NSApplication orderFrontCharacterPalette:] is added.
AppKit now adds "Special Characters..." menu item to the "Edit" menu if an item with -orderFrontCharacterPalette: action is not found in the menu. To prevent the behavior, you can set the NSDisabledCharacterPaletteMenuItem preference setting to YES.
NSBox
We no longer archive or unarchive the contentView for an NSBoxSeparator for keyed coding. The contentView has no purpose for an NSBoxSeparator.In 10.3 we defer sizing the NSBox after unarchiving until it is first drawn. This means that subviews that resize based on the box size can get resized later than they did on Jaguar and previous systems.
10.3 provides a new appearance for NSBox. Box types of NSBoxPrimary and NSBoxSecondary will both draw with an indented look, enhanced but similar in some ways to how NSBoxSecondary boxes drew on Jaguar. If your application needs a simple outline look, you should use an NSBoxType of NSBoxOldStyle and an NSBorderType of NSLineBorder. It is recommended that you use this only for boxes without titles.
Dragging
A local autorelease pool is now installed and released around NSDraggingDestination method invocations, for applications built on Panther or later. This allows autoreleased objects to be released during dragging, which improves memory usage but also changes the lifetime of some objects. This change is limited to applications built on Panther or later for compatibility reasons.A bug in generating the drag image would result in a flipped image (isFlipped: set) appearing unflipped if the source was cached (i.e. not a bitmap). The image will now appear in the correct orientation. This change will only affect applications compiled in Panther or later.
NSCursor
Applications built on Panther or later can now use cursor images of sizes other than 16x16. The cursor size will be defined by the size of the image passed to -initWithImage:hotSpot: or -initWithImage:foregroundColorHint:backgroundColorHint:hotSpot:. Applications can check for an NSAppKitVersionNumber greater than or equal to NSAppKitVersionNumberWithCursorSizeSupport to determine whether this support is available.NSCursor now provides new cursors for alias and copy drags, as well as slightly modified cursors for vertical and horizontal resize. We've also added additional public cursors:
+ (NSCursor *)pointingHandCursor;
+ (NSCursor *)closedHandCursor;
+ (NSCursor *)openHandCursor;
+ (NSCursor *)resizeLeftCursor;
+ (NSCursor *)resizeRightCursor;
+ (NSCursor *)resizeLeftRightCursor;
+ (NSCursor *)resizeUpCursor;
+ (NSCursor *)resizeDownCursor;
+ (NSCursor *)resizeUpDownCursor;
+ (NSCursor *)crosshairCursor;
+ (NSCursor *)disappearingItemCursor;
NSGraphics
The following functions have been added to disable and reenable all drawing to the screen by the calling context:void NSDisableScreenUpdates(void);Prevents drawing for all windows belonging to the calling process from being flushed to the screen. This function permits operations on multiple windows to appear atomic to the user, and is particularly useful for parent and child windows. Note that this function should be used with care for short operations only as the system will only allow updates to be disabled for a short time (currently one second) before automatically reenabling updates.
void NSEnableScreenUpdates(void);Reenables drawing for all windows belonging to the calling process. Calls to NSDisableScreenUpdates must be matched with calls to NSEnableScreenUpdates. Multiple calls stack.
NSHelpManager
We now have methods to support help lookup other than from the Help Menu. The following methods support navigation to a specific location in a help book, or querying a help book for a string when a Help button is pressed, for example.- (void)openHelpAnchor:(NSString *)anchor inBook:(NSString *)bookFind and display the text at the given anchor location in the given book. The argument should be a localized help book name or nil --- if nil, all installed help books are searched. This is a wrapper for AHRegisterHelpBook (which is called only once to register the help book specified in the application's main bundle) and AHLookupAnchor.
- (void)findString:(NSString *)query inBook:(NSString *)bookSearch for the given query in the given book. The argument should be a localized help book name or nil --- if nil, all installed help books are searched. This is a wrapper for AHRegisterHelpBook (which is called only once to register the help book specified in the application's main bundle) and AHSearch.
Accessibility
The AXWindows attribute of the application UI Element now returns the list of window in z-order, front-to-back.AXWindows now support a raise action which simulates bringing a window forward by clicking on its title bar.
NSAccessibilityRaiseActionAXWindows now support subroles to distinguish different types of windows.
NSAccessibilityStandardWindowSubroleAXWindow's title attribute used to include the path name for windows with represented files (as per [NSWindow title]). This has now been fixed so they only return the displayed title. You should access the represented file with the AXDocument attribute.
NSAccessibilityDialogSubrole
NSAccessibilitySystemDialogSubrole
NSAccessibilityUnknownSubrole
NSAccessibilityFloatingWindowSubrole
NSAccessibilitySystemFloatingWindowSubrole
AXWindows now support attributes to identify their default and cancel buttons.
NSAccessibilityDefaultButtonAttributeWindows now emit notifications when AXDrawers and AXSheets are created.
NSAccessibilityCancelButtonAttribute
NSAccessibilityDrawerCreatedNotificationAXScrollBars now expose their arrows and page up/down areas as AXButtons with subroles.
NSAccessibilitySheetCreatedNotification
NSAccessibilityIncrementArrowSubrole
NSAccessibilityDecrementArrowSubrole
NSAccessibilityIncrementPageSubrole
NSAccessibilityDecrementPageSubrole
10.3 provides more powerful text accessibility features. A number of new attributes were added for AXTextAreas and AXTextFields in order to return more detailed information about the text within. These additions are documented in the Accessibility APIs.
Two new methods were added to the Cocoa accessibility protocol in order to support parameterized attributes – one to return a list of them and one to return their values.
- (NSArray *)accessibilityParameterizedAttributeNames;A new function was added so you can raise an error if the parameter is the wrong type or has an illegal value. This function can also be used to raise an error if an attempt is made to set an attribute's value with the wrong type or an illegal value.
- (id)accessibilityAttributeValue:(NSString *)attribute forParameter:(id)parameter;
void NSAccessibilityRaiseBadArgumentException(id element, NSString *attribute, id value);The following constants have been removed:
NSAccessibilityWindowTitleRoleThe following constants have been added:
NSAccessibilityWindowProxyRole
NSAccessibilityRelevanceIndicatorRole
NSAccessibilityCancelAction
NSSound
NSSound uses CoreAudio to play most files in 10.3, but uses QuickTime to play some sound formats, like iTunes Music Store sounds and general URLs. In 10.2, NSSound primarily used QuickTime to play sounds.Unfortunately, the underlying requirements and limitations of the underlying frameworks bleed through to NSSound, and affect developers trying to use NSSound to play sounds on multiple threads. If your app is single-threaded, or you only play sounds on the main thread, then you need not worry about these issues.
Since QuickTime requires the run loop to be run for sound (or movies) to continue playing, NSSound requires this as well. By "run loop", what is meant is the run loop on the thread which starts the sound playing. For Cocoa applications, this is not a big deal for sounds played on the main thread, as Cocoa apps normally let the AppKit handle running the run loop on the main thread. Also, since QuickTime is not particularly thread-safe, it is not recommended to play sounds (or movies) on multiple threads.
The delegate -sound:didFinishPlaying: is also sent on the main thread, when it is automatically triggered by the sound finishing. However, when -stop is called it is sent immediately on that thread calling -stop. So unless one does all sound playing and stopping on the main thread, one cannot assume that the delegate method is being called on the main thread.
Doing all sound playing on the main thread will also insulate you better from future changes in the multi-threaded behavior of NSSound and the underlying frameworks.
Playing an NSSound more than once simultaneously
A given NSSound instance can only be playing, or not. If a sound is playing, and -play is called, nothing will happen. This was true in 10.2, and is still true in 10.3, and will be true in the future. However, a different NSSound instance can play the same sound file. So if you want the same sound potentially played overlapping, make a copy of the NSSound and play the copy:[[[mySound copy] autorelease] play];
NSBrowser
NSBrowser scrolling now provides immediate and continuous feedback. This of course provides a much better user experience, but also required some major changes. In particular, the internal view hierarchy had to change. So, if you have subclasses of NSBrowser or NSBrowserCell which depend on the internal view hierarchy, you should be on the look out for incompatibilities. In Panther, continuous scrolling is on by default for all applications. However, if necessary, you can revert to the old implementation by setting the NSBrowserSupportsContinuousScrolling default to NO. The old, non-continuous scrolling implementation remains for compatibility and will be removed in the next release.The implementation of continuous scrolling has the following implications:
The internal view hierarchy has changed. However, the public API dealing in browser geometry still returns values in browser coordinates. For example, -titleFrameOfColumn:, and -frameOfColumn: return rectangles relative to the NSBrowser.
Overriding NSBrowser's -drawRect: method is no longer very useful (due to the new view hierarchy).
The -drawTitleOfColumn:inRect: method's 'inRect' parameter is in the coordinates of the view that draws the titles. You do not need to do any conversion in order to draw.
The size of a browser's columns may now vary by one pixel. This happens when the available space for column tilling cannot be evenly divided by the number of visible columns.
The usage of the delegate methods -browserWillScroll: and -browserDidScroll: require some explanation. The non-continuous implementation sent these delegate messages each time a new column became visible. However, with continuous scrolling, partial columns can now be visible during scrolling. The continuous scrolling implementation uses these methods before and after scrolling has completed. So, these methods are no longer visibility notifications.
The use of -scrollViaScroller: is deprecated. This method no longer does anything when using the continuous scrolling implementation
The use of -updateScroller is deprecated. This method no longer does anything when using the continuous scrolling implementation.
Note, one can check for the support of the continuous scrolling by comparing the appkit version number against NSAppKitVersionNumberWithContinuousScrollingBrowser.
NSBrowser now provides three different column resizing modes: NSBrowserNoColumnResizing, NSBrowserAutoColumnResizing, and NSBrowserUserColumnResizing. For many applications auto resizing will be sufficient. In auto resizing, each column is given the same width, which is calculated using a combinination of the minimum column width, and max visible columns setting. Some applicaitons may decide that they know how to size columns better than NSBrowser, or the user, and will choose to use no column resizing. In NSBrowserNoColumnResizing mode, the developer explicitly will determine the width of every column, and neither the user, nor NSBrowser will change the assigned width. Finally, NSBrowser allows a column resizing mode that allows developers to choose the initial column width, while allowing users the option to resize the columns much like they can in the Finder.
NSBrowsers user column resizing mode (NSBrowserUserColumnResizing), and no column resizing mode (NSBrowserNoColumnResizing) deserve a little further explanation since they are new. Essentially auto column resizing is the same as the old browser behavior. A number of new methods and delegate methods have been introduced to support the two new modes. First of all, there is new API to specify the type of column resizing:
typedef enum _NSBrowserColumnResizingType {
NSBrowserNoColumnResizing = 0, /* Column sizes are fixed and set by developer. */
NSBrowserAutoColumnResizing = 1, /* No user resizing. Columns grow as window grows. */
NSBrowserUserColumnResizing = 2 /* Columns fixed as window grows. User can resize. */
} NSBrowserColumnResizingType;
- (void)setColumnResizingType:(NSBrowserColumnResizingType)columnResizingType;
- (NSBrowserColumnResizingType)columnResizingType;
- (void)setPrefersAllColumnUserResizing:(BOOL)prefersAllColumnResizing;Next, there is new API and delegate methods that allow you to declare the initial size for a newly visited browser column. There is also API that allows you to directly set the width of a column:
- (BOOL)prefersAllColumnUserResizing;
- (void)setWidth:(float)columnWidth ofColumn:(int)columnIndex;
- (float)widthOfColumn:(int)column;
@interface NSObject (NSBrowserDelegate)
- (float)browser:(NSBrowser *)browser
shouldSizeColumn:(int)columnIndex
forUserResize:(BOOL)forUserResize
toWidth:(float)suggestedWidth;
- (float)browser:(NSBrowser *)browser
sizeToFitWidthOfColumn:(int)columnIndex;
@endFinally, there is new API to do simple column width persistence, and new notifications that allow you to do more sophisticated path based column width persistence:
- (void)setColumnsAutosaveName:(NSString *)name;
- (NSString *)columnsAutosaveName;
+ (void)removeSavedColumnsWithAutosaveName:(NSString *)name;
APPKIT_EXTERN NSString * NSBrowserColumnConfigurationDidChangeNotification AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER;
// Object : browser - the browser whose column sizes need to be persisted.
// UserInfo : No user info.
@interface NSObject (NSBrowserDelegate)
- (void)browserColumnConfigurationDidChange:(NSNotification *)notification;
@end
NSBrowser no longer loses focus during a reloadColumn:, or loadColumnZero.
Performing a selectAll: on a matrix in a b