Developer Release Notes
Cocoa Application Framework (macOS 10.12.2 and Earlier)
This document contains the AppKit release notes for macOS 10.12.2 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 Software Update macOS 10.12.2 of Sierra
- Notes specific to Software Update macOS 10.12.1 of Sierra
- Notes specific to macOS 10.12 Sierra
- Notes specific to OS X 10.11 El Capitan
- Notes specific to Software Update OS X 10.10.3 of Yosemite
- Notes specific to OS X 10.10 Yosemite
- Notes specific to OS X 10.9 Mavericks
- Notes specific to OS X 10.8 Mountain Lion
- Notes specific to Mac OS X 10.7 Lion
- Notes specific to Mac OS X 10.6 Snow Leopard
- Notes specific to Mac OS X 10.5 Leopard
- Notes specific to Mac OS X 10.4 Tiger
- Notes specific to Mac OS X 10.3 Panther
- Notes specific to Mac OS X 10.2.5
- Notes specific to Mac OS X 10.2.3
- Notes specific to Mac OS X 10.2 Jaguar
- Notes specific to Mac OS X 10.1 Puma
- Notes specific to Mac OS X 10.0 Cheetah
- Notes specific to Mac OS X Public Beta
- Notes specific to Mac OS X Developer Preview 4
- Notes specific to Mac OS X Developer Preview 3
- Notes specific to Mac OS X Developer Preview 2
- Notes specific to Mac OS X Developer Preview 1
- Notes specific to Mac OS X Server
- Notes specific to Mac OS X Server Developer Release 2
- Notes specific to Mac OS X Server Developer Release 1
- Summary of OpenStep 4.x notes
Notes specific to macOS 10.12.2
Support for NSTouchBar
We introduced a version of macOS 10.12.1 with API to support the Touch Bar. There was also a version of macOS 10.12.1 that did not include support for the Touch Bar. In order to simplify the API story going forward, we have marked all NSTouchBar related API as available on 10.12.2 and later.Please see the documentation for detailed information on how to use these classes, and see notes below for additional guidance and known issues.
Support for replacing the escape key in NSTouchBar
In MacOS 10.12.2 the NSTouchBar class has a new property – escapeKeyReplacementItemIdentifier. When non-nil the item with the specified identifier replaces the normal system provided escape key. This item should be provided by the delegate in the touchBar:makeItemForIdentifier: method, or located inside the NSTouchBar's templateItems set. For consistency with the rest of the system, the item should generally be a button, and should be used for canceling operations, or operations that can be undone.Buttons in the esc key region will be automatically sized to the standard esc key width, and will only grow wider if their content requires it. In order to allow as much content as possible, they have modified metrics to reduce their inner padding. However, you should consider the space limitation when choosing localized strings in order to allow the button to stay at the standard width.
The escapeKeyReplacementItemIdentifier should be unique and not present in the normal defaultItemIdentifiers array.
If multiple NSTouchBar instances have replacement escape key items, the NSTouchBar provided closest to the first responder wins.
NSSliders in NSPopoverTouchBarItems
NSPopoverTouchBarItems that support press-and-hold and contain NSSliderTouchBarItems now have a one second delay before being dismissed after a press-and-hold gesture finishes. During that one second, interaction with the slider can be resumed and further delay the automatic dismissal of the popover.Control appearance during NSTouchBar customization
Customization will now show the stateless appearance of NSTouchBarItem controls in both the on screen palette and the bar preview. This means that controls will not show their disabled, highlighted, or selection state while the bar is being customized.NSCollectionViewFlowLayout fix
macOS 10.12 introduced an unintentional change in the way items are aligned in an incomplete (non-full) solitary row. macOS 10.12.2 restores the alignment behavior from previous releases, where items in a single row are positioned the same as in the last row of a multi-row flow layout.NSInspectorBar fix
32-bit apps running on 10.12.0 and 10.12.1 would crash after enabling NSTextView’s formatting bar (by setting "usesInspectorBar" to YES). This is fixed with 10.12.2.Notes specific to macOS 10.12.1
Context Transition Animations in the Touch Bar
AppKit will animate changes to which NSTouchBarItems are presented to the user any time a change is made to an NSTouchBar or the responder chain changes.In order to avoid unnecessary animation, AppKit will notice when items have the same identifier and frame and will not animate those changes. In order to achieve this result, use the same itemIdentifier between NSTouchBarItem instances that represent the same object to the user in different instances of NSTouchBar. If the responder chain changes such that NSTouchBar 'A' is being replaced by NSTouchBar 'B' and they both include a button with the title 'Cancel' the same itemIdentifier should be used for both items. AppKit will notice NSTouchBarItem instances that have the same itemIdentifier and the same frame and will not animate them even though the instances are changing.
It is important to note, however, that if the frame of the item being added is different from the frame of the item being removed, AppKit will still animate the change even if the itemIdentifiers are the same. If possible, avoid changes that would cause the frame of these items to change in between presentations.
NSTouchBar Support in Alerts
NSAlerts will automatically mirror buttons into the Touch Bar. The buttons presented in the bar will call -performClick: on the corresponding buttons in the NSAlert when tapped.Custom windows presented as sheets or as modal windows to the user will need to add this behavior manually as AppKit will not automatically add these buttons to the bar.
Principal Items in NSTouchBar
The principal item of an NSTouchBar will try to occupy the same amount of space as the QuickType item. If a principal NSTouchBarItem can not occupy the width of QuickType, it will be sized as close to that width as possible.Minimum and Maximum Size of NSTouchBarItems
AppKit will measure the minimum and maximum sizes of NSTouchBarItems at runtime. AppKit will cache these measured values and recalculate when necessary. Both the minimum and maximum sizes are measured at a priority of 200.An NSTouchBarItem's view should use auto-layout when possible to express the sizes which the view is capable of occupying. Use constraints with priorities higher than 200 to express minimum and maximum thresholds. AppKit will notice if a constraint is changed and recalculate the minimum and maximum widths of the view.
When using intrinsicContentSize, call -invalidateIntrinsicContentSize when something changes that affects the intrinsicContentSize. Otherwise, AppKit will not notice the change and will not measure the new minimum and maximum sizes of the NSTouchBarItem's view.
In general, NSTouchBarItem's views should not have an opinion about their width outside of their minimum and maximum sizes. AppKit will decide how much space a given view should occupy within the minimum and maximum size of a view relative to what else is in the Touch Bar.
NSTouchBar and Right to Left support
NSTouchBar does not currently support right to left specific behaviors at the top level. This means that AppKit will not automatically flip NSTouchBarItems in a right to left localization.Please do not manually reorder defaultItemIdentifiers as this will break user customized bars. Instead, try to encapsulate views that absolutely must flip inside of single views such as an NSStackView. NSView subclasses will continue to flip in right to left localizations when using auto-layout based on the NSUserInterfaceLayoutDirection. In general, anything beyond encapsulating views inside of a single NSView should be avoided as AppKit support may change in the future.
Segmented Control minimum size
Beginning with macOS 10.12.1, NSSegmentedControls will allow their titles to be easily compressed if they also have images. In this case, the default compression resistance drops to 100, and a "minimum size" constraint is applied with a priority of 750 to prevent clipping of the images or bezel. If you want your segmented control to be harder to compress, it will be necessary to set a compression resistance priority larger than this default.Touch Events and Event Routing
The Touch Bar produces direct touch events that contain a set of NSTouches. These events are similar to the indirect touch events that are produced by Magic Trackpads, but there are some differences you should be aware of. For example, direct touch events are routed much like on iOS. Each Direct touch is routed to its hit tested view and may also be sent to gesture recognizers. See the section on NSTouch for more details.Like indirect touches, each direct touch event and resulting NSTouch is an immutable snapshot of the event or touch at that instant. This is different from iOS where the same UITouch instance is reused for the duration of that touch.
Direct touch events are noted by the new event type, NSEventTypeDirectTouch. While there is a corresponding NSEventMaskDirectTouch, you cannot acquire direct touch events via -nextEventMatchingMask: or similar tracking methods. Direct Touches don’t flow through -sendEvent: either. Instead, direct touch events are routed upon receipt by the application. Important Note: This means that direct touch events are routed even when your app is in a tracking loop.
Direct touch events are coalesced. That is, all direct touch events in the event queue since the last touch event delivery are combined into a single event. The following function allows you to get the individual touches that make up the coalesced touch when needed.
/* An array of auxiliary NSTouch’s for the touch events that did not get delivered for a given main touch.NSView has been updated for direct touches as well. Notably, the acceptsTouchEvents property has been deprecated in favor of the new allowedTouchTypes property. The allowed touch types can be either empty, .indirect, .direct or both. For pre-10.12 built application, the default is empty. For applications built on 10.12 or later, the default is .direct. Your custom view can receive touch events by overriding the following responder functions:
This also includes an auxiliary version of the main touch itself. Only valid for NSEventTypeDirectTouch events.*/
public func coalescedTouches(for touch: NSTouch) -> [NSTouch]
public func touchesBegan(with event: NSEvent)
public func touchesMoved(with event: NSEvent)
public func touchesEnded(with event: NSEvent)
public func touchesCancelled(with event: NSEvent)
NSTouch
Direct touches are exposed as the NSTouch object. New properties and functions have been added to handle the differences between a direct touch from the Touch Bar and and indirect touch from a trackpad. You can ask the touch for its type so you know which functions are available to you.public enum NSTouchType : Int {
case direct // A direct touch from a finger (on a screen)
case indirect // An indirect touch (not a screen)
}
public var type: NSTouchType { get }Direct touches do not have a normalizedPosition in the digitizer space. Instead their position is in view coordinates.
public func location(in view: NSView?) -> NSPointAppKit will throw an exception if you attempt to acquire the wrong type of position for the type of touch.
public func previousLocation(in view: NSView?) -> NSPoint
As NSTouch objects are immutable, use the .identity property to track a specific touch throughout its life. Use the isEqual function to compare two touch identities.
Currently, the vertical position of all touches on the Touch Bar is generated from approximately the vertical center of the digitizer. As such, there is no vertical movement in touches in the current incarnation.
NSGestureRecognizer
Direct touch support has been added to most AppKit gesture recognizers. The complete list is: NSClickGestureRecognizer, NSPressGestureRecognizer, NSPanGestureRecognizer and NSMagnificationGestureRecognizer. By default, gesture recognizers do not receive touches. You can enable this by setting the new allowedTouchTypes property to .direct. Note: at this time, gesture recognizers still do not receive indirect touches. In addition, with the exception of NSMagnificationGestureRecognizer, you must set the numberOfTouchesRequired to a non zero value.NSGesture recognizer subclassers can receive touch events by overriding the following responder functions:
public func touchesBegan(with event: NSEvent)Direct touches do not send shouldAttemptToRecognizeWithEvent messages to their NSGestureRecognizerDelegate. Instead the iOS approach is used where the delegate is sent a message for each touch.
public func touchesMoved(with event: NSEvent)
public func touchesEnded(with event: NSEvent)
public func touchesCancelled(with event: NSEvent)
optional public func gestureRecognizer(_ gestureRecognizer: NSGestureRecognizer, shouldReceive touch: NSTouch) -> Bool
NSScrubber
macOS 10.12.1 introduces NSScrubber, a new AppKit control which arranges a finite collection of selectable content within the Touch Bar. It offers a variety of interaction modes and a flexible layout system. See the documentation for NSScrubber, NSScrubberLayout, and NSScrubberItemView for detailed information on how to build a scrubber control.In 10.12.1, there is a known issue which prevents a NSScrubber configured for free scrolling and continuous selection from working properly with a layout that is dependent on the selected item.
Sharing Service Picker for NSTouchBar
NSSharingServicePickerTouchBarItem allows one to easily add a popover bar that utilizes the sharing service. It works similar to the NSSharingServicePicker, and the delegate derives from NSSharingServicePickerDelegate and adds the ability to dynamically provide the items to share.NSSharingServicePickerTouchBarItem can not have the delegate be set to itself; this will cause an infinite loop.
Popover chevrons
The popover chevron is automatically displayed when the popoverTouchBar and pressAndHoldTouchBar properties refer to the same NSTouchBar instance. When the bars are different (including when the pressAndHoldTouchBar is nil) the chevron will not be displayed. Please note also that the chevron can not be automatically displayed when the item’s collapsedRepresentation has been replaced.Popover nesting
Popovers are not nestable. Behavior is undefined if a popover item is used inside another popover item’s popoverTouchBar or pressAndHoldTouchBar.A note on first responders
As the set of displayable bars is built from the responder chain, it is important that your responder chain matches what you expect. A common hiccup for views and view controllers is that they or a previous responder needs to be the first responder of a window. This might require over-riding the -acceptsFirstResponder method in a responder subclass.NSTouchBar and Text
The NSTouchBar feature surrounding the text editing functionalities is a core part of the user experience offered by the new hardware. NSTextView participates in the NSTouchBar facility out of the box and provides text editing and formatting functionalities transparently without requiring adoptions from applications. NSCandidateListTouchBarItem, a concrete NSTouchBarItem subclass, can be easily configured to show the natural language candidate list appropriate for the current user document by working with NSSpellChecker and the text input system. NSTouchBar supports a text-related item identifier, NSTouchBarItemIdentifierCharacterPicker, and returns an NSPopoverTouchBarItem configured to show the system character picker from -itemForIdentifier:.NSTextView Support
NSTextView should be able to present its NSTouchBar items out of the box without relying on any external controller from applications. For that, the view object implements -makeTouchBar configuring the item identifiers based on its property settings such as -isRichText. Also, NSTextView conforms to the NSTouchBarDelegate protocol and makes itself the delegate inside -makeTouchBar. This ensures its built-in items are properly associated with the bar through -touchBar:makeItemForIdentifier:. The method recognizes item identifiers for text color, text style (B/I/U), text alignment, text list, candidate list, and a group item preconfigured to contain the items above. The identifiers are NSTouchBarItemIdentifierTextColorPicker, NSTouchBarItemIdentifierTextStyle, NSTouchBarItemIdentifierTextAlignment, NSTouchBarItemIdentifierTextList, NSTouchBarItemIdentifierCandidateList, and NSTouchBarItemIdentifierTextFormat.There are new APIs for controlling the items displayed by NSTextView. A new property, -allowsCharacterPickerTouchBarItem, controls whether to include the system character picker with the text view. The property is also available for NSTextField and its subclasses. When properties affecting the item identifiers such as -isRichText, -updateTouchBarItemIdentifiers gets messaged. NSTextViewDelegate can get notified via -textView:shouldUpdateTouchBarItemIdentifiers:.
Whenever the states for text NSTouchBar items need updating, -[NSTextView updateTextTouchBarItems] gets messaged.
NSSpellChecker Support for NSTouchBar Candidate Generation
NSCandidateListTouchBarItem is a special purpose NSTouchBarItem subclass that provides the candidate list functionality. It is used by default by NSTextView to show a natural language candidate list appropriate for the current user document by working with NSSpellChecker and the text input system. Other non-NSTextView clients may need their own access to candidate generation to obtain the same sort of candidates for use with NSCandidateListTouchBarItem, and NSSpellChecker provides facilities for that.Candidate Generation
NSCandidateListTouchBarItem can be easily configured to work with NSSpellChecker and the text input system to display the candidate list appropriate for the user document content. Also, it can be configured to show arbitrary custom contents for applications. With the default configuration, the item is associated with NSTouchBarItemIdentifierCandidateList.Showing candidates from NSSpellChecker
The system candidate list based on the current selection inside the user document can be queried via the asynchronous method -[NSSpellChecker requestCandidatesForSelectedRange:inString:types:options:inSpellDocumentWithTag:completionHandler:]. The client passes in a string that should contain at least a paragraph’s worth of context around the point of interest (typically the insertion point). The selectedRange argument gives an indication of where the completion is occurring, and it should represent the portion of the string being checked that is selected, or be NSMakeRange(insertionPointIndex, 0) if there is an insertion point within or adjacent to the string being checked, or be NSMakeRange(NSNotFound, 0) if the selection is entirely outside of the string being checked.
As with other NSSpellChecker asynchronous methods, the return value from this method is a sequence number that can be compared against the number passed to the completion block, for example to protect against the possibility of the text having changed in the interim. When the candidates become available, the completion block is called with an array of NSTextCheckingResult objects, specifying the range to be replaced, the replacement string, and the type of replacement. This call may be made on an arbitrary queue, so if clients need to operate on the candidates on the main queue they will need to arrange to send them there.
Showing candidates from the text input system
The candidates from the text input system can be automatically displayed by configuring the candidate list item properly. The allowsTextInputContextCandidates property should be YES, and the client property should point to the target view conforming to the TextInputClient protocol. The view should return the candidate list item from -candidateListTouchBarItem.
Showing custom candidates
With -setCandidates:forSelectedRange:inString:, applications can show arbitrary candidate such as contacts, e-mail addresses, telephone numbers, etc. With the configuration, the delegate property should contain an object conforming to NSCandidateListTouchBarItemDelegate for handling the touch on the Touch Bar.
NSTextView Candidate List Support
NSTextView supports NSCandidateListTouchBarItem and can manage the candidate list displaying the candidates from NSSpellChecker and the text input system. Also, NSTextViewDelegate now has additional methods for managing the candidate list item contents.The automaticTextCompletionEnabled property controls whether to include the candidate list item in its NSTouchBar. NSTextField also has the corresponding property for controlling the behavior.
Two NSTextViewDelegate methods: -textView:candidatesForSelectedRange: and -textView:shouldSelectCandidateAtIndex:, can be used for managing custom candidates for the text view.
Related Methods
There is also an NSSpellChecker method, -deletesAutospaceBetweenString:andString:language:, for clients to use to after a candidate has been accepted, to determine whether or not to delete the trailing space after a candidate when the user subsequently types certain punctuation (such as periods and commas).In addition, there is an NSSpellChecker method, +isAutomaticTextCompletionEnabled, to allow clients to find out the state of the user’s global preference for showing or not showing candidates, and a notification when this preference changes, NSSpellCheckerDidChangeAutomaticTextCompletionNotification.
Finally, one of the new keys introduced in 10.12 for clients to use with existing NSSpellChecker text checking methods, NSTextCheckingSelectedRangeKey, is needed in order to maintain consistency of results between these methods and the new candidate generation method. Clients should use this key in the options dictionary whenever they are calling NSSpellChecker methods like requestCheckingOfString:... or checkString:... that take an options dictionary. The value should be an NSValue created using -valueWithRange:, where the range should follow the same rules as the range passed in to requestCandidatesForSelectedRange:... Notice that the candidate generation method now has access to the selected range, so for consistency the text checking methods need access to it as well.
RTF Background Color Handling
In 10.12, 10.12.1, iOS 10, and 10.1, when a background color is set on a range of characters (using NSBackgroundColorAttributeName), and the resulting attributed string is saved as RTF and loaded back in, the characters following the range appear with a white background color, rather than no background color. This is often not visible in cases where text is drawn on a white surface, but could be a problem otherwise.This problem will be addressed in an upcoming release.
Notes specific to macOS 10.12
Some of the major topics covered in this section:
- NSCollectionView
- Layout
- Dragging File Promises
- Control Convenience Constructors
- NSGridView
- Window Tabbing
- Wide Color
Overall API Updates
This release brings many API refinements, taking advantage of new API features in Objective-C and enabling APIs to come across more naturally in Swift.Some of the key API update areas include:
- Adoption of Swift 3 API design guidelines, which has a large impact on the APIs as they appear in Swift.
- Adoption of Foundation Value Types, where appropriate in APIs. For instance NSData and NSURL in API now appear as Data and URL in Swift. (Note that prefix dropping is a Foundation-only change and does not apply to other frameworks.)
- Updated names of enumerations to improve consistency and exposure in Swift.
NSCollectionView: Collapsable Sections
Finder’s icon view offers users the ability to collapse any section (each grouping by kind in “Arrange by: Kind” mode, for example) into a single horizontally scrollable row, via a “Show Less” button in the section’s header. A section will sometimes start out in this collapsed state, with the button (now titled “Show All”) toggling the section back into a normal flow layout. You can now easily offer the same capability to users of your NSCollectionViews.To enable this feature:
1. Make your header view conform to the new NSCollectionViewSectionHeaderView protocol. This protocol provides an optional "sectionCollapseButton” property that we’ll use the help the CollectionView find the button.
2. Add an NSButton as a subview or descendant of your section header view. (If you’re using .xib files, you can do this in the interface editor.)
3. Set the button’s image or title to something general that indicates a toggle action — or, if desired, to indicate an initially uncollapsed state. (e.g. You’re likely to want it to read “Show Less”, or similar.)
4. Set up Auto Layout constraints to position your button as desired within the header view.
5. Wire the header view’s “sectionCollapseButton” outlet to your button.
6. Wire your button’s action to send -toggleSectionCollapse: to the First Responder. (Alternatively, you can use another action of your own, and have its implementation use either -toggleSectionCollapse: or the programmatic collapse/expand API described below to change the section’s state as desired.)
When the user clicks the button, -toggleSectionCollapse: should get routed to the NSCollectionView, which identifies the section based on the enclosing header view instance, and toggles the section’s collapsed state.
As in Finder’s icon view, the button is automatically shown when applicable, and hidden when not. If you want to change the title of the button to reflect the current collapsed/uncollapsed state, you can do so by overriding the -toggleSectionCollapse: action method on NSCollectionView to update the button’s title, after calling up to super. For example:
// Override -toggleSectionCollapse: to update the button's title.
- (void)toggleSectionCollapse:(id)sender { // "sender" is the "Show Less" / "Show All" button.
// Let NSCollectionView collapse or expand the corresponding section.
[super toggleSectionCollapse:sender];
// Update the button's label to show the action it will now perform.
if ([sender isKindOfClass:[NSButton class]]) {
// We must have a Flow layout, to be able to collapse and expand sections.
NSCollectionViewFlowLayout *layout = (NSCollectionViewFlowLayout *)[self collectionViewLayout];
if ([layout isKindOfClass:[NSCollectionViewFlowLayout class]]) {
/* Find the containing header view, and from it the corresponding sectionIndex.
(The `sender` button is required to reside in a section Header view.) */
NSSet<NSIndexPath *> *headerIndexPaths = [self indexPathsForVisibleSupplementaryElementsOfKind:NSCollectionElementKindSectionHeader];
NSUInteger sectionIndex = NSNotFound;
for (NSIndexPath *headerIndexPath in headerIndexPaths) {
NSView *headerView = [self supplementaryViewForElementKind:NSCollectionElementKindSectionHeader atIndexPath:headerIndexPath];
if (headerView && [(NSView *)sender isDescendantOf:headerView]) {
sectionIndex = [headerIndexPath section];
break;
}
}
if (sectionIndex != NSNotFound) {This example method uses API that enables you to programmatically query and change the collapsed/expanded state of any section:
NSString *newLabel;
if ([layout sectionAtIndexIsCollapsed:sectionIndex]) {
newLabel = [NSString stringWithFormat:@"Show All (%lu)", (unsigned long)[self numberOfItemsInSection:sectionIndex]];
} else {
newLabel = @"Show Less";
}
[(NSButton *)sender setTitle:newLabel];
}
}
}
}
@interface NSCollectionViewFlowLayout
...
/* Returns YES if the specified section is currently collapsed; NO if not, or if there is no such section. Defaults to NO.
*/
- (BOOL)sectionAtIndexIsCollapsed:(NSUInteger)sectionIndex NS_AVAILABLE_MAC(10_12);
/* Collapses the specified section to a single row, if it is not already collapsed.
*/
- (void)collapseSectionAtIndex:(NSUInteger)sectionIndex NS_AVAILABLE_MAC(10_12);
/* Un-collapses the specified section, if it is currently collapsed.
*/
- (void)expandSectionAtIndex:(NSUInteger)sectionIndex NS_AVAILABLE_MAC(10_12);
...
@end
NSCollectionView: Floating Headers
Header and Footer views in a Flow layout can now be “floated”, using the same API provided by iOS’ UICollectionView.Set these properties to YES to get headers that pin to the top of the visible area and footers that pin to the bottom while scrolling. These properties are archived with the layout's other persistent properties. Enabling this feature may affect the parenting of header and footer views.
@interface NSCollectionViewFlowLayoutWhen sectionHeadersPinToVisibleBounds is set to YES, the header view (if any) for the topmost visible section will stay pinned to the top of the scroll area in preference to being scrolled out of view. As the user scrolls down, the header stays pinned there until the next section’s header (or the top of the next section, if the next section has no header) pushes it out of the way.
...
@property BOOL sectionHeadersPinToVisibleBounds NS_AVAILABLE_MAC(10_12);
@property BOOL sectionFootersPinToVisibleBounds NS_AVAILABLE_MAC(10_12);
...
@end
The sectionFootersPinToVisibleBounds property behaves similarly, with pinning to the bottom of the scroll area provided instead.
NSCollectionView: Scrollable Background View
An NSCollectionView’s backgroundView can now be set to scroll together with the CollectionView’s content, instead of floating stationary behind the content. When backgroundViewScrollsWithContent is YES, the CollectionView's backgroundView (if any) will match the CollectionView's frame and scroll with the CollectionView's items and other content. When NO (the default, compatible with the behavior on OS X 10.11), the backgroundView is made to fill the CollectionView's visible area, and remains stationary when the CollectionView's content is scrolled. This property is archived with the CollectionView's other persistent properties.@interface NSCollectionViewSetting this property necessarily changes the way the backgroundView is parented. To float the backgroundView behind scrolling content, we make it a sibling of the CollectionView’s ClipView (as on 10.11). To have it scroll with the CollectionView’s content, we instead make the backgroundView a subview of the CollectionView.
...
@property BOOL backgroundViewScrollsWithContent NS_AVAILABLE_MAC(10_12);
...
@end
The backgroundView property is meant to interoperate with backgroundColors (and now does so better, thanks to a fix in 10.12). If a CollectionView has both a backgroundView and backgroundColors, backgroundColors[0] will show through any areas not opaquely covered by the backgroundView. (backgroundColors beyond the first element are a legacy feature of NSCollectionView from OS X < 10.11. When using 10.11’s “new” NSCollectionView, only the first color is used.)
The backgroundView’s frame is set to match the CollectionView’s, and updated automatically to maintain that equality.
NSCollectionView: File Promise Drag Fix
On OS X 10.11, a dataSource-based NSCollectionView acting as the dragging source for a file promise drag would erroneously attempt to invoke the old -collectionView:namesOfPromisedFilesDroppedAtDestination:forDraggedItemsAtIndexes: delegate API method, instead of first checking for the newer -collectionView:namesOfPromisedFilesDroppedAtDestination:forDraggedItemsAtIndexPaths: API (and would raise an exception if the old-form delegate method wasn’t found). This has been fixed, so that NSCollectionView will first look for and attempt to invoke the new method. Failing presence of the new method, NSCollectionView will fall back to the old method, but only if it is a single-section CollectionView.NSCollectionView: Delegate Message Send Fix
On 10.12, an NSCollectionView’s delegate now receives -collectionView:didEndDisplayingItem:forRepresentedObjectAtIndexPath: (and the corresponding items are placed in the CollectionView’s reuse queue) for all currently instantiated items when the CollectionView is removed from its window, and not just when the items are scrolled out of view or removed. This makes -collectionView:didEndDisplayingItem:forRepresentedObjectAtIndexPath: a more reliable cleanup opportunity for resources that may be associated with items. The same is true for -collectionView:didEndDisplayingSupplementaryView:forElementOfKind:atIndexPath: (which due to a bug was not sent at all on OS X 10.11).On 10.12, -collectionView:willDisplaySupplementaryView:forElementKind:atIndexPath: is now sent for a supplementary or decoration view that is about to be displayed. Due to a bug, this delegate message was not sent on OS X 10.11.
NSCollectionViewGridLayout
NSCollectionViewGridLayout now implements -initWithCoder: and -encodeWithCoder: to persist and restore its properties as it should. It did not do so on OS X 10.11.NSToolbar
NSToolbar's contextual menu will no longer show the "Hide Toolbar" item in applications linked on or after 10.12. Please be sure to include a menu item in your application's menu bar to toggle this behavior.NSTabView - New since WWDC seed
NSTabView now has two new enums used to determine its style. NSTabPosition describes the position of the tabs and NSTabViewBorderType describes the border of the tab view. This is now the preferred method over using NSTabViewType.Setting the tabPosition or tabViewBorderType will affect the NSTabViewType. Setting the tabViewType will also set the tabPosition and tabViewBorderType.
TabViewBorderType of anything other than NSTabViewBorderTypeBezel will only be respected if NSTabPosition is NSTabPositionNone as all other configurations are invalid. In an invalid configuration (i.e. a tabPosition of NSTabPositionLeft and a tabViewBorderType of NSTabViewBorderTypeLine) the tabViewBorderType will default to NSTabViewBorderTypeBezel.
NSSlider isVertical
NSSlider and NSSliderCell's `vertical` property is now readwrite and has changed its type from NSInteger to BOOL. In previous releases, NSSliderCell would try to automatically determine its orientation based on the aspect ratio of a received cell frame. This also led to -1 being returned from `isVertical` if it could not make a determination (e.g. the cell frame had a zero size), and could lead to the slider changing orientation when it was resized.With 10.12, the vertical property is set at initialization time based on the aspect ratio of the frame parameter, and defaults to horizontal in ambiguous (equal width/height) cases; however it is recommended to explicitly set the vertical property or use the convenience constructors. To dynamically change the orientation of the slider, explicitly set the `vertical` property.
NSSplitView Delegate
For apps linked against the 10.12 SDK, NSSplitView's delegate property has zeroing `weak` memory semantics, compared to the previous `assign` memory semantics. However, if the object being set as the delegate doesn't support weak references, it will fallback to having assign semantics.When linked against an older SDK, the delegate will continue to have `assign` semantics even when run on 10.12 or later.
It is important for apps that deploy to older OS X versions to still code against the `assign` semantics, and clear the delegate property when the delegate object is deallocated.
NSAlert Delegate
For apps linked against the 10.12 SDK, NSAlert's delegate property has zeroing `weak` memory semantics, compared to the previous `retain` memory semantics. However, if the object being set as the delegate doesn't support weak references, it will fallback to having assign semantics on 10.12.When linked against an older SDK, the delegate will continue to have `retain` semantics even when run on 10.12 or later.
It is important for apps that deploy to older OS X versions to still consider the `retain` behavior and protect against retain cycles.
NSAlert Used Outside of Runloop
With 10.11 showing a modal NSAlert (or any other UI) outside of the main runloop will cause it to not disappear on modal session end. With 10.12, these alerts will be visually dismissed for apps linked against the 10.10 SDK or earlier. For newer apps, alerts should only be shown within the main run loop; or in cases where that is not possible, apps should make sure to flush the CATransaction after the modal run of the alert finishes (i.e. +[CATransaction flush]).NSCursor
Prior to OS 10.12, -[NSCursor initWithImage:foregroundColorHint:backgroundColorHint:hotSpot:] would act as a designated initializer. The color hint parameters were unused and are now deprecated. For apps linked on or after OS 10.12, the color hint initializer calls into the new designated initializer, "-[NSCursor initWithImage:hotSpot:]". Apps which depend on which initializer is called should move to the new designated initializer.NSCursor now better handles NSImage objects that contain vector representations and/or bitmap representations larger than @2x. It will now use the best available representation when the cursor is scaled via Accessibility preferences.
Layout Pass
In 10.12, the deferred view layout pass has been cleaned up, optimized and extended. This has resulted in several changes that developers should be mindful of when implementing view layout.The deferred view layout pass represented by [NSView setNeedsLayout:] and [NSView layout] is now independent of autolayout and constraints. As a result, you can now implement custom layout mechanisms by overriding [NSView layout] without implicitly activating autolayout on the window. For example, if a view has child CALayers, it can now cleanly lay out those child layers in a [NSView layout] override and simply call [self setNeedsLayout:YES] whenever the child layout geometry needs to be updated in a deferred layout pass.
Previously, there were times where CoreAnimation would trigger [NSView layout] calls on parts of the view hierarchy out of band with the rest of the layout pass, resulting in inconsistent layout pass behavior or sometimes excessive layout calls on parts of the view hierarchy. The view layout pass is now driven entirely by AppKit. There could be cases where custom views were laying out child views or layers in a [view layout] override and were not setting view.needsLayout=YES appropriately, but the problem was masked by CoreAnimation. The solution in these cases is to ensure that view.needsLayout=YES is set whenever necessary. Note that layout for a view is now automatically invalidated when the view’s frame size changes, so view.needsLayout=YES should only be necessary on relevant state changes.
When overriding [NSView layout], AppKit no longer requires calling [super layout]. In particular, the view.needsLayout bit is now cleared automatically outside of the [view layout] call. This allows you to completely override [view layout] and block all of the default layout behavior if so desired. Also note that the bit is cleared before [view layout] is called, which means that if you dirty layout during your [view layout] override (either via calling [view setNeedsLayout:YES] directly or by modifying constraints), it will schedule a second layout pass.
Coordinated with a generic layout pass, AppKit now attempts to be smarter about when to actually engage an autolayout engine on an NSWindow. Specifically, the presence of an override to [NSView updateConstraints] now indicates to AppKit that the view in question, by default, uses constraints, and as such will activate autolayout in any NSWindow into which it is placed. If, however, you are writing a custom view type that supports but does not require constraints, then the correct solution, as before, is to override the class-level [NSView requiresConstraintBasedLayout] method and return NO.
As part of the layout pass optimization, AppKit is now more strict about certain requirements and limitations of the API. In particular:
- Be mindful of how layout is applied in a deferred manner, such that a view cannot rely on the frames of any subviews being current when its frame changes or during the view’s layout call (the ordering is not guaranteed). If you implement custom layout behaviors on top of deferred constraint-based layout and require the frames being current, the correct method is to call [self layoutSubtreeIfNeeded], which will perform the layout pass synchronously for just that subtree. Also note that using [self layoutSubtreeIfNeeded] is in general good practice if you are relying on frames of descendant views provided by other frameworks or libraries, since it can be difficult to determine whether any of those components use deferred layout or not, and the answer might change from release to release.
- AppKit is now more efficient about only calling -layout on views when actually necessary, which can reveal holes where code previously did not properly invalidate layout when necessary (the above CoreAnimation case being one of those instances). If you find -layout method not being called when expected on your view, make sure that the view is actually marked as needsLayout=YES.
- There are certain API calls that are specific to autoresizing masks or constraint-based layouts, and using one or the other, particularly in view hierarchies where both methods are used, can lead to unexpected results. Specifically, the resizeSubviewsWithOldSize/resizeWithOldSuperviewSize pair of API calls are specifically tied to autoresizing masks and whether a view has autoresizesSubviews set, and as a consequence those API calls can—and most likely will—be called before any constraint-based view layout is performed. The best layout-method-agnostic method of watching for size changes on a custom view class continues to be overriding the [NSView setFrameSize] call, and the best method for laying out children is to override [NSView layout], since it no longer automatically implies autolayout activation.
- The constraint solver works best when a set of layout constraints are activated once and left alone to re-evaluate as necessary during resize. However, if you must modify constraints as your view resizes, the best method continues to be overriding [NSView updateConstraints] and then setting self.needsUpdateConstraints=YES from within an [NSView setFrameSize] override. This method reduces the chance of a layout loop, makes layout dependencies easier to debug, and allow AppKit to better optimize the performance of the layout changes.
Improved debugging support for layout
AppKit now provides some additional support for debugging layout feedback loops. If you encounter layout passes that do not terminate naturally, you can run your application with the following argument: -NSViewLayoutFeedbackLoopDebuggingThreshold 100. The framework will then track dirty layout and attempt to identify cycles, logging information that may be useful in isolating the problem.To assist debugging redundant view layout, there are now two user defaults. NSViewLayoutStrictMode, when enabled, will log the pointer of the view receiving redundant layout. This behavior can be modified to throw an exception by setting the NSViewLayoutStrictModeThrowsException user default to YES. NSViewLayoutStrictModeThrowsException has no effect when NSViewLayoutStrictMode is not set to YES.
Item Based Dragging File Promises
The Drag & Drop API has been modernized with two new classes: NSFilePromiseProvider and NSFilePromiseReceiver. These new objects:• Support drag flocking
• UTI based
• Pasteboard Writer/Reader compliant
• File coordinated when possible
Also, NSFilePromiseProvider is compatible with drag destinations using the non-item based drag file promise API. Likewise, NSFilePromiseProvider is compatible with dragging sources using the non-item based drag file promise API.
Dragging File Promise Source - Changed since WWDC seed
Use the NSFilePromiseProvider class when creating file promises. Instantiate one NSFilePromiseProvider for each file promised. Before writing any NSFilePromiseProvider to the pasteboard, it must contain a file type being promised and an NSFilePromiseProviderDelegate. The file type must be a UTI that ultimately conforms to kUTTypeData or kUTTypeDirectory. The NSFilePromiseProviderDelegate will do the heavy lifting of writing the promised file to the destination directory. Optionally, you may attach a userInfo object of your choosing to the NSFilePromiseProvider. This is often useful to determine which promise is being referenced when promising multiple files and using the same NSFilePromiseProviderDelegate instance.There are only three NSFilePromiseProviderDelegate methods you need to implement:
- (NSString *)filePromiseProvider:(NSFilePromiseProvider*)filePromiseProvider fileNameForType:(NSString *)fileType;Discussion: This method is called when the drag destination “calls in” the file promise. At this point you should determine and return the final filename, which should be the base filename, not a full path. Note: The drag process is halted while waiting for this method to return. Taking too long in this method may result in the drag getting cancelled. Do not start writing the file yet.
- (void)filePromiseProvider:(NSFilePromiseProvider*)filePromiseProvider writePromiseToURL:(NSURL *)urlDiscussion: Write the contents of this promise item to the provided URL and call completionHandler when done. It is important to call the completion handler as the file writing is wrapped with NSFileCoordinator. Likewise, be sure write your file to the supplied url parameter. This method is called after the drag completes. Note: This request shall occur on the NSOperationQueue supplied by -promiseOperationQueue.
completionHandler:(void (^)(NSError *errorOrNil))completionHandler;
- (NSOperationQueue *)promiseOperationQueueForFilePromiseProvider:(NSFilePromiseProvider*)filePromiseProvider;Discussion: Return the operation queue that the write request should be issued from. If this method is not implemented, the mainOperationQueue is used. While optional, it is strongly recommended that you provide an operation queue other than the main operation queue to avoid blocking your main thread.
Dragging File Promise Destination
Use the NSFilePromiseReceiver object to receive file promises. Since NSFilePromiseReceiver implements the NSPasteboardReader protocol, you get all the file promises on the drag pasteboard like so:NSArray<NSFilePromiseReceiver*> filePromises = [pasteboard readObjectsForClasses:@[[NSFilePromiseReceiver class]] options:nil];Likewise you can enumerate the draggingItems by
[draggingInfo enumerateDraggingItemsWithOptions:0 forView:view classes:@[[NSFilePromiseReceiver class]]Compatibility note: A non-item based drag source (e.g. don’t use NSFilePromiseProvider) may promise multiple files on the same pasteboard item. To be compatible with these drag sources many NSFilePromiseReceiver methods return an array of values. Muti-file item based promises result in one NSFilePromiseReceiver per promised file.
searchOptions:@{} usingBlock:^(NSDraggingItem *draggingItem, NSInteger idx, BOOL *stop) {
NSFilePromiseReceiver *filePromiseReceiver = dragItem.item
}];
+ (NSArray<NSString *> *)readableDraggedTypes;Discussion: A view must register what types it accepts via -registerForDraggedTypes:. Use this class method to get the file promise drag types that NSFilePromiseReceiver can accept, in order to register a view to accept promised files. NSFilePromiseReceiver can accept file promises from both the item based NSFilePromiseProvider and the non-item based API. If you don’t register all these drag types you may fail to be notified about some file promise drags. Registration is simply: [view registerForDraggedTypes:[NSFilePromiseReceiver readableDraggedTypes]];
@property(copy, readonly) NSArray<NSString*> *fileTypes;Discussion: Returns an array of UTIs for the filetypes promised. NSFilePromiseProvider promises 1 filetype per item. The count of fileTypes should tell you the number of promised files in this item, however, that is not guaranteed. Historically, some legacy file promisers only list each unique fileType once and write one or more files per type.
- (void)receivePromisedFilesAtDestination:(NSURL *)destinationDir options:(NSDictionary *)optionsDiscussion: This effectively calls in the promises. Therefore, only call once you are accepting the file promise. All file promisesReceiver's in a drag must specify the same destination location. The options dictionary is ignored for now. The reader block is called on the supplied operationQueue when the promised file is ready to be read. It is highly recommended that you specify an optation queue other than the main operation queue to avoid blocking the main thread waiting for the file promise to be written by the source (as this may be a long process). When the source is an NSFilePromiseProvider, the readerBlock call is wrapped in an NSFileCoordination read. Otherwise, a heuristic is used to determine when the promised file is ready to be read. Note: Writing of the promised file may fail. When that occurs, the readerBlock is still called with a non-nil error. There may be nothing at the fileURL, or it may be a partial / corrupt file.
operationQueue:(NSOperationQueue *)operationQueue reader:(void(^)(NSURL *fileURL, NSError * __nullable errorOrNil))reader;
@property(copy, readonly) NSArray<NSString*> *fileNames;Discussion: Returns an array of the file names of the promised files that are being written to the destination location. This property returns an empty array until the file promise is called in via receivePromisedFilesAtDestination.
NSControl
Built-in NSControl subclasses now automatically mirror some directional properties (e.g. NSButton’s imagePosition) when unarchived from a Base-localized Interface Builder archive into a right-to-left user interface. This behavior is suppressed if the control is configured with “Mirror” set to “Never”.The cases of the NSControlSize enumeration have been renamed to have a common prefix, per modern naming conventions. The existing names are now deprecated.
The -[NSControl sendAction:to:] method incorrectly declared its `theAction` parameter as non-nullable. It is now correctly marked as nullable.
NSButtonCell
The previously-unused gradientType property, and corresponding NSGradientType enumeration, are now deprecated.NSButton
NSButton now has several new convenience constructors for creating system standard push buttons, checkboxes, and radio buttons:NSButton(title:target:action:)NSButton’s keyEquivalentModifierMask property is now declared with type NSEventModifierMask instead of NSUInteger.
NSButton(image:target:action:)
NSButton(title:image:target:action:)
NSButton(checkboxWithTitle:target:action:)
NSButton(radioButtonWithTitle:target:action:)
NSTextField
NSTextField now has several new convenience constructors for creating non-wrapping labels, wrapping labels, attributed labels, and editable text fields:NSTextField(labelWithString:)The -control:isValidObject: delegate method incorrectly declared the `object` parameters as nonnull. It is now correctly marked as nullable.
NSTextField(wrappingLabelWithString:)
NSTextField(labelWithAttributedString:)
NSTextField(string:)
NSSegmentedControl
NSSegmentedControl has new convenience constructors for creating a segmented control given an array of labels or an array of images:NSSegmentedControl(labels:trackingMode:target:action:)
NSSegmentedControl(images:trackingMode:target:action:)
NSSlider
NSSlider has new convenience constructors for creating horizontal linear sliders:NSSegmentedControl(target:action:)
NSSegmentedControl(value:minValue:maxValue:target:action:)
NSImageView
NSImageView has a new convenience constructor for creating a non-editable image view with a given NSImage:NSImageView(image:)
NSButton - New since WWDC seed
NSButton provides a new property, imageHugsTitle, which affects how the button image is positioned when the button is made larger than its intrinsic content size. By default, the image is always positioned relative to the bezel edge, but with imageHugsTitle = YES, the image is always positioned adjacent to the title text.NSButton now exposes an imageScaling property, available back to 10.5 and later. Accessing this property is preferred over accessing the NSButtonCell equivalent.
NSPopUpButton - New since WWDC seed
For applications linked against the macOS Sierra SDK and later, NSPopUpButton no longer reserves drawing space for arrows if arrowPosition is set to NSPopUpNoArrow.New Layout Container: NSGridView
NSGridView is an auto layout container for arranging content views into a grid. At a high level it's similar to NSStackView, except it allows for alignment across both rows and columns. Like a spreadsheet, rows and columns can all be varying sizes, or dynamically hidden & shown. Individual cells may be merged to achieve a layout that crosses the basic grid boundaries. See NSGridView.h for the API and more information.NSLayoutConstraint
NSLayoutConstraint now exposes properties for a firstAnchor and nullable secondAnchor. This is part of a longer-term move towards leveraging layout anchors more heavily in the API. Use of these is recommended over the existing item/attribute pairs, since some anchors cannot be represented that way. For this reason, NSLayoutConstraint's firstItem property is now nullable, and developers may begin to encounter valid constraints with nil items or NSLayoutAttributeNotAnAttribute.NSViewNoInstrinsicMetric unavailable from Swift - New since WWDC seed
The NSViewNoInstrinsicMetric constant was replaced with the properly-spelled NSViewNoIntrinsicMetric in macOS 10.11, and is unavailable from swift. This can present a problem for swift developers that are using the 10.12 SDK, targeting macOS 10.10, and need to override intrinsicContentSize to not specify a size in one or both dimensions. In this case, it's acceptable to simply use -1.0 instead.Accessibility
In NSAccessibilityConstants.h, we added the role constant “NSAccessibilityMenuBarItemRole” for items in the menubar.In NSAccessibilityProtocols.h, the NSAccessibilityLayoutArea protocol now declares accessibilityFocusedUIElement as a read-only property rather than a method, so that it matches the NSAccessibility protocol. This allows Swift classes to implement the NSAccessibilityLayoutArea protocol by overriding the property.
In NSAccessibilityConstants.h, we added a new constant “NSAccessibilityRequiredAttribute” which indicates that a form field is required to have content for successful submission of the form. In NSAccessibilityProtocols.h, defined the corresponding protocol method for setting and getting the value of this attribute on an accessible object.
Menus
NSMenuItem’s keyEquivalentModifierMask property is now declared with type NSEventModifierMask instead of NSUInteger.NSMenu and NSMenuItem now conform to the NSUserInterfaceItemIdentification protocol.
NSMenu and NSMenuItem now conform to the NSAccessibility and NSAccessibilityElement protocols. These classes now support the accessibility identifier and description that can be set in Interface Builder. You can also change the accessibility description in code with the -setAccessibilityLabel property setter.
NSMenu's delegate property is now declared "weak". The NSMenu implementation has actually stored a weak reference to the delegate (for zeroing-weak-compatible objects) since OS X v10.9, so this change is not a change in behavior but simply documents the current implementation.
Menus - New since WWDC seed
The popUpContextMenu and popUpMenuPositioningItem methods now align the menu at the popup location according to the user interface layout direction of the presenting view. If the view uses right-to-left layout, then the menu is right-aligned at the specified position; otherwise it is left-aligned.NSTableView / NSOutlineView
NSTableView properly supports Right to Left Layout for applications that link on 10.12 and higher. Previously, the userInterfaceLayoutDirection was hardcoded to be NSUserInterfaceLayoutDirectionLeftToRight. NSTableView and NSOutlineView will now automatically flip the table column order for applications are right to left. This is only affects the visual representation of the table columns; the actual logical tableColumn array order is still maintained.NSTableView (and NSOutlineView) now has a proper weak delegate and dataSource implementation. This means a developer is no longer required to clear them out to nil when the delegate (and/or dataSource) object is freed. This change applies to all applications on 10.12. Applications that target older releases should still reset the delegate to nil; doing so is harmless for 10.12 and higher. Note that the _delegate and _dataSource ivars in NSTableView have always been private, and should not be directly accessed. In 10.12, they are now opaque objects, and may not represent the real delegate or dataSource. Instead of directly using the ivars, one should use the property accessors.
NSOutlineView has a new property named stronglyReferencesItems to allow it to automatically retain the items returned to it when set to YES. The default value is YES for applications linked on 10.12 and higher, and NO otherwise. In general, this value should always be YES, but can be set back to NO for cases where the items would cause a retain cycle. This could happen if the item either directly or indirectly retained the outline view, which would cause it to never get released.
NSOutlineView will now reload the cell views associated with ‘item’ when [outlineView reloadItem:] is called. The method simply calls [outlineView reloadDataForRowIndexes:columnIndexes:] passing the particular row that is to be reloaded, and all the columns. For compatibility, this will only work for applications that link against the 10.12 SDK.
NSTableView will now reload "full width" cell views (meaning the column index for them is -1) when [tableView reloadDataForRowIndexes:columnIndexes:] is called, and the column index contains -1. For compatibility, this will only work for applications that link against the 10.12 SDK.
NSOutlineView - New since WWDC seed
NSOutlineView can now use isEqual: rather than pointer equality to compare items. NSOutlineView will use isEqual: rather than pointer equality if stronglyReferencesItems is set to YES.Prior to 10.12, disabling mirroring in Interface Builder on an NSOutlineView would not work; this has been resolved. Applications that require an NSOutlineView to always be left-to-right (even in a right-to-left language) should explicitly set the userInterfaceLayoutDirection in code if they are targeting an OS prior to 10.12.
NSVisualEffectView
The class now has a new material for selection: NSVisualEffectMaterialSelection. In addition, a new property called emphasized can be used to indicate that the selection should draw in "first responder" status, or not.NSWindow
NSTitlebarAccessoryViewController supports NSLayoutAttributeLeading and NSLayoutAttributeTrailing to specify an abstract position that automatically flips depending on the localized language. These only work for applications that link on 10.12 and later. For applications that do not link on 10.12, NSLayoutAttributeLeft will automatically flip to the Right when in a Right To Left language and when run on 10.12.NSWindow's backgroundColor would be ignored if a custom appearance was set on the window, and the window's contentView had wantsLayer=YES. This has been fixed for all applications.
Replacing the contentViewController on an visible NSWindow would leave the firstResponder set to the window; this has now been fixed to automatically determine the correct first responder when the contentViewController is changed. Applications targeting prior OS releases should explicitly assign the firstResponder in the new contentViewController's viewWillAppear.
NSWindow Automatic Window Tabbing
Automatic Window Tabbing allows any application to easily (and automatically) adopt tabs. This feature stacks multiple windows together and presents a tab bar in the window title bar area to make it appear to the user as though there is one single window. In reality, only the selected tab's window is shown, and all the other windows are hidden directly using CoreGraphics. An application only has to order a window in, and it will automatically be tabbed with similar windows based on heuristics the user's preference for tabbing. Ordering a window out removes the tab. Windows that are not visible are ordered out with respect to CoreGraphics, but are still considered visible with respect to AppKit / NSWindow.There is a new user System Preference under the Dock section to control tabbing. The behavior of this preference is described in General Flow below.
The NSWindow header has two new enumerations (NSWindowUserTabbingPreference, NSWindowTabbingMode) and several new properties. Please examine the header for some detailed comments on what the new methods are for.
API Adoption
• What should an application which works well with out-of-the-box window tabbing do? - In general, nothing has to be done. A non-NSDocument based application may wish to support the "+" button by implementing newWindowForTab: somewhere in the responder chain.• What should an application which already has support for tabbing do? - The application should explicitly opt-out of automatic window tabbing by calling [NSWindow setAllowsAutomaticWindowTabbing:NO]. It should respect the userTabbingPreference and create tabs based on the General Flow described below.
• What should an application do to customize the standard tabbing behavior? - See the detailed Behavior section below for how the properties interact with each other.
General Flow
The behavior depends on the new system preference under the Dock section to allow tabbing: Always / In Full Screen Only [Default] / Manually. Holding down option, in general, will invert the standard behavior. For each option:When In Full Screen Only [Default]:
* When in Full Screen all windows opened will prefer to tab, including menu items (Cmd-N, Open Recents, etc), the open panel, and opening documents from Finder.
* When in Full Screen Holding down option will prefer to window [in a new space] instead of a tab
* When on the Desktop things work exactly as they always have, with the additional ability to create tabs manually. See Manually.
When Always:
* When on the Desktop or in Full Screen opening similar windows should prefer to always tab, including menu items (Cmd-N, Open Recents, etc), the open panel, and opening documents from Finder.
* Holding down the option button will prefer to create a window instead of a tab.
When Manually:
* When on the Desktop or in Full Screen opening windows will prefer to just create a normal window.
* Holding down the option button will prefer to create a tab instead of a window.
Behavior
The userTabbingPreference is a representation of the system preference for tabbing. Applications that already explicitly adopt tabbing, including ones that do not use automatic window tabbing, should read the value of the userTabbingPreference before showing a window to determine if a regular show of a window should prefer to tab with other windows, or be shown in a new window.[NSWindow allowsAutomaticWindowTabbing] is a way for an application to opt-out of automatic window tabbing. An application that opts out will not get the standard menu items that perform tabbing. The default value for allowsAutomaticWindowTabbing will be YES for all applications except for some that are specifically black-listed out. Those apps can explicitly adopt automatic window tabbing by setting the value of allowsAutomaticWindowTabbing to YES early in the application bring up (in applicationWillFinishLaunching).
A window’s tabbingMode always defaults to NSWindowTabbingModeAutomatic. When the tabbingMode is set to NSWindowTabbingModeAutomatic, and [NSWindow allowsAutomaticWindowTabbing] is YES, the system will automatically tab together similar windows based on tabbingIdentifier. Automatic window tabbing will attempt to tab together windows based on the userTabbingPreference, and how the window was created (based on the logic in “General Flow of Automatic Window Tabs”). When the tabbingMode is set to NSWindowTabbingModeAutomatic, and [NSWindow allowsAutomaticWindowTabbing] is NO, no automatic window tabbing will happen.
A window’s tabbingMode can explicitly be set to NSWindowTabbingModePreferred to allow the window to tab together with other similar windows based on the tabbingIdentifier. This can be set anytime an application wants a window to become in a tab. For instance, it can be set to NSWindowTabbingModePreferred for a window created by a Cmd-T shortcut to always create a window in a tab, regardless of any other system settings, and regardless of the userTabbingPreference. It is up to an application to properly respect the userTabbingPreference and set NSWindowTabbingModePreferred only at appropriate times.
A window’s tabbingMode can explicitly be set to NSWindowTabbingModeDisallowed to explicitly disallow window tabbing. It is up to an application to properly respect the userTabbingPreference and set NSWindowTabbingModePreferred only at appropriate times based on the logic in “General Flow of Automatic Window Tabs”.
A window’s tabbingMode is only checked when a window is about to be shown, and the tabbingMode should be updated before the window is ordered in.
New Button
The plus button will be shown if newWindowForTab: is implemented in the responder chain. NSDocumentController informally implements newWindowForTab:, but only returns YES from respondsToSelector: for this selector if the self.documentClassNames.count > 0 and if the app has a default new document type. In other words, it only responds to it if NSDocument has at least one registered document class name which can be edited.NSWindow Actions
An application can utilize the standard window tabbing actions to select tabs, merge them into one window, etc: -selectNextTab:, -selectPreviousTab:, -moveTabToNewWindow:, -mergeAllWindows:, -toggleTabBar:.By default, applications with automatic tabbing will get standard tab menu items added under the Window menu that are hooked up to these actions. However, an app may wish to add contextual menu options that perform the actions. Or, an application which opts-out of automatic tabbing (by setting NSApp.allowsAutomaticWindowTabbing=NO) may still wish to participate in tabs.
To validate the above actions, NSWindow will perform the validation inside its implementation of - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item;, but only if the selector matches one of the above items.
NSAnimation
Prior to macOS 10.12, NSAnimationEaseOut was reversed. It would perform an "ease in" animation beginning at the final value and ending at the initial value. For apps linked on or after macOS 10.12, NSAnimationEaseOut now describes a curve beginning at the initial value and ending at the final value, that slows down towards the end.NSImage
NSImage now supports multiple representations to support different user interface layout directions.This can be configured in Xcode for images loaded through asset catalogs. If you are building an image programmatically, note that NSImageRep gains a layoutDirection property. layoutDirection may take one of three values: NSImageLayoutDirectionUnspecified, NSImageLayoutDirectionLeftToRight, NSImageLayoutDirectionRightToLeft. NSImageLayoutDirectionUnspecified means that image rep is equally appropriate for left-to-right and right-to-left layout directions. It is the default value for new image reps.
When drawing an NSImage the correct layout direction is inferred based on the prevailing drawing conditions. This is the case for example inside a view's -drawRect: implementation. In some circumstances it is necessary to manually specify the desired layout direction. This can be accomplished by specifying the NSImageHintUserInterfaceLayoutDirection in the hints dictionary passed to NSImage's draw or rep selection methods.
NSColor
NSColorSpace now includes support for the “display P3” color space, which has standard DCI-P3 primaries, a D65 white point, and the same gamma curve as the sRGB IEC61966-2.1 color space. It is a canonicalization of the new iMac display profiles that is useful for tagging wide color gamut content.+ (NSColorSpace *)displayP3ColorSpace;We also have a corresponding convenience constructor in NSColor:
+ (NSColor *)colorWithDisplayP3Red:(CGFloat)r green:(CGFloat)g blue:(CGFloat)b alpha:(CGFloat)a;
NSColorSpace also exposes sRGB color spaces that enable specifying colors outside of the 0..1 range:
+ (NSColorSpace *)extendedSRGBColorSpace;These provide extended versions of existing color spaces sRGBColorSpace and genericGamma22GrayColorSpace.
+ (NSColorSpace *)extendedGenericGamma22GrayColorSpace;
To provide consistency with UIColor, the behaviors of the following methods (all introduced in 10.9) have been modified to accept extended color component values. If red, green, blue, or saturation, brightness, or white values are outside of the 0..1 range, these will create colors in the extended sRGB or extendedGenericGamma22GrayColorSpace color spaces:
+ (NSColor *)colorWithWhite:(CGFloat)w alpha:(CGFloat)a;
+ (NSColor *)colorWithRed:(CGFloat)r green:(CGFloat)g blue:(CGFloat)b alpha:(CGFloat)a;
+ (NSColor *)colorWithHue:(CGFloat)h saturation:(CGFloat)s brightness:(CGFloat)b alpha:(CGFloat)a;
Finally, we have the following new API in NSColor to enable creation of RGB-based colors from HSB component values using an arbitrary color space. An exception will be raised if the color model of the provided color space is not RGB:
+ (NSColor *)colorWithColorSpace:(NSColorSpace *)space hue:(CGFloat)h saturation:(CGFloat)s brightness:(CGFloat)b alpha:(CGFloat)a;
NSWindow deep buffer support for wide color monitors
On monitors that support wide color, NSWindow automatically increases the default depth of associated backing stores and offscreen buffers. This can be overridden by setting NSWindow.dynamicDepthLimit = NO, and also setting NSWindow.depthLimit to the desired value. The supported depths are NSWindowDepthTwentyfourBitRGB, NSWindowDepthSixtyfourBitRGB, and NSWindowDepthOnehundredtwentyeightBitRGB. NSWindowDepthTwentyfourBitRGB is the historical default value, and the value used for monitors deemed insufficiently wide. NSWindowDepthSixtyfourBitRGB is the new default for wide monitors, like the P3 panels in the current iMacs. AppKit automatically adjusts the bitdepth of layer backing stores to match the window they are hosted in. This can be overridden on a layer by layer basis using CALayer.contentsFormat.NSImage support for wide color image reps
NSImage will now use colorspace to disambiguate otherwise identical image reps. For example, on a system with Display P3 output colorspace NSImage will favor an NSBitmapImageRep whose colorspace is display P3, over an otherwise identical NSBitmapImageRep whose colorspace is sRGB.The colorspace of an image rep can be set using the existing colorSpace property of NSBitmapImageRep, or by configuring the appropriate fields in the asset catalog inspector in Xcode.
Colors in RTF Files
Colors are now preserved in RTF files with a lot more fidelity, by recording the color space and writing count components with more precision. Among other things this enables deep colors to be saved in RTF files.The extensions to the RTF spec that enable this will be published shortly.
NSDisplayGamut - New since WWDC seed
Some displays support a wide gamut colorSpace. By default, a window inherits its colorSpace from its display, so some windows support wide gamut colorSpaces as well. In 10.12, we've added a facility to check for a wide gamut screen or window so that, for example, the right image assets can be selected.The NSDisplayGamut enumeration defines buckets for sRGB and p3.
typedef NS_ENUM(NSInteger, NSDisplayGamut) {NSScreen and NSWindow have a method to check if the receiver is capable of representing the given display gamut.
NSDisplayGamutSRGB = 1,
NSDisplayGamutP3
};
- (BOOL)canRepresentDisplayGamut:(NSDisplayGamut)displayGamut;Asset selection is one intended use for this API. If you have assets of varying gamut, start with the richest and ask your destination whether it can represent it. If you need to do asset selection without a known destination, use the first screen in NSScreen's screens array.
if ([window canRepresentDisplayGamut:NSDisplayGamutP3]) {
bestAsset = P3Asset;
} else {
bestAsset = sRBGAsset;
}
NSColorList - New since WWDC seed
NSColorList has a new API which enables saving color lists using the secure keyed archiving format. This format is preferred, since in addition to being more secure, it is more complete than the older unkeyed format, enabling archiving of colorspace-based colors. Files saved is this format can be read back as early as 10.11. The API is also available back to 10.11.Like the existing writeToFile: method, specifying a nil destination causes the color list to be saved to the user's colorlists directory. This also adds the color list to availableColorLists, using the name of the color list.
- (BOOL)writeToURL:(nullable NSURL *)url error:(NSError **)errPtr;At this point, use of the existing writeToFile: method is discouraged.
NSColorPanel writes color lists that are created or edited using the color palette picker with this modern format.
NSFont
The weight for +[NSFont boldSystemFontOfSize:] is changed to NSFontWeightBold to match the instance returned from +systemFontOfSize:weight: with NSFontWeightBold.NSTextInputContext
The role of -invalidateCharacterCoordinates is clarified in the header comment. NSTextView is updated to adopt the new behavior.NSTokenField
The menu indicator inside the token is now displayed on the left of title when -userInterfaceLayoutDirection is NSUserInterfaceLayoutDirectionRightToLeft.NSLayoutManager
Just like on iOS, NSLayoutManager now renders NSLinkAttributeName with underlined blue color without any NSTextView attached on macOS for apps linked with 10.12 SDK or later.NSTextField
With the change for NSLinkAttributeName rendering for NSLayoutManager, NSTextField now renders the link without the field editor.NSTextView - New since WWDC seed
With macOS Sierra, NSTextView is transitioning to a new modern object ownership policy that is consistent throughout in behavior and aligned with the Automatic Reference Counting.In previous macOS releases, NSTextView was using dual ownership policies. With the default ownership mode, NSTextView was retained by its text container forming a reverse ownership tree structure with the NSTextStorage at the root acting as the main owner of the entire object network. The secondary ownership mode was designed for developer convenience and used only by instances initialized by -[NSTextView initWithFrame:]. With the initializer, NSTextView transparently allocated the entire TextKit object network including NSTextContainer, NSLayoutManager, and NSTextStorage. In the mode, NSTextView also retained NSTextStorage effectively creating a circular reference. To workaround the circular reference issue, NSTextView was explicitly counting its references when operating in the convenience mode. This dual ownership issue was making NSTextView not compatible with the ARC weak storage.
With the new ownership policy, we are simplifying the ownership model vastly with just a single mode serving all circumstances. The ownership policy between TextKit objects are unchanged. NSTextStorage owns a list of NSLayoutManagers, and each layout manager owns a list of NSTextContainers. There are two main differences from the default mode in the previous policy: NSTextContainer weakly referencing its NSTextView and each text view always strongly referencing its NSTextStorage. With this new policy, we can serve the flexible TextKit object network as well as NSTextView's convenient behavior freeing developers from maintaining the NSTextStorage reference.
For compatibility purpose, there is a class method controlling the ownership policy introduced, +[NSTextView stronglyReferencesTextStorage]. It returns YES by default, and all instances initialized by a class returning YES operate in the new ownership policy. You can create a subclass of NSTextView overriding the factory method if needed to work in the older ownership policy. Note that such instances are not compatible with the ARC weak storage.
Notes specific to OS X 10.11
Some of the major topics covered in this section:- Swiftification
- Force Touch related APIs
- Full Screen
- NSCollectionView
- NSSplitView
- NSStackView
- NSSearchField
- NSGestureRecognizer
- NSAlignmentFeedbackFilter (new class)
- NSHapticFeedbackManager (new class)
- NSPressureConfiguration (new class)
- NSLayoutGuide (new class)
- NSLayoutAnchor (new class)
- NSWindow
- NSTableView
- Unification of AppKit and CoreAnimation display cycles
- TextKit
- NSFont
- Dynamic Tracking
Swiftification
Framework APIs in both OS X 10.11 and iOS 9 have adopted new Objective-C features such as nullability, lightweight generics, and “kindof.” These features enable API intent to be expressed more precisely and APIs to come across better in Swift.For instance we can now indicate that nil is a valid value for the destination view when converting an NSRect between coordinate systems:
- (NSRect)convertRect:(NSRect)rect toView:(nullable NSView *)view;and that the array returned by splitting an NSString with componentsSeparatedByString: is an NSArray of NSStrings, and it will never be nil:
- (NSArray<NSString *> *)componentsSeparatedByString:(NSString *)separator;You will notice many API changes in AppKit and other frameworks due the adoption of these new features. When building your projects against the new SDKs you may get build warnings or errors; these are important to react to since in some cases they may point out actual issues. In addition, adopting these new features in your own code should allow better compile time detection of bugs.
Please see WWDC 2015 Session 202, “What’s New in Cocoa,” for more info on this “Swiftification” effort, as well as other changes in AppKit and Foundation.
NSCollectionView
We’ve brought the power, flexibility, and scalability of iOS’ widely used UICollectionView to OS X 10.11. The new and substantially improved NSCollectionView can present heterogeneous collections of variable-sized items, allows for completely customizable layout, supports sections with optional header and footer views, and recycles and lazily instantiates items to enable scaling to large numbers of objects. For optimal performance, it is designed to be layer-backed, so be sure to run with “wantsLayer” set to YES on the CollectionView’s enclosing NSScrollView or an ancestor view.To use NSCollectionView’s new API model, you specify a layout by setting an NSCollectionView’s “collectionViewLayout” property, and either provide a “dataSource” or bind your CollectionView’s “content” to an NSArray or NSArrayController (see “Binding Content to an NSCollectionView”, below). You must also disconnect and discard the NSCollectionView’s “itemPrototype”, which is a vestige of the 10.10-and-earlier API model.
Independently of whether you provide a dataSource or use the “content” binding, you can bind properties of your item view subtree’s controls to properties of the item’s representedObject.
The new “NSCollectionViewFlowLayout” class will be familiar to developers who’ve used UICollectionViewFlowLayout on iOS. It provides flexible layout of CollectionView items into rows or columns, and can accommodate items of variable size. Most clients can achieve their desired results using Flow layout, by adjusting its flexible parameters, implementing delegate methods, or if necessary subclassing. Completely developer-defined layouts are also possible, however, by subclassing either NSCollectionViewFlowLayout (advisable if the desired layout is similar to Flow) or its abstract base class, NSCollectionViewLayout.
To allow for optional grouping of items into sections, NSCollectionView uses the same two-level (section,item) indexing model as UICollectionView. A CollectionView’s content is composed of an ordered list of sections (one, by default), each of which contains an ordered list of items addressed by integer index within its containing section. Each (section,item) index pair that constitutes an item designation is encapsulated in an NSIndexPath, and existing NSCollectionView APIs that deal in single integer indices and NSIndexSets thereof have been replaced with uses of NSIndexPath and NSSets of NSIndexPaths. As on iOS, a set of NSIndexPath category methods helps with creating and examining individual NSCollectionView index paths:
@interface NSIndexPath (NSCollectionViewAdditions)A minimal NSCollectionView dataSource implements the two required methods of the NSCollectionViewDataSource protocol. The first required method returns the number of items to show in the CollectionView. (A single section is assumed, if the delegate does not implement -numberOfSectionsInCollectionView:.) The second required dataSource method instantiates an item for a given index path. Below is a simple example of implementing these methods, producing items in the CollectionView that represent a hypothetical NSArray of model objects:
+ (NSIndexPath *)indexPathForItem:(NSInteger)item inSection:(NSInteger)section;
@property (readonly) NSInteger item;
@property (readonly) NSInteger section;
@end
- (NSInteger)collectionView:(NSCollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return myModelObjects.count;
}
- (NSCollectionViewItem *)collectionView:(NSCollectionView *)collectionView itemForRepresentedObjectAtIndexPath:(NSIndexPath *)indexPath {
// Identify the model object specified by the given indexPath.
NSUInteger itemIndex = indexPath.item;
MyModelObject *modelObject = myModelObjects[itemIndex];
// Get an NSCollectionViewItem that will represent the modelObject in the CollectionView, by sending the
CollectionView -makeItemWithIdentifier:forIndexPath: with an identifier that specifies the kind of item we want to create.
(Some CollectionViews will contain only one kind of item. Others may wish to present some objects differently, by passing
different identifier values for different model objects.) If we name our item nib “Thing.xib”, we can simply pass @“Thing”
as the identifier, and NSCollectionView will find the nib file automatically. If you prefer to use nib filenames that differ
from your item identifiers, you’ll need to register the nibs you plan to use with your NSCollectionView, using the
-registerNib:forItemWithIdentifier: method. This enables the NSCollectionView to find the desired nib based on the item
identifier you provide.
NSCollectionViewItem *item = [collectionView makeItemWithIdentifier:@“Thing” forIndexPath:indexPath];
// Associate the item with our model object, using the “representedObject” property that NSCollectionViewItem inherits from
NSViewController. We aren’t required to set this property, but doing so will make it easy to find the item’s corresponding
model object, and facilitates binding properties in the item’s view subtree through the item’s “representedObject”.
item.representedObject = modelObject;
// Set up any desired state in the item’s view tree. Alternatively, we could bind control properties
through the item’s representedObject, which we set above.
item.textField.stringValue = modelObject.title;
// Return the item.When you send -makeItemWithIdentifier:forIndexPath: to a CollectionView, it first attempts to recycle an unused item of the requested type, if one is available. If a new item must be created, the CollectionView attempts to create it from an NSNib or NSCollectionViewItem subclass that was registered with the CollectionView. If you follow expected naming conventions, registering item nibs with CollectionView is unnecessary. When asked to make an item with identifier “Thing”, if no NSNib or Class has been registered with that name, NSCollectionView will first search for “Thing.nib”, then for an NSCollectionViewItem descendant class named “Thing”.
return item;
}
An item .nib must contain exactly one top-level instance of NSCollectionViewItem or a subclass. The item’s “view” outlet should be wired to the root of a view subtree that provides the item’s visual representation. Usually you want some sort of container view, containing TextFields, ImageViews, and other controls that display the item’s content.
Item size can be determined globally for all of a CollectionView’s items (by setting an NSCollectionViewFlowLayout’s “itemSize” property), or can be varied from one item to the next (by implementing -collectionView:layout:sizeForItemAtIndexPath: on your CollectionView’s delegate).
As before, you are completely free to determine item appearance. Items in a selectable CollectionView are responsible for visually indicating whether they are “selected”. OS X 10.11 adds a “highlightState” property to NSCollectionViewItem, that should also be taken into account when determining the visual appearance of an item. You may choose to show “selection” and “highlightState” indication by drawing the item slightly differently, or (since 10.11 CollectionViews operate layer-backed) by changing backing layer properties such as backgroundColor, cornerRadius, borderWidth, and borderColor, if you wish.
NSCollectionViewFlowLayout offers the potential to show multiple “sections” of items, each with an optional header view and/or footer view. These “supplementary” views are instantiated similarly to CollectionView items, by implementing the optional -collectionView:viewForSupplementaryElementOfKind:atIndexPath: dataSource method, and using NSCollectionView’s -makeSupplementaryViewOfKind:withIdentifier:forIndexPath: API from within it. The header view or footer view may be supplied in a nib (wherein it must be the only top-level NSView) or as a Class specified by name. Sizing of the header or footer is determined by implementing the delegate methods: -collectionView:layout:referenceSizeForHeaderInSection: and -collectionView:layout:referenceSizeForFooterInSection:, whose returned size values are constrained to the CollectionView.
NSCollectionViewGridLayout, unique to OS X, encapsulates NSCollectionView’s 10.10-and-earlier layout behavior, for those who might still want to use it in conjunction with the new NSCollectionView API model. NSCollectionViewGridLayout does not currently understand sections or provide for header or footer views. NSCollectionViewFlow layout is generally recommended as a more powerful, versatile, and flexible replacement.
NSCollectionView’s Accessibility implementation is now wired to its new API and content model. Some further refinement is still needed, but CollectionViews now successfully advertise their items (and now sections and supplementary views) for Accessibility navigation. Keyboard navigation from item to item has also been restored.
NSCollectionView now allows for customizing the inter-item-gap drop target indicator appearance, if desired. Register a NSView subclass, or a .nib containing a single top-level view, with your NSCollectionView, using -registerClass:forSupplementaryViewOfKind:withIdentifier: or -registerNib:forSupplementaryViewOfKind:withIdentifier: with a “kind” parameter of NSCollectionElementKindInterItemGapIndicator. Then watch for -collectionView:viewForSupplementaryElementOfKind:atIndexPath: to be sent to your NSCollectionViewDataSource with NSCollectionElementKindInterItemGapIndicator as the “kind”. Send -makeSupplementaryViewOfKind:withIdentifier:forIndexPath: to your CollectionView in response, passing the same identifier you registered with. NSCollectionView will use your custom view, when provided in this way, in place of its default inter-item gap indicator. Note that your view’s frame will be set to the bounding box of the inter-item gap, which might be larger than the indicator you want to draw (especially if items being dragged have been temporarily removed from the CollectionView, leaving a large layout hold behind). It’s up to you to determine an appropriate drawn size and appearance for your indicator within this bounding box, based on any knowledge you may have about the kind of layout that’s in use (Flow, Grid, or custom).
See WWDC 2015 Session 225, “What’s New in NSCollectionView,” and the accompanying “CocoaSlideCollection” code sample, for more information about configuring and using instances of the new NSCollectionView.
Binding Content to an NSCollectionView
If your NSCollectionView does not need sections or supplementary views, you may use NSCollectionView’s NSContentBinding to provide the model objects you want the CollectionView to display items for. The NSCollectionView’s “content” can be bound to an NSArrayController or to an NSArray of model objects.An NSCollectionView that displays only one kind of item can automatically infer the item nib or class to instantiate, provided you explicitly register a single item nib or class with the NSCollectionView. For example:
NSNib *itemNib = [[NSNib alloc] initWithNibNamed:@“MyItem” bundle:nil];In this case, you do not need to provide a dataSource. The choice of item identifier is arbitrary. — As long as the NSCollectionView finds only a single item nib or class has been registered, it will know what to instantiate.
[collectionView registerNib:itemNib forItemWithIdentifier:@“item”];
If you need to be able to display more than one kind of item in your NSCollectionView, you’ll need to provide a dataSource in addition to the content binding. Your dataSource’s -collectionView:itemForRepresentedObjectAtIndexPath: method gives you the opportunity to determine the appropriate item identifier to use when sending -makeItemWithIdentifier:forIndexPath: (just as in the non-Bindings dataSource example above). You’ll still need to implement -collectionView:numberOfItemsInSection:, since it’s a required NSCollectionViewDataSource method, but you can implement it to simply return “collectionView.content.count”. However, since you’ve used the “content” binding to tell the NSCollectionView about your model objects, you’ll find that the items returned by -makeItemWithIdentifier:forIndexPath: will have their “representedObject” property automatically set by NSCollectionView. You do not have to find the model object that corresponds to the indexPath and manually associate it with the created item. You also don’t have to explicitly register item nibs or classes when using this approach. If you prefer, you can rely on NSCollectionView to automatically.
NSPopover
NSPopover now takes the contentInsets of containing scroll views into account when tracking a view in that scroll view. This prevents issues such as the popover pointing to a view that is obscured by the titlebar.NSSplitViewItem animations
Animating the collapse or reveal of an NSSplitViewItem now uses implicit animations to animate from the start state to the end state. In 10.10, it would animate the constant of a constraint.The collapse/uncollapse animation now follows a standard timing and duration. To override the animation, set a custom animation on the NSSplitViewItem for the "collapsed" key of the animations dictionary.
Animating the uncollapse of an NSSplitViewItem will now uncollapse at a priority described by its set holdingPriority, rather than at a priority of NSLayoutPriorityRequired. This gives expected behavior, consistent with an unanimated collapse of the same item.
New NSSplitViewItem properties
NSSplitViewItem has several new properties that describe the metrics associated with that item:• minimumThickness and maximumThickness describe the minimum and maximum width or height of the item, and directly relate to .Required priority constraints managed by the NSSplitViewItem; setting these to NSSplitViewItemUnspecifiedDimension will cause no enforced minimum or maximum by the receiver, though the effective minimum and maximum could be determined by other constraints in the view hierarchy.
• preferredThicknessFraction describes the ideal percentage of the containing NSSplitView that that item takes up. This is used to return the item to that ideal fraction when the user double clicks on a neighboring divider or takes the containing window fullscreen. This does not influence how the item grows or shrinks as the split view does (the holdingPriority is responsible for that).
• automaticMaximumThickness describes the maximum thickness to which the item can be automatically resized. This affects the item when proportionally resized with the containing split view, or when the preferredThicknessFraction is used to size the item (double clicking on neighboring divider, entering fullscreen). Note this is not used as an absolute maximum, so the user or other constraints could still resize the item larger. Setting this to NSSplitViewItemUnspecifiedDimension will not enforce any automatic maximum.
• collapseBehavior describes the behavior that occurs when the item collapses and uncollapses: such as if its the containing split view (and potentially window) or the sibling items that get resized as a result. For instance, a collapseBehavior of .PreferResizingSplitViewWithFixedSiblings will attempt to the neighbor items at the current size and position on screen, typically growing the item out of the side of the window, falling back to resizing the items only if there's not enough space on screen or when in fullscreen.
When the first or last NSSplitViewItem is collapsed, if springLoaded is set to true, it will transiently uncollapse into an overlay when the user drags and hovers on the edge of the split view where the item is collapsed. Once the drag session ends, it will re-collapse. This defaults to false but should be used for collapsible items that contains drag targets, such as sidebars.
NSSplitViewItem sidebars and content lists
Sidebars in 10.11 have new behavior of automatically collapsing when the window is made smaller, and showing as overlays when at these small sizes in fullscreen. To make the notion of sidebars explicit, NSSplitViewItem has a new class constructor +sidebarWithViewController:. Sidebar SplitViewItems have the following behavior and defaults:• Visual effect material background. The item's set viewController's view should not contain its own NSVisualEffectView background. NSSplitViewItem will manage this background view, ensuring a standard material for both Aqua and Dark appearances, as well as changing the EffectView's blendMode to .WithinWindow when overlaid.
• Vibrant divider. Similarly to the material background, neighboring dividers of sidebars are vibrantly blended with the same material as the sidebar background.
• Auto-collapsing and -uncollapsing on containing SplitView size changes (see -NSSplitViewController.minimumThicknessForInlineSidebars)
• The ability to overlay at small containing SplitView sizes when in fullscreen
• .minimumThickness and maximumThickness default to standard minimum and maximum sidebar sizes
• .preferredThicknessFraction defaults to the standard fraction for sidebars
• .holdingPriority defaults to the standard for sidebars. In 10.11 this is higher than the default holdingPriority, though not guaranteed in the future.
• .canCollapse defaults to YES
• .springLoaded defaults to YES
automaticMaximumThickness does not default to an explicit value for sidebars, but it should be set to the thickness at which the content is fully visible but tightly fitted (e.g. when any text stops truncating).
NSSplitViewController's minimumThicknessForInlineSidebars describes the size at which sidebars automatically collapse when made smaller, and uncollapse when made larger again. Note that if the split view is not able to made smaller than the set thickness, sidebars will not automatically collapse. When set to NSSplitViewControllerAutomaticDimension, the default, this will use the constraints in the window to determine the size for auto-collapsing. Once the SplitView can't be made any smaller during a live resize, it will auto-collapse any sidebars. Once the split view has grown large enough to where it can completely uncollapse the sidebar, it will do so. Setting an explicit size is better for performance and ensures a more consistent experience.
NSSplitViewController also has a new action method: -toggleSidebar:, which can be used to easily and animatedly collapse the first collapsible sidebar item. For instance, a main menu item can be used to toggle sidebars in the active window by setting -toggleSidebar: as its action and using nil / the first responder as its target.
Content Lists are a similar concept that has been made explicit in 10.11, with +[NSSplitViewItem contentListWithViewController:]. These are meant to be used with split panes that show a list of contents, such as Mail's message list, Photo's collection list of photos, etc. They have the following defaults:
• .minimumThickness defaults to the standard for content lists. Content lists do not have any standard maximum.
• .automaticMaximumThickness defaults to the standard absolute size for content lists.
• .preferredThicknessFraction defaults to the standard fraction for content lists.
• .holdingPriority defaults to the standard for content lists. In 10.11 this is higher than the default holdingPriority and less than that of sidebars. This is not guaranteed to stay constant in the future.
NSSplitView
NSSplitView.debugDescription now returns a description with details on how the split view is configured:• Its delegate
• The technique it uses to layout: "constraints"/"resizeSubviews"/"resizeSubviews-autoResizingConstraints"
• How dividers are drawn: "views"/"layers"/"drawRect"/"notDrawn"
• The value of arrangesAllSubviews
In order to make the layout as flexible as possible, we recommend making sure the layout technique is "constraints". NSSplitView.debugReasonForLayoutMode will return the reason the receiver is using the layout technique it is. A common reason SplitViews cannot use constraints is that their delegate implements one of the following NSSplitViewDelegate methods:
• -splitView:constrainMinCoordinate:ofSubviewAt:
• -splitView:constrainMaxCoordinate:ofSubviewAt:
• -splitView:resizeSubviewsWithOldSize:
• -splitView:shouldAdjustSizeOfSubview:
These methods are incompatible with auto layout and are discouraged. You can achieve their effects and more with auto layout constraints.
NSSplitView Arranged Subviews
In 10.11, NSSplitView has the option to not arrange all of its subviews as split panes. By setting arrangesAllSubviews to NO (it defaults to YES), the split view is in a mode where a view added as a subview is treated as standard subview with no implications for its layout. In order to make a view be an arranged split pane, it needs to be added with -addArrangedSubview: or -insertArrangedSubview:atIndex:. Adding an arranged subview will also add that view as a subview if it wasn't already. With this, arrangedSubviews is always a subset of subviews. See header documentation for more details about the effect of setting arrangesAllSubviews and the value of arrangedSubviews.Setting arrangesAllSubviews to NO will allow AppKit to use specialized views for the dividers, rather than drawing it on the SplitView itself. In this case, the split view now returns YES for -mouseDownCanMoveWindow, and only the divider views return NO.
NSSplitViewController objects set arrangesAllSubviews to NO by default for their owned NSSplitView.
NSStackView
NSStackView has had a significant under-the-hood overhaul. It creates significantly less constraints to accomplish its layouts, as well as removes private views from its view hierarchy, using NSLayoutGuides where needed instead. Now, added arranged views are direct subviews of the stack view, whereas there had been an intermediate view in 10.9 and 10.10. The constraints created by NSStackView all have identifiers to aid in debugging layout issues.NSStackView has new distribution behaviors that describe how views are aligned in the stacking axis, using the distribution property.
• A value of .GravityAreas matches the existing behavior where views are aligned in groups along the stacking axis.
• Similarly .EqualSpacing matches the previous behavior of setting hasEqualSpacing to YES (which is now deprecated), where the arranged subviews fill the stack view with equal spacing between each view.
• A value of .EqualCentering is new behavior where the centers of the arranged views are spaced equally apart
• A value of .Fill is new behavior where the arranged views strongly fill the stack view along the stacking axis
• A value of .FillEqually is new behavior where the arranged views fill and are equally sized along the stacking axis.
• A value of .FillProportionally is new behavior where the arranged views fill and are sized proportionally based on their intrinsicContentSizes along the stacking axis.
New view management methods have been added to support adding arranged views with the new distribution behaviors, which do not have a notion of NSStackViewGravity for the second parameter of -addView:inGravity:: -addArrangedSubview:, -insertArrangedSubview:atIndex:, and -removeArrangedSubview:, which also correspond to the UISStackView view management methods. Using these methods to add arranged views are preferred when the distribution is not set to .GravityAreas.
NSStackView has a detachesHiddenViews property which can be set to cause hidden views to be detached from the stack view when their hidden value is YES. This also causes views that are automatically detached to become hidden rather than removed from the view hierarchy. This defaults to YES, matching the behavior of UIStackView, except for apps that are not linked against 10.11 SDK or later to match behavior on 10.9 and 10.10. Note that regardless of the value of detachesHiddenViews, the visibility priority for a view can be read/written to manage the detached views.
All added arranged views will have translatesAutoresizingMaskIntoConstraints set to NO if they had been YES. Before 10.11, it was left as-is and often led to unsatisfiable constraint exceptions if it had been YES.
Automatic detaching by using visibility priorities now reattaches views more consistently, such as in table view cells or with views who have weak intrinsic content size compression resistances.
Context Menu Callbacks
New NSView methods, -willOpenMenu:withEvent: and -didCloseMenu:withEvent:, can be used by the receiver to create visual effects for a menu being shown or dismissed against it. These will be called for contextual menus, and should be used instead of making visual changes in -menuForEvent:. They should not be used to make modifications to the passed in menu object.View-based TableViews in Vibrant Contexts (e.g. Popovers)
In Yosemite, an NSPopover with a standard (non-source list) NSTableView with NSTextField labels would result in an incorrect appearance where the bounding boxes around the TextField would be incorrectly blended vibrantly and look inconsistent with the rest of the table view background. This is fixed in 10.11. A workaround for 10.10 is to manually set the appearance of the TableView to NSAppearanceNameAqua.SourceList style TableViews in VisualEffectViews
In 10.11, SourceList style NSTableViews contained inside NSVisualEffectViews will inherit the containing backing rather than adding their own backing NSVisualEffectView. This containing NSVisualEffectView can be used to effectively control the blendingMode and material of the TableView's material backing. In this state, the TableView itself won't punch out content behind it, including content drawn by views between it and the containing VisualEffectView.NSDictionaryControllerKeyValuePair changes
NSDictionaryControllerKeyValuePair is now an object rather than an informal protocol.NSWorkspace deprecations
The NSWorkspaceLaunchOptions: NSWorkspaceLaunchPreferringClassic and NSWorkspaceLaunchAllowingClassicStartup have been deprecated. NSWorkspaceLaunchDefault has been updated to just include NSWorkspaceLaunchAsync.The activeApplication method has been deprecated in favor of frontmostApplication.
The mountedLocalVolumePaths and mountedRemovableMedia methods have been deprecated. Use NSFileManager’s mountedVolumeURLsIncludingResourceValuesForKeys:options: instead.
The openFile:fromImage:at:inView: method has been deprecated. The image, position, and view were ignored. Use the openURL: method on NSWorkspace.
The performFileOperation:source:destination:files:tag: method has been deprecated. Many of the file operations were unimplemented, or have replacements on NSFileManger or NSWorkspace. See the individual file operations in the NSWorkspace header for replacements.
Extended Dynamic Range support
NSScreen has a new method, maximumExtendedDynamicRangeColorComponentValue, which returns the current maximum color component value for the screen. Typically the maximum is 1.0, but if any rendering context on the screen has requested extended dynamic range, it may return a value greater than 1.0, depending on system capabilities and other conditions. Only rendering contexts that support extended dynamic range can use values greater than 1.0. When the value changes, NSApplicationDidChangeScreenParametersNotification will be posted.An NSView can request extended dynamic range by setting its wantsExtendedDynamicRangeOpenGLSurface property to YES. If any view on the screen has this enabled, the NSScreen which the OpenGL surface is on may have its maximumExtendedDynamicRangeColorComponentValue increased. When composited by the Window Server, color values rendered by this OpenGL surface will be clamped to the NSScreen’s maximumExtendedDynamicRangeColorComponentValue rather than 1.0.
NSWindow Full Screen and Spaces restoration
In previous versions of OS X, non-fullscreen windows which used NSWindow’s encodeRestorableStateWithCoder: and restoreStateWithCoder: implementations (including those that call through to super) would, on login only, have their windows restored to the space they were on previously. In OS X 10.11, this behavior has been extended to full screen windows.NSSearchField Cancel Button Visibility
The previous behavior was that the cancel button became visible whenever the search field had focus or had text. Now, the cancel button will only show when the field contains text.NSSearchField Notifications for Searching
Some applications present in a special mode when performing a search. For example, Finder changes the title of the window, inserts a bar with additional search options, and filters contents accordingly. Some applications continue to present a special mode after any text entered in the search field is cleared, while other applications exit the mode when text is cleared.In an attempt to have a consistent experience, NSSearchField will now notify when the user is searching. The user is considered to be searching starting when the first action message is sent with non-empty text. Searching ends when text is cleared. We've added a new delegate protocol which will be the vehicle for these notifications.
@protocol NSSearchFieldDelegate <NSTextFieldDelegate>
@optional
- (void)searchFieldDidStartSearching:(NSSearchField *)sender;
- (void)searchFieldDidEndSearching:(NSSearchField *)sender;
@end
@interface NSSearchField : NSTextField
...
@property (assign) id<NSSearchFieldDelegate> delegate;
@end
NSSearchField Option for Centered Placeholder
We've added a new "centersPlaceholder" property that controls whether the placeholder, magnifier search button, and search menu are centered when the search field does not have focus and does not have text. With focus, the control animates its contents to the edge.The default value for centersPlaceholder is YES since the centered-look described above was the default in Yosemite.
With the centered-look, the only way to have an empty placeholder is to use a space, “ “, since nil or the empty string will use the “Search” default. If linked on or after 10.11, only nil will result in the default, no matter what centersPlaceholder is.
Since the search field can appear centered or not, we’ve added a “centered” parameter to the custom layout methods in NSSearchFieldCell. Note that the value will not always be the same as the search field’s focus state, nor is it the same as the new centersPlaceholder parameter which states the ability to be centered. The older NSSearchFieldCell versions remain since they may be useful to those who implement custom search fields using cells.
@property BOOL centersPlaceholder;Defaults to YES. When set, the search field's components are centered within the control if the field is empty and does not have focus. When receiving focus, given the field is empty, the centered objects will animate to the edges of the control. When this property is set to NO, the components are always at the edge. When YES, the configuration requires wantsLayer to also be YES. When NO, wantsLayer should be NO for standard appearance but can be YES for custom drawing.
- (NSRect)rectForSearchTextWhenCentered:(BOOL)isCentered;The rectangle for the search text-field within the bounds of the search field. Subclasses can override this method for custom layout purposes. The isCentered parameter is YES when centersPlaceholder is YES and the caller is interested in the centered appearance. It is no either when centersPlaceholder is NO or when centersPlaceholder is YES and the caller is interested in the non-centered appearance. Use this instead of -[NSSearchFieldCell searchTextRectForBounds:].
- (NSRect)rectForSearchButtonWhenCentered:(BOOL)isCentered;The rectangle for the search button within the bounds of the search field. Subclasses can override this method for custom layout purposes. The isCentered parameter is YES when centersPlaceholder is YES and the caller is interested in the centered appearance. It is no either when centersPlaceholder is NO or when centersPlaceholder is YES and the caller is interested in the non-centered appearance. Use this instead of -[NSSearchFieldCell searchButtonRectForBounds:].
- (NSRect)rectForCancelButtonWhenCentered:(BOOL)isCentered;The rectangle for the cancel button within the bounds of the search field. Subclasses can override this method for custom layout purposes. The isCentered parameter is YES when centersPlaceholder is YES and the caller is interested in the centered appearance. It is no either when centersPlaceholder is NO or when centersPlaceholder is YES and the caller is interested in the non-centered appearance. Use this instead of -[NSSearchFieldCell cancelButtonRectForBounds:].
NSTextField maximumNumberOfLines property for wrapping text
It’s desirable for developers to restrict the number of lines of a wrapping text field, especially when the field reflows with changes in the layout. To accommodate that, we’ve introduced a new property for NSTextField called maximumNumberOfLines.@interface NSTextFieldFor text that wraps (see NSCell’s lineBreakMode), this property determines the maximum number of lines to display. A value of 0 means there is no limit, which is the default, and the text fills the cell’s bounds. If the text reaches the number of lines allowed, or the height of the container cannot accommodate the number of lines needed, the text will be clipped (or truncated if truncatesLastVisibleLine is set). The value of this property also affects -[NSControl's sizeToFit], -[NSView fittingSize] and -[NSView intrinsicContentSize]. Most importantly, if the value of this property is not 1, multiple lines may be used to find the field’s intrinsicContentSize. Prior to 10.11, the intrinsicContentSize would be determined as if the maximumNumberOfLines was 1.
@property NSInteger maximumNumberOfLines;
@end
This is similar to UILabel’s numberOfLines property. There are important differences though (thus the name change). First and foremost, NSTextField’s version only works when the lineBreakMode is set to wrap, NSLineBreakByWordWrapping or NSLineBreakByCharWrapping, while UILabel’s functionality is also subject to other line break modes that clip or truncate. Our reasoning is that the maximum number of lines is a concept of the container and not the text model. They should be independent. Second, UILabel has an unfortunate affect that reflowing does not happen if preferredMaxLayoutWidth is set to any value, including 0, a value for us indicates no preferred width. We want to reflow the field especially when preferredMaxLayoutWidth is set to 0.
Here are some notes on maximumNumberOfLines’ compatibility with other NSCell text-related properties:
scrollable - Text wrapping only applies when scrollable is NO. So maximumNumberOfLines is only in affect when scrollable is not set.
wraps - This is a convenience that returns YES if lineBreakMode is word or character wrapping. Setting it to YES also sets scrollable to NO. maximumNumberOfLines then is only observed when YES.
usesSingleLineMode - When set to YES, wrapping is ignored, and if lineBreakMode is set to word or character wrapping, it is interpreted as NSLineBreakByClipping. maximumNumberOfLines then is only observed when NO.
truncatesLastVisibleLine - When set to YES, when text overflows its container, adds ellipses to the last visible line when lineBreakMode is set to word or character wrapping. So if the text exceeds maximumNumberOfLines text will be truncated when YES and clipped when NO.
When maximumNumberOfLines is not 1, auto layout can now change the width and height of the text field as long as maximumNumberOfLines (and preferredMaxLayoutWidth) is respected. Which means that the intrinsic, or natural, content size of a text field can now vary with the field’s container instead of being the size of the text that fits on a single line.
NSTextField intrinsicContentSize improvements
Previously, unless NSTextField’s preferredMaxLayoutWidth was set to something other than 0, the -intrinsicContentSize method would measure the text field with its contents on a single line, even if wrapping was enabled. Because of this behavior, the auto layout engine would not be able to find a size for a text field that spanned multiple lines, even though doing so would produce an acceptable and aesthetically better layout.We’ve changed things such that the autolayout engine provides a width that acts like the preferredMaxLayoutWidth for the NSTextField, allowing for a text field to reflow into multiple lines. This works only for apps linked against 10.11 that have wrapping text and a preferredMaxLayoutWidth of 0. Also, it only works if the constraints on the text field allow for content compression.
Opting out:
There are some cases where you may want to opt out of the above behavior. Here are two known scenarios:
1. The height component of intrinsicContentSize may not update correctly if the string (including attributed string) contents of the text field change such that the field’s height should change but the width should remain the same. The result is a text field that either clips or is too tall. This is a known bug and you may want to opt out using the instructions below.
2. Text may wrap and the height of the text field may increase even if the line break mode of the text field is set to truncate or clip. This can occur if you have an attributed string set on the text field. When using an attributed string, its paragraph style (which includes line break mode) takes precedence over the corresponding properties of the text field and the text field cell. Also note that the default line break mode is to wrap, which means if your attributed string does not have an explicit paragraph style set, it’s assumed to wrap.
There are a few options to opt out of the new behavior, on a per text field basis. First, if the field should only have a single line of text, turn on single line mode on the text field. You can also set the maximumNumberOfLines to 1. However, if you require more than one line, you can also set the maximumNumberOfLines property to a value that makes sense - as long as it is not 0. Another option is to set a preferredMaxLayoutWidth to something other than 0 (which you have to do in code, there is no way to do this in Interface Builder). For all these workarounds, please do them as soon to creation as possible.
When using attributed strings, remember that not specifying a paragraph style is the same as using the default paragraph style, which includes a wrapping line break mode. So being explicit with a truncating or clipping line break mode is preferable.
NSGestureRecognizer Additions
NSGestureRecognizer now has a -pressureConfiguration property, allowing an NSPressureConfiguration to be automatically set by the gesture engine. When multiple gesture recognizers are active, the most compatible behavior will be set allowing for the broadest range of input. As gesture recognizers fail or begin, this effective configuration will be updated. If all gesture recognizers fail, the configuration will revert to the underlying view's configuration or the system default, as appropriate.NSGestureRecognizerDelegate Additions
NSGestureRecognizerDelegate now allows filtering of which gesture recognizers should be allowed to participate in an event stream. This is useful for applications which don't want to subclass a built in recognizer. For example, an application might only support rotation in part of a view. On the first event which initiates the stream, the delegate will be asked if the recognizer should be allowed to recognize with the event. If YES the event processing will continue as normal. If NO, the recognizer will be excluded from the active set. Note that the recognizer will not have received the event when the delegate method is invoked, so -locationInView: will not be up to date.NSAlignmentFeedbackFilter
AppKit provides clients with a convenient means of performing haptic feedback for alignment guides. These are used when the user is dragging an object on the screen, and the object "snaps" to known locations on screen.While NSHapticFeedback allows clients to perform arbitrary haptic feedback, NSAlignmentFeedbackFilter helps the client perform feedback only when necessary. Often when implementing alignment guides, an application will track which object the aligned object is aligned to. A naive implementation could perform feedback when this object changes, but there are many common cases where this will perform extraneous feedback and annoy the user. The feedback filter accepts requests to perform feedback, but takes cursor velocity, snap distance, and other heuristics into account to make sure the alignment feedback feels correct.
The feedback filter uses a prepare and commit model. The app must feed the object events so that it can track cursor state. The app then asks for alignments to be prepared, and when it has chosen one or more alignments it can ask the object to perform feedback for those alignments.
The prepare methods take 3 locations. Feedback should be performed when the item jumps on screen due to alignment, so the filter has to take the previous location and the proposed alignment location. If the filter only took these two though, an item would get "stuck" as soon as it hit a guide -- how would the controller know when to release it? So, it takes a third "fallback" location which will be used if the item should become un-stuck from the guide as the user drags away.
The prepare methods come in 3 variants. Some alignments only affect the x or y coordinates, and some affect both. As such, for any given movement a controller could have multiple prepared alignments.
For every relevant event (tracking loop matching +inputEventMask), or action from gesture recognizer:
Inform the filter of the current event (-updateWithEvent: or -updateWithPanRecognizer:)
Store where the item currently is ("previousPoint")
Move the item by the appropriate delta for the mouse movement - this may be 0.0pt or several pt,
depending on image space transformations.
Store where the item currently is ("defaultPoint")
Determine the distance you'd like to align the item (for example, 5px up to match a horizontal guide, "alignedPoint")
If alignment filter says you may move by that distance
(-alignmentFeedbackTokenFor[Horizontal|Vertical]MovementInView:previousPoint:alignedPoint:defaultPoint:)
Perform the feedback (performFeedback:performanceTime:)
Move the item to the alignedPoint
Else
Move the item to the defaultPoint
NSWindow Split View resize increments and aspect ratio
NSWindow's resizeIncrements, aspectRatio, contentResizeIncrements, and contentAspectRatio will be ignored when determining window size in Split View. A window should do its best to fill the entire size of the tile, and these window properties would circumvent the system's ability to size two windows side by side.Full Screen Menu Item
AppKit automatically creates an "Enter Full Screen" menu item after the application finishes launching if an equivalent menu item isn't found. If this menu item should not be created for your app, before NSApplicationDidFinishLaunchingNotification is sent you may set the NSFullScreenMenuItemEverywhere default to NO.- (void)applicationWillFinishLaunching:(nonnull NSNotification *)notification {
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"NSFullScreenMenuItemEverywhere"];
}
Force Touch Trackpad Haptic Feedback
Haptic feedback should be performed sparingly. It should be a subtle interaction with the user during key moments of user input. Additionally, in general you should only perform haptic feedback when the input velocity strongly implies that the user is specifically trying to find the key spot. Also see NSAlignmentFeedbackFilter which can do a lot of this for you in certain circumstances.Use the NSHapticFeedbackManager to get the default haptic feedback performer. Then ask the NSHapticFeedbackPerformer to perform the feedback. Always ask for the default feedback performer as it may change based on the user's accessibility preferences and the current input device.
NSHapticFeedbackPerformer
func performFeedbackPattern(NSHapticFeedbackPattern, performanceTime:NSHapticFeedbackPerformanceTime);
NSHapticFeedbackPatterns• .Alignment - Use when visually aligning objects. This includes re-sizing, range limits, horizon alignments, etc..
• .LevelChange - Use when there are discreet changes due to pressure. For example, this is used by NSButton when set to NSMultiLevelAcceleratorButton type.
• .Generic - Use when neither of the option options are appropriate.
NSHapticFeedbackPerformanceTimeIt is important that the user feel the feedback at the same time the correlated UI occurs on screen. However, you should not perform haptic feedback in the middle of drawing. Instead, set the appropriate performance time when performing haptic feedback.
• .DrawCompleted - Use when haptic performance should occur when the next cocoa or CoreAnimation drawing completes.
• .Now - Use when using OpenGL or there is no correlated UI change.
• .Default - Same as NSHapticFeedbackPerformanceTimeDrawCompleted.
Force Touch Trackpad Configuration
By default, the force touch trackpad is configured for deep click. That is, it will force click if the user exerts sufficient force on the trackpad while the cursor is stationary This way, the user always gets feedback that they have indeed pressed hard enough for a force click even if the app doesn't respond to it. However, this is not appropriate for all UI. For example, force clicks do not occur on the menu bar, in menus, nor during pressure-sensitive drawing.There are two ways to configure the trackpad. Both start off with the initialization of an NSPressureConfiguration object with an NSPressureBehavior.
See NSEvent.h for the list of NSPressureBehaviors and a description of each one.
• Preferred - Set the pressureConfiguration property on an NSView. This is the most responsive method. The pressure configuration will be applied by the system as soon as the cursor enters the view, even if your application is not responsive yet.
• Alternate - Call set() on an NSPressureConfiguration object. The configuration is only applied if the user is holding the mouse down, the configuration will be reset on mouse up. Note: This means that you are racing the user. In some cases, the user may perform a mouse down and up before your application even receives the initial mouse down event. This approach is best used when you absolutely have to consult the mouseDown location before determining the correct configuration.
NSDocument Deadlock Avoidance
Autosave, iCloud Drive, and File Coordination introduced a great deal of asynchronous behavior to NSDocument. The APIs -performSynchronousFileAccessUsingBlock: and -performActivityWithSynchronousWaiting:usingBlock: were added to ensure that NSDocument operations, both UI-level and file-level, happened in a sensible and serial manner. Unfortunately, the current implementation of these APIs combined with NSDocument's historical reliance on the main thread have made these APIs vulnerable to deadlocks. A great deal of work has been done to understand and prevent these deadlocks.A certain class of deadlock was identified that is caused by inappropriate use of the -performAsynchronousFileAccessUsingBlock:, -performActivityWithSynchronousWaiting:usingBlock:, and -continueAsynchronousWorkOnMainThreadUsingBlock: APIs. The following code demonstrates the inappropriate pattern:
[self performAsynchronousFileAccessUsingBlock:^( void (^fileAccessCompletionHandler)(void) ) {
... do some work asynchronously ...
[self continueAsynchronousWorkOnMainThreadUsingBlock:^{
fileAccessCompletionHandler();
[self performActivityWithSynchronousWaiting:YES usingBlock:^( void (^activityCompletionHandler)(void) ) {
...
activityCompletionHandler();
}];
}];
}];While this looks innocuous, it turns out that if another activity was initiated prior to this code, and that activity called performSynchronousFileAccessUsingBlock: on the main thread after the above file access began, this code results in a deadlock. The above performActivityWithSynchronousWaiting:YES call above never returns (because the prior activity is still active). Also performSynchronousFileAccessUsingBlock:, whose blocking of the main thread was 'interrupted' by continueAsynchronousWorkOnMainThreadUsingBlock:, can never run because the 'interrupting' block never returns, resulting in deadlock.
In OS X 10.11, code to detect and avoid this particular deadlock has been added to NSDocument. When detected, NSDocument will log a message to the console starting with "An attempt was made to call performActivityWithSynchronousWaiting:YES within a block passed to continueAsynchronousWorkOnMainThreadUsingBlock:". The message will also print diagnostic information (the contents of _NSDocumentSerializationInfo()) to help you identify the call sites of performActivityWithSynchronousWaiting:YES, and other information necessary to understand the situation. Finally, NSDocument will automatically defer performing the activity via CFRunLoopPerformBlock() to avoid the deadlock.
If you encounter the above message during testing, we recommend avoiding this deadlock by deferring the above performActivityWithSynchronousWaiting:YES call until a future run loop cycle via CFRunLoopPerformBlock or similar API, or passing NO for the SynchronousWaiting: parameter.
NSSegmentedControl
NSSegmentedControls in applications linked on 10.11 or later may or may not contain subviews. Developers should not assume when enumerating subviews of an NSSegmentedControl that the subviews method will return an empty array, or that any subviews returned are only those added by the Developer or that the subviews are always of a specific class. Developers are advised to filter on view tag or identifier to find specific views they are searching for, and use respondsToSelector checks if invoking methods on random subviews of an NSSegmentedControl.NSOpenPanel
A new property has been added to NSOpenPanel:/* Gets and sets the disclosure state of an accessory view in an NSOpenPanel.In 10.11, most NSOpenPanels have adopted a design that was formerly reserved only for applications which adopted iCloud Documents support and this panel design was only used if iCloud Drive was turned on in System Preferences. This note will refer to the design as the “iCloud Open Panel Design” although the design is no longer limited to applications with iCloud Documents support. The iCloud Open Panel Design introduced an NSOpenPanel behavior where accessory views are hidden by default, and an “Options” button is available in the bottom controls of the panel to disclose / hide an accessory view by user action. To prevent unexpected UI behavior changes in 10.11, NSOpenPanels in all applications linked before 10.11 which were not already using the iCloud Open Panel Design (meaning they did not adopt iCloud Documents support) will always disclose accessory views by default. The “accessory views disclosed by default" behavior matches prior shipping OS behavior where these applications would be using the "Non iCloud Open Panel Design." If your application is linked on 10.11 or later, accessory views set on an NSOpenPanel will be hidden by default whether or not your application adopted iCloud Documents support. If you find a default hidden accessory view to be undesirable, you can use the new property of NSOpenPanel to get and set the disclosure state. It is recommended you use this property before you run or begin an NSOpenPanel. This property is not preserved between panel runs, and the value of this property is also dependent on whether an accessory view has been set, and whether the panel design supports disclosure and hiding of accessory views.
If hiding / disclosing an accessory view is not applicable this property will
behave like a read only property.
*/
@property (getter=isAccessoryViewDisclosed) BOOL accessoryViewDisclosed;
NSSavePanels do not use the iCloud open panel design and do not have disclosing / hiding accessory view functionality. The accessory view in an NSSavePanel is always visible and cannot be hidden.
NSOpenPanel
Prior to 10.11, sandboxed open and save panels always granted a sandbox extension to the result of a successfully completed panel, even if the client of the panel did not explicitly request the results via the methods that retrieve an url or urls. The pre 10.11 behavior was relied on by some client applications, which were inferring the panel results via other means. On 10.11, clients must explicitly request the results of the panel by calling the URL or URLs methods, or a sandbox extension will not be granted, and any attempt to access the resources pointed at the by the result of the completed panel will run into sandbox access restrictions. Please add calls to the URL or URLs method to your code, even if you think you already know the result.NSLayoutGuide
NSLayoutConstraints will now accept instances of NSLayoutGuide as layout items. This provides an easy way to calculate intermediate view geometry without having to create dummy views. Previously, dummy views were required in order to achieve equal or proportional spacing between views.Layout Anchors
NSView and NSLayoutGuide now provide a set of properties to aid concise creation of NSLayoutConstraints. The properties are subclasses of NSLayoutAnchor, and can be used roughly like this:NSLayoutConstraint *constraint = [myButton.leadingAnchor constraintEqualToAnchor:container.leadingAnchor constant:10];The factory methods are available in multiple forms to allow omission of extraneous parameters, and can be activated inline if a reference to the constraint is not needed. E.g.,
[entryField.leadingAnchor constraintGreaterThanOrEqualToAnchor:label.trailingAnchor].active=YES;
NSMenuItemCell
-setTag: now sets the tag value on the cell’s NSMenuItem.NSMenu
NSMenu now provides a userInterfaceLayoutDirection property for explicit control of the menu content layout direction. If not specified, a menu uses the layout direction of the application. The layout direction of a menu may be set explicitly in Interface Builder.NSApplication
10.11 supports a new type of menubar behavior that hides the menubar during normal non-fullscreen interaction. The menubar shows itself automatically when the mouse moves into a hot area at the top of each display. When this mode is enabled, the NSApplication.presentationOptions property will include the NSApplicationPresentationAutoHideMenuBar value.Prior to 10.11, the SetSystemUIMode API provided by HIToolbox, and the setPresentationOptions API of NSApplication provided by AppKit, did not allow explicitly enabling an auto-hiding menubar without also hiding the Dock. -setPresentationOptions now allows the options to contain AutoHideMenuBar without also including HideDock or AutoHideDock. To ensure compatibility with existing applications, the SetSystemUIMode API will only allow applications linked on 10.11 and later to pass the combination of kUIModeNormal and kUIOptionAutoShowMenuBar; if this combination is specified by an application linked on Yosemite or earlier, the AutoShowMenuBar option is ignored.
NSWindow / Full Screen
NSWindow will implicitly opt certain candidate windows into becoming full screen capable. An explicit full screen capable window has the NSWindowCollectionBehaviorFullScreenPrimary bit set in the window.collectionBehavior. A candidate for an implicitly full screen capable window will be a titled NSWindow that is resizable, and can resize to fit the entire screen. The exact semantics of this may change over time, and it is recommended to explicitly opt in primary document windows by including NSWindowCollectionBehaviorFullScreenPrimary in the collectionBehavior.When in full screen, if a new window is shown the following logic takes effect: A new full screen space will be created for explicit full screen windows. For implicit full screen windows, the window will simply be shown on the current full screen space. These exact semantics may change over time.
NSWindow now supports full screen tiling (Split Screen). Full screen tiling allows two separate windows to share the same full screen space. The system will implicitly determine candidate windows for a full screen tile based on window properties, such as the window being a full screen candidate (either explicit or implicit). A window can explicitly allow or disallow itself to be tiled by including NSWindowCollectionBehaviorFullScreenAllowsTiling or NSWindowCollectionBehaviorFullScreenDisallowsTiling in the window.collectionBehavior. For example, a non resizable window may still want to explicitly allow itself to be placed in a full screen tile and would include NSWindowCollectionBehaviorFullScreenAllowsTiling. Or, a full screen capable window may want to disallow tiling; this means it will never be placed with a secondary tile window, and will always be alone when in full screen.
Once a window is allowed to be placed in a full screen tile, the system will check to see if it actually fits in a target tile for a given screen resolution. The minFullScreenContentSize is then queried, and if it fits it will be placed in a tile. In general, one does not need to set minFullScreenContentSize, and it will be implicitly determined by auto layout. If the window is not using auto layout, it will fall back to using the contentMinSize or minSize of the window. This is usually sufficient for a window that does not significantly change its layout when in full screen. However, if it does have a significant layout change, then the window can explicitly set the minFullScreenContentSize and maxFullScreenContentSize as desired. If a window wants to force allow tiling and respect any size it is given, it can include NSWindowCollectionBehaviorFullScreenAllowsTiling and set the minFullScreenContentSize to 0,0.
NSWindows in full screen will no longer get a call to constrainFrameRect:toScreen: for applications linked on 10.11 or later. Instead, the window's frame will automatically be constrained within the tile space it is allowed to be in.
NSWindow
The use of NSDrawer is discouraged. Drawers are rarely used in modern Mac apps. As much as possible, redesign your UI to avoid using drawers; if you’re creating a new app, avoid adding a drawer to the design.In OS X 10.10.0, sheets were not offset by a custom value set for [self contentBorderThicknessForEdge:NSMaxYEdge]. In 10.11 this has been fixed for all apps.
[NSWindow windowWithContentViewController:] would always incorrectly create a class of NSWindow, even if the method was called on a subclass of NSWindow. In 10.11 this has been fixed for all apps, and applications targeting a lower OS release should not use the method unless a regular NSWindow is desired.
NSWindow's NSTitlebarAccessoryViewController now supports a layoutAttribute of NSLayoutAttributeLeft for applications linked on 10.11 or later. Prior to 10.11, more than one item with NSLayoutAttributeRight may not have been presented correctly; this has been fixed.
For applications linked on 10.11 and higher, a NSTitlebarAccessoryViewController with the layoutAttribute set to NSLayoutAttributeRight will no longer right indent toolbar items, unless the titleVisibility == NSWindowTitleHidden. This allows placing a view/button/textfield (etc) above the toolbar without right indenting the toolbar. However, for the NSWindowTitleHidden, there is still a desire to indent the toolbar on the right to leave space for the accessory view.
A borderless NSWindow with a contentView that is an NSVisualEffectView with a maskImage will correctly round the corners; previously, the corners may have been jagged. The contentView must be an NSVisualEffectView for this to happen.
Prior to 10.11, a window with titlebarAppearsTransparent=YES would not allow any views under the “virtual” titlebar area to get mouse events. Commonly this would show up in applications that place a search field in this area. This has been fixed on 10.11 for all applications.
NSTableView / NSOutlineView
NSTableView now supports a new feature dubbed “Swipe To Delete”. The delegate can implement tableView:rowActionsForRow:edge: and return an array of NSTableViewRowAction objects that represent buttons. Typically these buttons are used to delete a row, or show more information. See NSTableView’s header, and NSTableViewRowAction.h for more information.NSTableView now has API to hide individual rows: hideRowsAtIndexes:withAnimation:, unhideRowsAtIndexes:withAnimation:, and hiddenRowIndexes. This API has been made public in 10.11, but can be used back to 10.10.2. Hiding rows may be desired when it is not possible to quickly update the datasource to represent changed data. Instead, rows can temporarily be hidden, and later deleted. Visually a hidden row looks and acts the same as a deleted row. Note that hiding a row may or may not actually set the rowView.hidden property; this is an implementation detail that may change, so do not depend on rowView.hidden necessarily being YES for hidden rows. Hiding a row that is currently selected will leave it selected; developers should un-select these rows either before or after hiding it — otherwise the user will be confused because there is a hidden row that is also selected.
NSTableView will now set the objectValue to nil for NSTableCellViews when they are removed from the table. This will help avoid unexpected references to objects when the NSTableCellViews are placed in the table's re-use queue.
NSTableView now returns the minimum frame size from the auto layout method intrinsicContentSize.
The NSTableViewSelectionHighlightStyleSourceList should now only be used when the background color has not been changed from the default color. Normally, the table adds an NSVisualEffectView to implement the blur effect, but if it detects a custom backgroundColor was set it will not attempt to do this. Previously the selection would still draw with a blur. On 10.11 the selection will now draw as a normal selection highlight style.
NSViewController
For applications linked on 10.11 and later, NSViewController has better Swift class name searching abilities to load the NIB. First, the full Swift class name will be searched for in the bundle and mainBundle, such as MyApplication.ClassName. If a NIB can not be found with that name, it will then attempt to search for just “ClassName”. This allows one to easily load a NIB by doing a simple view controller initialization without having to pass an explicit nibName.NSVisualEffectView
NSVisualEffectView has additional materials available, and they are now organized in two types of categories. First, there are abstract system defined materials defined by how they should be used: NSVisualEffectMaterialAppearanceBased, NSVisualEffectMaterialTitlebar, NSVisualEffectMaterialMenu (new in 10.11), NSVisualEffectMaterialPopover (new in 10.11), and NSVisualEffectMaterialSidebar (new in 10.11). Use these materials when you are attempting to create a design that mimics these standard UI pieces. Next, there are specific palette materials that can be used more directly to create a specific design or look. These are: NSVisualEffectMaterialLight, NSVisualEffectMaterialDark, NSVisualEffectMaterialMediumLight (new to 10.11), and NSVisualEffectMaterialUltraDark (new to 10.11). These colors may vary slightly depending on the blendingMode set on the NSVisualEffectView; in some cases, they may be the same as another material.Prior to 10.11, the material NSVisualEffectMaterialTitlebar would slightly round the top edges of the view (applying corners). This has been fixed for applications that use the material in locations other than the top of a window.
NSApplication
NSApplication will now crash on uncaught exceptions for applications linked on 10.11 and higher. It is highly recommended to test applications with the default: “-NSApplicationCrashOnExceptions YES” , as this enables the behavior without having to be linked on 10.11 and allows developers to catch problems.Unification of AppKit and CoreAnimation display cycles
AppKit deferred layout and display now occur at the same time as CoreAnimation deferred layout and display. AppKit layout and drawing operations between +[NSAnimationContext beginGrouping] and +[NSAnimationContext endGrouping] will be treated as an atomic unit (this also applies to +[CATransaction begin] and +[CATransaction commit].) This can be used in place of NSDisableScreenUpdates and NSEnableScreenUpdates. The use of these two functions is discouraged. Likewise, it should no longer be necessary to use +[NSWindow disableScreenUpdatesUntilFlush] (its use is also discouraged.)NSColorList
NSColorLists now conform to NSSecureCoding and can be securely unarchived. We encourage you to support NSSecureCoding in any custom subclasses of NSColor you may have in your applications.TextKit for OS X
Mac OS X 10.11 introduces all the functionalities added to iOS TextKit including the exclusion paths, the maximum number of lines restriction, CGGlyph-based NSLayoutManager API, detailed layout controls via NSLayoutManagerDelegate, etc. The same TextKit API is now available for all platforms.NSFont
NSFont now offers factory methods for querying system fonts with additional weights such as ultra light or heavy. +systemFontOfSize:weight: and +monospacedDigitSystemFontOfSize:weight: both take NSFontWeightTrait. It's recommended to use the 9 predefined NSFontWeightTrait values in NSFontDescriptor.h. When a weight that's not supported by the running environment, NSFont uses the following fallback logic.The runtime version dependent behavior is designed to minimize impacts coming from future OS X introducing additional weights. To summarize the behavior:
(A) if the app is linked on the current or older SDK:
return the system font corresponding to the next higher available weight (so for instance a request for unavailable SemiBold returns Bold)
this makes sure that if a weight if supported in the future release, the space taken up the element will be no wider, and still fit
(B) if the app is linked on a future SDK:
return the system font corresponding to the next lower available weight (so a request for unavailable SemiBold returns Medium)
this makes sure that an element ends up taking space no wider than what it does when run on the future system
The system font APIs (i.e. +systemFontOfSize:) now returns a font instance with proportional digit glyphs. +monospacedDigitSystemFontOfSize:weight: provides access to monospaced digit glyphs.
NSFontManager
API related to NSFontDescriptor and NSFontCollection provided by NSFontManager is formally deprecated. NSFontManager is now focusing to its main task for managing text attributes of the selected object.-[NSFontManager sendAction] was incorrectly declared as a property in previous Mac OS X releases. It is corrected and now back to be an instance method.
-fontManager:willIncludeFont: is formally deprecated. It was never implemented on Mac OS X. Developers seeking a way to filter fonts displayed to users should use NSFontCollection.
NSTextFieldCell
-[NSTextFieldCell dealloc] no longer invokes -[NSNotificationCenter removeObserver:] with itself. That behavior had a side effect of implicitly unregistering for any notification observer registered by subclasses. Now, it is the responsibility of subclasses to properly unregister.NSTextView
-[NSTextView insertText:] is now formally deprecated. -insertText:replacementRange: from NSTextInputClient should be used as the overriding point for customizing text input behavior.NSAttributedString
NSCharacterShapeAttributeName is deprecated. The functionality for customizing font features is available through NSFontDescriptor instead. Along with the attribute, -[NSTextView toggleTraditionalCharacterShape:] is deprecated.Dynamic Tracking (available for OS X and iOS)
The dynamic tracking is a text feature, typically for UI elements, that a single line title attempts to tighten inter-character spacing/tracking for fitting into a narrower space before truncation. It's controlled by a new NSParagraphStyle property, -allowsDefaultTighteningForTruncation . The behavior is similar to OS X feature controlled by -[NSParagraphStyle tighteningFactorForTruncation]. The main difference is that the range of ratio between the line and available space is automatically determined for the API clients (based on information coming from fonts) with the new text feature unlike the existing API controlling the ratio explicitly. -tighteningFactorForTruncation!=0.0 enables the existing tightening behavior when -allowsDefaultTighteningForTruncation state=YES with binaries linked with Mac OS X 10.11 or later SDK. For pre-10.11 binaries, -tighteningFactorForTruncation is always preferred regardless of -allowsDefaultTighteningForTruncation value. Also, explicitly setting 0.0 to -tighteningFactorForTruncation has a side effect of disabling -allowsDefaultTighteningForTruncation. -allowsDefaultTighteningForTruncation is YES by default for apps linked against Mac OS X 10.11 or later SDK. It's disabled for pre-10.11 applications and on iOS.NSText
NSTextAlignment enums (NSTextAlignmentLeft, NSTextAlignmentRight, etc) is replacing the existing values. The old values are informally deprecated.NSAttributedString
NSTextWritingDirection is replaced by NSWritingDirectionFormatType. NSTextWritingDirection is formally deprecated.NSUnderlineByWordMask is replaced by NSUnderlineByWord. NSUnderlineByWordMask is formally deprecated.
-[NSAttributedString containsAttachments] is replaced by -containsAttachmentsInRange:. -containsAttachments is informally deprecated.
Document format reading methods (i.e. -[NSAttributedString initWithURL:documentAttributes:]) without NSError arguments are now deprecated.
-[NSAttributedString URLAtIndex:effectiveRange:] is deprecated. NSDataDetector is the preferable replacement.
NSLayoutManager
NSGlyph and related APIs are replaced by CGGlyph-based TextKit counterparts. NSGlyphAttribute and NSGlyphInscription are replaced by NSGlyphProperty.New CGGlyph-based API:
-CGGlyphAtIndex:isValidIndex:
-CGGlyphAtIndex:
-setGlyphs:properties:characterIndexes:font:forGlyphRange:
The API for receiving the editing notification from NSTextStorage, -textStorage:edited:range:changeInLength:invalidatedRange: is replaced with -processEditingForTextStorage:edited:range:changeInLength:invalidatedRange.
There are additional functionalities brought over from iOS TextKit. Developers can now identify line fragments that are truncated with -truncatedGlyphRangeInLineFragmentForGlyphAtIndex:. Bounding rects enumeration APIs, -enumerateLineFragmentsForGlyphRange:usingBlock: and -enumerateEnclosingRectsForGlyphRange:withinSelectedGlyphRange:inTextContainer:usingBlock:, are replacing the old NSRectArray-based APIs.
NSLayoutManagerDelegate is enhanced with more text layout customization abilities previously only available through NSTypesetter subclassing. Sophisticated dynamic capabilities such as glyph substitution, dynamic line fragment override, line break point override, are provided to NSLayoutManager delegates.
We're deprecating the screen font support formally. Applications could either provide a UI feedback to users asking permission upgrading to the floating-point font metrics or keep using the deprecated APIs.
NSTextTab
NSTextTabStyle and corresponding methods are deprecated. +[NSTextTab columnTerminatorsForLocale:] is added for conveniently instantiating decimal tab stops.NSStringDrawing
NSStringDrawing API from iOS TextKit is introduced for OS X. NSStringDrawingContext for handling additional options along with methods taking it as an argument are added. Corresponding existing methods without NSStringDrawingContext argument are deprecated.NSTextAttachment
NSTextAttachment API based on NSData and UTI from iOS TextKit is introduced for OS X. The new API is not relying on an external NSTextAttachmentCell object and offers more flexibilities in controlling various rendering aspects of the attachment contents without requiring to subclass NSTextAttachment or NSTextAttachmentCell. For developers requiring the dynamic layout aspect of NSTextAttachmentCell, a new NSTextAttachment protocol, NSTextAttachmentContainer, can be overridden.The designated initializer is changed from -initWithFileWrapper: to -initWithData:ofType:. The default implementation of -initWithFileWrapper: invokes the new designated initializer with nil arguments, then, sets -fileWrapper property. Applications targeted for Mac OS X releases prior to 10.11 should override both old and new designated initializers.
NSTextContainer
Matching iOS TextKit, the -containerSize property and -initWithContainerSize: are replaced with -size and -initWithSize:. We're adding a TextKit semantics for the -size property that 0.0 indicates the infinite value.Along with the method renaming, we're moving the designated initializer to -initWithSize:. The default implementation of -initWithContainerSize: just invokes -initWithSize:.
The TextKit enhancements for NSTextContainer providing powerful container shape customization capabilities are introduced to Mac OS X 10.11, too. -exclusionPaths, -lineBreakMode, and -maximumNumberOfLines allow richer layout customization previously available only by overriding -lineFragmentRectForProposedRect:… method.
-lineFragmentRectForProposedRect:sweepDirection:movementDirection:remainingRect: is replaced by -lineFragmentRectForProposedRect:atIndex:writingDirection:remainingRect:. The layout engine invokes the former method if it's overridden. The old API along with NSLineSweepDirection and NSLineMovementDirection are informally deprecated.
-containsPoint: is now deprecated.
Notes specific to OS X 10.10.3
OS X SDK corresponding to Yosemite 10.10.3 brings support for the Force Touch trackpad on the MacBook and the early 2015 13” MacBook Pro. These notes describe those updates.Note that many (but not all of the APIs) described here are available on machines shipping with the Force Touch trackpad and machines updated to 10.10.3 or newer. Look for “NS_AVAILABLE_MAC(10_10_3)” or similar availability indication.
Pressure Events
The new Force Touch trackpad has the ability to report pressure. Like the rotation and magnification gestures that came before it, the pressure gesture is reported as a new event type, NSEventTypePressure, and a new responder method -pressureChangeWithEvent:. The new NSEventTypePressure event is only available in 64 bit.NSEventTypePressure is a fluid gesture. And like all fluid gestures, it has a phase that describes the sequence of the pressure gesture stream:
@property (readonly) NSEventPhase phase;A pressure gesture can go through multiple stages:
@property (readonly) NSInteger stage NS_AVAILABLE_MAC(10_10_3);Stage 0: The lowest stage. Generally this means the user is applying less pressure than what is required to get a mouse down. Effectively you only see one stage 0 pressure event per stream when the gesture ends.
Stage 1: Equivalent to a mouse down. NSEventTypePressure events are not posted until the user applies enough pressure that the trackpad will also issue a mouse down.
Stage 2: Equivalent to a force click. The user has applied significant additional pressure greater than what is needed for a mouse down. Stage 2 should generally be used as the trigger for an additional action. For example, lookup is performed when the pressure stream transitions to stage 2.
Note: Generally, the trackpad will actuate as the gesture transitions across stages.
Note: It is possible for stage to increase or decrease multiple integer values per change. For example, a quick removal of the user’s fingers while at stage 2 may cause stage to transition to 0 without a pressure event with stage 1.
@property (readonly) float pressure;The pressure on the trackpad for the current stage. The range for this value is [0,1]. Each stage has a pressure curve appropriate for that stage. That is, pressure may change from [0,1] for stage 1. And likewise, [0,1] for stage 2.
Note: Pressure of only one stage should be used. Carefully consider the use case. If variable input is useful for all cases, then use the pressure during stage 1 as this is the most comfortable range for the user. If variable input is useful only in rare situations where the user input must not be ambiguous with a regular mouse click, then use stage 2. Though, generally, for such cases, use stage 2 to denote this input and ignore stage 2 pressure. Do not attempt to combine stage 1 and stage 2 pressure to get a larger range. Doing so will cause undue stress on the user’s fingers.
Note: pressure is not appropriate for weight measurements.
@property (readonly) CGFloat stageTransition NS_AVAILABLE_MAC(10_10_3);The animation value for stage transitions. Positive stageTransition describes approaching the next stage of the pressure gesture. Negative stageTransition describes approaching release of the current stage. For example, as the user approaches stage 2, stageTransition will increase towards 1. The moment the gesture transitions to stage 2, stageTransition immediately return to 0 and will decrease towards -1 as the user releases pressure on the trackpad until the gesture transitions to stage 1 again. Generally, only the positive stage transition values are animated.
Note: stageTransition doesn’t match the pressure curve. There are bands where the pressure may change but the stageTransition remains at 0. stageTransition remains at 0 until the gesture is very near a stage transition.
@property (readonly) NSEventMask associatedEventsMask NS_AVAILABLE_MAC(10_10_3);This property makes it possible to determine on the mouse down if pressure should be expected from the input device. Since pressure and mouse events are independent streams, you sometimes need to make a decision (for example, a starting pressure) immediately on mouse down.
Example:
if (event.associatedEventMask & NSEventMaskPressure) {
self.pressure = 0; // Pressure events are coming!
} else if (event.subtype == NSTabletPointEventSubtype) {
self.pressure = event.pressure; // tablets embed pressure in the mouse event.
} else {
self.pressure = 1; // This device does not have pressure. Default to full pressure
}
Getting Pressure Events
There are 3 ways to get pressure events.1. Override the NSResponder method:
-(void)pressureChangeWithEvent:(NSEvent *)event;2. In a tracking loop, add NSEventMaskPressure to the eventMask.
NSEventMask eventMask = NSLeftMouseDraggedMask | NSLeftMouseUpMask | NSEventMaskPressure;or:
[self.window trackEventsMatchingMask:eventMask timeout:NSEventDurationForever mode:NSEventTrackingRunLoopMode handler:^(NSEvent *event, BOOL *stop) {
if (event.type == NSEventTypePressure) {
// yay, pressure!
} else if (event.type == NSLeftMouseUpMask) {
*stop == YES;
}
}];
NSEvent *event = [self.window nextEventMatchingMask:NSLeftMouseDraggedMask | NSLeftMouseUpMask | NSEventMaskPressure];3. In a NSGestureRecognizer subclass by overriding the method:
-(void)pressureChangeWithEvent:(NSEvent *)event;
Spring Loaded Drag & Drop
The Finder spring loading feature has been expanded and adopted in more places. All application windows will spring forward automatically. Tabs will activate themselves in response to spring loading while segmented controls and buttons can be configured to respond to spring loading.Spring loading is triggered by hovering over a spring loading capable target. The length of the hover delay is controlled via the Accessibility Mouse & Trackpad system preference pane.
Using a pressure sensitive trackpad, the user can also trigger spring loading by pressing harder on the trackpad during a drag. This is akin to a nested click inside of a drag. During a drag, the user can press harder. The trackpad will actuate, arming the spring loading under the cursor. When the user relaxes just enough to release the nested click, the trackpad will actuate and trigger the spring loading without dropping the drag. Once the user triggers spring loading via a pressure sensitive trackpad, the hover feature is disabled until the user starts a new drag & drop operation.
Spring Loaded Controls
Segmented controls and buttons can be configured to send their action in response to the user dragging an item. Set springLoaded to YES and the user will be able to interact with the control via force clicking or hovering during a drag. This property causes action on force-click or extended hover while dragging. Defaults to NO.@property (getter=isSpringLoaded) BOOL springLoaded NS_AVAILABLE_MAC(10_10_3);
Spring Loaded Tab View
Tab views and tab view controllers now change selection in response to forcek click or extended hover while the user is dragging an item.Accelerator Buttons
There are new button types for pressure sensitive trackpads, referred to as "accelerator" buttons. These act like continuous-mode push buttons in that applications generally respond to them while they are held down, and then stop when they are released. Their main feature is the ability to interpret variable pressure, allowing the user to directly control the speed of the related action by changing the pressure they apply. This is intended to be used for things like controlling the speed of fast forward and rewind for media playback, the advancement speed for week view in calendar, or the zoom speed in maps.There are two types of accelerator buttons:
• A "regular" type where fine-grained precision is desirable, and the range of values is a floating point number. Here the button's value is 0 when not pressed, and ranges from [1...2) when pressed.
• A "multi-level" type with a configurable number of explicit levels (up to 5). These buttons also present a value of 0 when not pressed, but have an integer value from [1...N] to indicate the discrete acceleration levels.
typedef NS_ENUM(NSUInteger, NSButtonType) {
NSAcceleratorButton NS_ENUM_AVAILABLE_MAC(10_10_3),
NSMultiLevelAcceleratorButton NS_ENUM_AVAILABLE_MAC(10_10_3),
}
// Configures the maximum allowed state for NSMultiLevelAcceleratorButton buttons, allowed values range from [1,5].The new button types are variants of the standard NSMomentaryLightButton, and work with a variety of bezel styles. A simple call to setButtonType is all that's necessary to use them -- this will properly configure relevant aspects of the button. Accelerator buttons behave differently than standard NSButtons in a variety of ways. Beyond the additional API above, here are the major behavioral distinctions:
@property NSInteger maxState NS_AVAILABLE_MAC(10_10_3);
* Accelerator buttons do not have a value equal to their state. Instead, their value conveys the variable acceleration amount.
* Actions work slightly differently for accelerator buttons -- they will change value and send action messages repeatedly while interacting with the user. On mouse up, they reset their value to 0 and send a final action message.
* The value of a multi-level accelerator button can be explicitly set in order to suppress lower levels of the accelerated range. This is used, for example, when a video is already fast forwarding at 4x: we provide no acceleration messages or feedback until the 8x level is reached.
* Accelerator buttons allow a greater range of values: [0,1.99999...] or [0,5] compared to the standard on/off/mixed for other buttons.
* Accelerator buttons do not support mixed state, and in fact state is not generally useful for them (as with regular pushbuttons).
All accelerator buttons will carry a state of 0 when not pressed, or 1 when pressed/clicked normally. The value will rise above 1 when pressed harder. For fine grained control over speed, NSButtonTypeAccelerator exposes a doubleValue ranging from 1 when initially pressed to 1.999999 (or so) when fully pressed. This design allows the doubleValue and integerValue to "match", and avoids having to expose a separate property for the fractional part of the value.
An NSMultiLevelAcceleratorButton provides a configurable number of distinct pressure levels, with tactile feedback as the user reaches each one. Clients configure the number of discrete levels by changing the new maxAcceleratorLevel property. It is always 1 for other button types, but defaults to 2 for multi-level accelerator buttons. Values outside the range [1,5] will be pinned and cause a warning to be logged. Values above 1 will add additional levels to the button, with a light actuation, on capable hardware, when each one is reached. The control takes on these integer values during interaction with the user, and sends an action message whenever they change. As an aside, note that the pressures needed to reach a given level remain the same, regardless of the maxAcceleratorLevel. In other words, the pressure thresholds for the allowed levels are not evenly spread across the available pressure range. Instead, they are consistent between buttons configured with different maxAcceleratorLevels, so if it takes 200g to reach level 2 on a 5-level button, it will be the same for a 2-level button.
Applications respond to accelerator buttons using the traditional action handlers. They read the control's state/integerValue or doubleValue, and configure the speed of the related operation to match. The application is responsible for translating the floating point [0,1.99999] or integer [0,5] values into an appropriate speed for their specific use case.
When an accelerator button is configured with continuous=YES, it will not send actions when the value changes (as pressure changes or on mouse up). Instead, it sends actions out periodically at a variable rate dependent on the pressure. The rate used is based on the interval configured with -setPeriodicDelay:interval:, but is adjusted dynamically based on the pressure applied to allow both slower and faster action rates. This is roughly consistent with how the 'continuous' property works for other button types, and is useful for clients who simply want an action message when it's time for them to flip to the next page, etc. Calling -setButtonType:NSAcceleratorButton will also reconfigure the periodic delay & interval.
Accelerator Segmented Controls
Segmented controls can be configured for pressure sensitivity, which can be used to accelerate user interactions. For example, a -/+ zoom control may want to accelerate zooming the harder the user presses. A continuous control with a periodic interval may accelerate page turning, as the periodic interval is automatically adjusted based on pressure.typedef NS_ENUM(NSUInteger, NSSegmentSwitchTracking) {
NSSegmentSwitchTrackingMomentaryAccelerator NS_ENUM_AVAILABLE_MAC(10_10_3) = 3, // accelerator behavior, only selected while tracking
}
/* This message is valid only for trackingMode=NSSegmentSwitchTrackingMomentaryAccelerator and provides the double value for the selected segment.The control may have its tracking mode set to NSSegmentSwitchTrackingMomentaryAccelerator. doubleValueForSelectedSegment represents the value which the individual segment would return without any segment offset, as if it were a standalone accelerator button.
*/
- (double)doubleValueForSelectedSegment NS_AVAILABLE_MAC(10_10_3);
Accelerator Segmented Control Behaviors
Behavior of momentary accelerator segmented controls vary based on the state of their continuous flag and whether or not the hardware supports pressure sensitivity. Below is a description of the behavior in each configuration.Continuous Momentary Accelerator, non-pressure sensitive trackpad:
Actions will be sent at the cell's periodic interval, starting after the cell's periodic delay. Upon mouse up a final action will be sent with selectedSegment=-1.
Continuous Momentary Accelerator, pressure sensitive trackpad:
Actions will be sent at a periodic interval automatically adjusted based on pressure, starting after the cell's delay. Upon mouse up a final action will be sent with selectedSegment=-1. In this configuration the adjusted periodic interval is useful for accelerated page turning of content.
Non-Continuous Momentary Accelerator, non-pressure sensitive trackpad:
An initial action will be sent with doubleValueForSelectedSegment=1.0. Upon mouse up a final action will be sent with doubleValueForSelectedSegment=0.0 and selectedSegment=-1.
Non-Continuous Momentary Accelerator, pressure sensitive trackpad:
Each time the pressure changes, an action will be sent with doubleValueForSelectedSegment=[1.0+pressure]. Upon mouse up a final action will be sent with doubleValueForSelectedSegment=0.0 and selectedSegment=-1. In this configuration the doubleValueForSelectedSegment is useful to accelerate actions such as zooming.
Notes specific to OS X 10.10
Some of the major topics covered in this section include:- Swift exposure of Cocoa APIs
- Storyboards
- NSViewController
- NSVisualEffectView
- Handoff support
- NSSplitViewController
- NSTabViewController
- NSGestureRecognizer
- Dark menus
- NSImage changes
- Right-to-left UI improvements
- NSTableView/NSOutlineView updates
Swift
Swift is a new programming language for Cocoa and Cocoa Touch that provides many innovative features while also seamlessly operating with Cocoa APIs and Objective-C. You can find documentation, release notes, and other resources for Swift development on the Apple developer website.We expect Swift to change and evolve rapidly before Swift 2.0, and we also expect to update the way Objective-C APIs come through in Swift. In the meantime, some items of note regarding the interaction of Swift with existing Cocoa APIs in 10.10 Yosemite:
• Instance methods in Cocoa APIs come across to Swift pretty much as-is. A method such as:
- (BOOL)setResourceValue:(id)value forKey:(NSString *)key error:(NSError **)error;in Swift looks like:
func setResourceValue(value:AnyObject?, forKey key:String, error:NSErrorPointer) -> BoolThe first part of the Objective-C name appears as the first part of the Swift name, outside the parens, and the labels on the second and subsequent arguments appear as labels in Swift as well.
In both cases, "value" and "key" are simply the names of the local variable corresponding to the first and second arguments, and are not part of the method name.
Important to note that the Swift name of such a method includes the labels, so the name of such a method would be read as in ObjC, as "setResourceValue forKey error", not "setResourceValue." Removing the names of the arguments, the method signature is:
func setResourceValue(AnyObject?, forKey:String, error:NSErrorPointer) -> BoolSo basically Objective-C instance method names are exposed automatically and without any changes in Swift.
init methods are also exposed automatically, but there is a mapping: Both init methods and convenience constructors are exposed in Swift as constructors. In such cases if present, "with" is dropped, and all the arguments, including the first one, have an explicit label. So a method such as:
- (instancetype)initWithTimeIntervalSinceNow:(NSTimeInterval)secs;is exposed as:
convenience init(timeIntervalSinceNow secs: NSTimeInterval)and the same thing happens with a convenience constructor such as:
+ (NSColor *)colorWithPatternImage:(NSImage *)image;which appears as:
init(patternImage image: NSImage) -> NSColor• As you can see in the above examples, some Objective-C Cocoa types are mapped to their Swift counterparts, such as NSString/String above. Also note that by-ref NSError return arguments (NSError **) appear as NSErrorPointer in Swift. id appears in Swift as AnyObject, and NSArray appears as AnyObject[]. NSInteger is mapped to Int, and in most cases so is NSUInteger.
• Cocoa structs such as NSRange and NSSize are available to Swift with custom constructors, such as:
let size = NSSize(width: 20, height: 40)• To localize strings in your applications you can use the NSLocalizedString(key:tableName:bundle:value:comment:) function. This function provides default values for the tableName, bundle, and value arguments, so you can use it in a variety of forms, shortest being:
result = NSLocalizedString("Update", comment:"Title of button the user can click to update account info")• Enums in Cocoa which follow the "common prefix" naming guideline (which applies to the more recent enums) appear in Swift with the common prefix removed. For instance:
typedef NS_ENUM(NSInteger, NSByteCountFormatterCountStyle) {is exposed in Swift as:
NSByteCountFormatterCountStyleFile,
NSByteCountFormatterCountStyleMemory,
NSByteCountFormatterCountStyleDecimal,
NSByteCountFormatterCountStyleBinary
};
enum NSByteCountFormatterCountStyle : Int {and you can refer to the individual values as NSByteCountFormatterCountStyle.File or just .File in appropriate contexts.
case File
case Memory
case Decimal
case Binary
}
• Objective-C selectors appear using the Selector type in Swift. You can construct a selector with a string literal, such as
let action: Selector = "updateAccountInfo:"
Storyboards
AppKit has new API to support the Storyboarding feature being introduced in OS X 10.10.The NSStoryboard class (declared in NSStoryboard.h) provides the means to access your app’s storyboards and instantiate controllers by name. (You can also specify an NSMainStoryboardFile in your app’s Info.plist, which causes the named storyboard to be automatically loaded, and its initial controller instantiated and presented, at app launch time.) Both NSViewController and NSWindowController have a new, readonly “storyboard” property, that returns the NSStoryboard (if any) that the controller belongs to.
The NSStoryboardSegue class (declared in NSStoryboardSegue.h) encapsulates a named connection from a sourceController to a destinationController, while the NSSeguePerforming protocol, to which both NSViewController and NSWindowController now conform, provides the means to hook into the mechanics of these transitions. When your app performs a segue, the segue’s destinationController is presented. The presented destinationController remains shown until it is dismissed (usually in response to a user action).
To dismiss a ViewController or WindowController, you send it (or have it send itself) the new -dismissController: message. -dismissController: is declared as an IBAction, so you can wire it up as the destination of a target-action connection if you wish.
NSWindowController
NSWindowController has a new “contentViewController” property, that mirrors the “contentViewController” property of the associated window. An NSWindowController that’s part of a storyboard is required to have a contentViewController — a requirement that Xcode’s storyboard editor assists you in fulfilling when you drag an NSWindowController into the storyboard canvas.As is always the case when new properties or methods are added to an OS X framework class, those additions may interfere with the operation of identically named properties or methods in 3rd party code. When this happens, developers are advised to rename their properties or methods to avoid problems. On OS X 10.10, AppKit strives to maintain compatibility for apps linked before 10.10, by sensing the case where an NSWindowController subclass declares a “contentViewController” ivar and a “-contentViewController” getter method, but no “-setContentViewController:” setter method. In such cases, NSWindowController’s -setContentViewController: method (which will now be invoked in place of a synthesized setter, at nib-unarchiving time for example) sets the subclass instance’s “contentViewController” ivar to help affected code continue to operate. This workaround is no longer applied once the app is linked on OS X 10.10 or later, so in the long term, an affected app should rename its “contentViewController” ivar and getter, to ensure they continue to operate as intended. AppKit also avoids invoking its own contentViewController getter and setter methods, for apps linked before OS X 10.0 that would have no knowledge of this new property.
Prior to OS X 10.10, an NSWindowController only received -windowWillLoad, -loadWindow, and -windowDidLoad messages when the NSWindowController itself performed the nib loading — that is, when the NSWindowController was instantiated in code and given the name of a nib file to load on demand when its -window was first requested. This meant -windowWillLoad, -loadWindow, and -windowDidLoad would not be sent to an NSWindowController that was loaded as one of a nib file’s constituent objects.
AppKit now sends -windowWillLoad, -loadWindow, and -windowDidLoad to an NSWindowController that’s unarchived from a nib, if the NSWindowController is wired up to an associated window. An override of -windowDidLoad can count on all connections described in the nib having been established. The documented behavior of -isWindowLoaded has changed slightly to match: For an NSWindowController unarchived from a nib, -isWindowLoaded reports NO at first, then YES at the time -windowDidLoad is invoked and thereafter. To prevent binary incompatibility problems due to these changes in long-standing NSWindowController behavior, the changes described in this paragraph only take effect for apps linked on or after OS X 10.10, when running on OS X 10.10 or later.
NSViewController
NSViewController now has a suite of methods for presentation hooks: -viewWillAppear, -viewDidAppear, -viewWillDisappear, and -viewDidDisappear. See the header comments in NSViewController.h for more details.NSViewController now has a -viewDidLoad method that can be overridden for instances that want to know when the view has been loaded, and can do additional setup work as needed. To maintain compatibility for apps that may invoke their own, identically named “-viewDidLoad” methods, AppKit invokes -viewDidLoad only for apps linked on or after OS X 10.10.
NSWindow now has a contentViewController property. This property makes it easy to assign the contentView of a window using a view controller. There is also a convenience method for creating a titled window: [NSWindow windowWithContentViewController:]. See the NSWindow.h header comments for more details.
NSViewController now has the following new features, all commented in great detail in NSViewController.h:
• Parent/child container view controller methods
• View controller presentation options
• View controller transition options
NSViewController now conforms to the NSUserInterfaceItemIdentification protocol. This allows it to participate in the Transparent Application Lifecycle management. Any NSViewController can now use the suite of methods declared on NSResponder for state restoration: encodeRestorableStateWithCoder:, restoreStateWithCoder:, invalidateRestorableState, and restorableStateKeyPaths.
NSViewController now has a suite of methods to control layout for its associated view: updateViewConstraints, viewWillLayout and viewDidLayout. See the NSViewController.h header for more information.
The following changes take effect for applications linked on 10.10 and higher:
• NSViewController automatically adds itself into the responder chain. When a view is assigned, the view's current nextResponder is saved off. The view's nextResponder is then set to be the viewController, and viewController's nextResponder is set to be the previously saved nextResponder. When the view has a call to setNextResponder: made, the method will be forwarded to the view’s view controller. If you currently are already doing your own responder chain management, take particular care as the view.nextResponder may already be the NSViewController. This can cause an issue for apps that manually set the viewController.nextResponder = view.nextResponder, without checking to see if the view.nextResponder != viewController; otherwise, an infinite loop may be created, causing an application to hang. To debug these hangs, run the application with “-NSResponderDebugResponderLoops YES”. This option will check the responder chain for loops anytime setNextResponder is called (note that this option will slow down program execution).
Put another way, the responder chain with view controllers looks like this:
ChildView -> ParentView -> ParentView’s ViewController -> ParentView’s ParentView -> Window -> Window’s WindowController.
• loadView: would previously not have well defined behavior if there was a "nil" nibName. On 10.10 and later, if nibName is nil NSViewController will automatically try to load a nib with the same name as the classname. This allows a convenience of doing [[MyViewController alloc] init] (which has a nil nibName) and having it automatically load a nib with the name "MyViewController".
Auto Layout
For apps linked against 10.10 and later, recalculating a window’s key view loop will layout any dirty subtrees in the window. Previously, the key loop would be calculated using the dirty layout frames, potentially leading to an incorrect key view loop. This affected layouts created with constraints as well as with NSStackView.Constraints that span over parent views (e.g. a button within a container view constrained to a sibling of the container view) that are created in IB are no longer removed at runtime. Apps running on systems earlier than 10.10 will still have this issue — a workaround for apps deployed on earlier systems is to create the affected constraints programmatically.
Constraints from a Leading or Trailing edge of one layout item to the Center X of another no longer behave unexpectedly (such as causing unsatisfiable constraints) in RTL environments. Apps running on systems earlier than 10.10 will still have this issue — a workaround for apps deployed on earlier systems is to use explicit Left or Right attributes in RTL environments.
NSPopover
The responder chain no longer ends at the NSPopover window. That window now has a nextResponder of its parentWindow, i.e. the window of the positioningView.The contents of popovers shown from borderless windows and status bar items are now focusable. In these cases, the popover window will become the key window. Transient popovers shown from these contexts will also properly close.
If the popover’s contentView returns NO from -acceptsFirstResponder, the popover will no longer make it the first responder. This can be utilized to show help text popovers without making the source lose first responder or key status.
Calling the -close method on a popover will now cause child popovers to be forced closed as well (in accordance with past documentation). Calling -performClose: will still fail if the popover has a nested popover.
-popoverWillClose: is now called on the delegate before the popover queries its animates property to determine if it should animate. This allows the pattern of always having animates = NO, and turning it on in willClose: and willShow: to animate showing and closing the popover.
In OS X 10.10, NSPopover now positions its content flush to the edges of the popover. Previously it was inset (2, 2, 2, 2).
Detachable Popover Improvements
NSPopoverDelegate’s new -popoverShouldDetach:. This is called when the user begins dragging the popover to determine if it should be allowed to detach. If the delegate does not implement this method, the default behavior will be NO.An app wanting to use a custom detached window vended from -detachableWindowFromPopover: will need to implement this method to return YES if linked against 10.10 or higher. Apps linked before this will continue to work as expected without the method implementation.
Automatically created detachable windows. If the delegate returns YES from -popoverShouldDetach:, but does not implement or returns nil from -detachableWindowForPopover:, a detached window will be automatically created to detach to. This will reuse the contentViewController’s view as its contentView, and maintain a popover-like appearance with a seamless transition during the detach. During the detach with an automatic window, -PopoverShould/Will/DidClose notifications will not be sent. Instead, when the automatic window is going to be closed, -popoverShouldClose: will give the delegate a chance to prevent the closing of the window, and PopoverWill/DidClose notifications will be sent if it does close. If -showRelativeToRect:ofView:preferredEdge: is called on the popover while an automatically created detached window is shown, the popover will not appear, but the detached window will be made key and ordered front. Once the detached window is closed, calling -showRelativeToRect:ofView:preferredEdge: will return to showing the popover.
-detachableWindowForPopover: will only be called once the popover is going to be fully detached, not when the user begins the drag. This means that the popover appearance during the drag will be based on the popover itself, rather than the final detached window. -popoverShouldDetach: should be used to indicate that the popover should be allowed to be dragged and detached.
-popoverShouldClose: is no longer called on the delegate or popover when the window is detaching to a separate window.
Popover Appearance Changes
In order to allow for appearance customization through NSAppearance, NSPopover’s appearance property is transitioning from type NSPopoverAppearance to NSAppearance. If you plan to deploy to 10.10 or above, the former is considered deprecated, and you should no longer set or read the appearance property with NSPopoverAppearanceMinimal or NSPopoverAppearanceHUD. If you are deploying to 10.10 as a minimum, the appearance property is declared as taking an NSAppearance. If no appearance is specified, the effective appearance defaults to NSAppearanceNameVibrantLight. This corresponds to the appearance created by NSPopoverAppearanceMinimal. NSPopoverAppearanceHUD has no parallel style in NSAppearance.Apps linked against 10.9 and earlier will have the NSAppearanceNameAqua appearance set on their popover content, while apps linked against 10.10 and later will not (and will inherit the VibrantLight appearance from the popover). This is to prevent unintended vibrancy effects (see the NSVisualEffectView section for a deeper description of these effects) in apps that were not prepared for them. However, when building an app for Yosemite, these possible vibrancy side effects should be kept in mind in popovers.
NSSplitView
In 10.10, SplitViews using auto layout will now correctly hide the first and last dividers when the delegate returns such from -splitView:shouldHideDividerAtIndex:. In 10.9 and earlier, these dividers would fail to hide.In 10.10, SplitViews using auto layout will respect when its userInterfaceLayoutDirection is NSUserInterfaceLayoutDirectionRightToLeft. Its subviews and dividers will be ordered from Right to Left; i.e. subview 0 is the right-most view and divider 0 is the right-most divider. SplitViews not using auto layout will continue to not order views right-to-left.
NSSplitViewController
NSSplitViewController is a new container view controller class in 10.10. It manages an NSSplitView to layout its child view controller’s views. The use of NSSplitViewItem objects expose the SplitViewController specific properties of a child view controller in the NSSplitViewController.One benefit of NSSplitViewController is the lazy loading of childViewControllers’ views. In general the performance benefits of this may not be as significant as NSTabViewController’s lazy loading, but does come into play in a couple situations: (1) when setting up the SplitViewController and adding SplitViewItems, the children’s views will not be loaded until the SplitViewController’s is loaded, and (2) when adding a collapsed SplitViewItem to a SplitViewController; the item’s view will only be loaded once the view is uncollapsed (programmatically or by the user). If it is never uncollapsed, it is never loaded by SplitViewController.
The use of auto layout with NSSplitViewController allows an app to customize whether the collapse of a split view item causes, for instance, the window to stay the same size or shrink without exposing any additional API or requiring the developer to do additional work. If the constraints used to create the layout of a child view controller’s view hierarchy are not enough to describe the behavior during collapse/uncollapse, NSSplitViewItem’s holdingPriority property can be set to specify this behavior.
The collapsed property of NSSplitViewItems are KVC/KVO compliant. This allows developers to do things like bind a PushOnOff button to the collapsed state of the item. As the button is toggled, so is the split view item. And vice versa: if the SplitViewItem is collapsible and the user collapses the the item (with a drag or double click), the button’s state is updated:
[toggleButton bind:NSValueBindingSince the collapsed property is also animatable with the SplitViewItem's animator proxy, the binding could be instead written against the animator property. This gives all the same benefits as the previous example, but now when the button is toggled, the split view item’s view controller will be animated in or out:
toObject:sidebarSplitViewItem
withKeyPath:@"collapsed"
options:@{NSValueTransformerNameBindingOption : NSNegateBooleanTransformerName}];
[toggleButton bind:NSValueBinding
toObject:[sidebarSplitViewItem animator]
withKeyPath:@"collapsed"
options:@{NSValueTransformerNameBindingOption : NSNegateBooleanTransformerName}];
NSStackView
For layer-backed StackViews, views are no longer clipped to any private container bounds. This change only affects apps linked against 10.10. NSStackView now overrides +requiresConstraintBasedLayout to return YES, allowing StackViews to properly function in windows that do not have any other constraints.Dark Menus
Dark menu support is enabled by users via the General preferences pane. It’s a system-wide setting, not per application, and it is intended to apply to a small number of system elements — menu bar, menus, dock, and cmd-tab application switcher. It does not apply to application windows.NSStatusItem appearance and Dark Menu support
There are a number of stylistic changes added and supported by NSStatusItem, including appearance changes for Dark Menus. Use template images to ensure correct styling based on the various states the status item can be in (light menu, dark menu, inactive light, inactive dark, selected, disabled, etc). NSStatusBarButton’s appearsDisabled property can be used to give the image a disabled or “off” look without having the item be functionally disabled. The -button property has been added to give access to the NSStatusBarButton that is internally created and displayed in the status item. Existing properties that had previously been forwarded to this button are softly deprecated and should be accessed directly through the button. The button property also allows screen position measurement (such as for use with popovers) without setting a custom view.Creating a standard status item with proper styling can be done in a just few lines:
NSStatusItem *myStatusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
gearImage.template = YES;
myStatusItem.button.image = gearImage;
myStatusItem.button.accessibilityTitle = @"My Status Item";
myStatusItem.button.appearsDisabled = networkIsDisabled;
myStatusItem.menu = myStatusItemMenu;
The view property of NSStatusItem is softly deprecated as well as API meant to support its use. The custom view property alone is not enough for AppKit to provide standard styling for the different status item states and enables items to look and feel out of place in the menu bar. Please file enhancement requests if NSStatusItem & NSStatusBarButton’s API does not allow for a behavior that should be standardized and allowed.
NSTabViewController
NSTabViewController is a new container view controller class in 10.10. It provides:• Lazy loading of non-visible view controllers
• Easy design time construction with Storyboarding
• A target for bindings
• Easy/abstract hookup for displaying large content switches
• A way of providing standard tab-style UI with external controls
NSTabViewController represent an abstract target for providing content switching. tabViewItems and selectedTabViewItemIndex are KVC/KVO compliant. This allows one to bind an NSSegmentedControl (or other bindable objects) directly to them:
[segmentedControl bind:NSContentBinding toObject:tabViewController withKeyPath:@"tabViewItems" options:nil];
[segmentedControl bind:NSSelectedIndexBinding toObject:tabViewController withKeyPath:@"selectedTabViewItemIndex" options:nil];
Right to Left UI
• NSComboBox will now correctly flip when its userInterfaceLayoutDirection is NSUserInterfaceLayoutDirectionRightToLeft.• NSDatePicker will now correctly flip when its userInterfaceLayoutDirection is NSUserInterfaceLayoutDirectionRightToLeft.
• NSPopupButton will now correctly flip when its userInterfaceLayoutDirection is NSUserInterfaceLayoutDirectionRightToLeft.
• NSSearchField will now correctly flip when its userInterfaceLayoutDirection is NSUserInterfaceLayoutDirectionRightToLeft.
• NSSplitView will layout its subviews from right to left when its userInterfaceLayoutDirection is NSUserInterfaceLayoutDirectionRightToLeft and is using auto layout.
Sheet Appearance Changes
Sheets shown from windows will inherit the appearance of their sheet parent window — for instance, a sheet shown from a window with a VibrantDark appearance will also have a VibrantDark appearance. An appearance can be specifically set on a sheet and override the inherited appearance. If the sheets have a vibrant appearance, their content will be vibrantly blended, and potential unintended vibrant side effects should be kept in mind in sheets (see the NSVisualEffectView section for a deeper description of these effects).NSDatePicker changes
On applications that build against the 10.10 SDK, when running on 10.10 or later, NSDatePicker and NSDatePickerCell’s setLocale: and setTimeZone: methods will copy the given locale/time zone instead of simply retaining it.NSDatePicker now supports the Chinese lunar calendar when the calendar property is set to an NSCalendar created with the NSCalendarIdentifierChinese identifier.
NSDatePicker’s NSClockAndCalendarDatePickerStyle now supports RTL according to its locale.
NSTokenField property behavior changes
On applications that build against the 10.10 SDK, when running on 10.10 or later, NSTokenField and NSTokenFieldCell’s setTokenizingCharacterSet: methods will copy the given character set instead of simply retaining it.NSSpeechRecognizer property behavior changes
On applications that build against the 10.10 SDK, when running on 10.10 or later, NSSpeechRecognizer’s setCommands: method will copy the given array instead of simply retaining it.NSWorkspace methods for passing configuration and options when opening a URL
NSWorkspace now has a pair of methods for opening URLs with specific configuration options. See openURL:options:configuration:error: and openURLs:withApplicationAtURL:configuration:error: in AppKit/NSWorkspace.h.NSScreen behavior changes
NSScreen instances are now immutable — their properties will not change. To get updated information as displays are reconfigured, listen for the NSApplicationDidChangeScreenParametersNotification and, when it arrives, get new NSScreen instances, which will have the updated information.NSScreen isEqual will now return YES for the same display even if the NSScreen instances are not identical.
On applications that build against the 10.10 SDK, when running on 10.10 or later, [NSScreen screens] will return an empty array if no displays are attached instead of nil.
Handoff support in AppKit
NSUserActivity is a new class in Foundation which encapsulates the state of a user activity in an application on a particular device, in a way that allows the same activity to be continued on another device in a corresponding application from the same developer. Examples of user activities include editing a document, viewing a web page, or watching a video.There are new APIs on the NSApplication delegate to support continuing an activity from an NSUserActivity. There is also a userActivity property on NSResponder and NSDocument that lets AppKit manage calling -becomeCurrent and -invalidate, along with -updateUserActivityState: and -restoreUserActivityState: methods that can be overridden to save/restore your own state in the NSUserActivity.
Additionally, document based apps can put NSUbiquitousDocumentUserActivityType in each CFBundleDocumentTypes entry in the Info.plist and AppKit will automatically create NSUserActivities for documents in iCloud (that are accessible from the userActivity property). Activity types specified in this way do not need to appear in the Info.plist NSUserActivityTypes array, which is used to declare the activities supported by the application.
NSUserActivities that have the NSUserActivityDocumentURLKey set in their userInfo can be automatically continued using NSDocumentController’s openDocumentWithContentsOfURL:display:completionHandler:. NSDocument will put its fileURL in the userInfo with NSUserActivityDocumentURLKey in its implementation of updateUserActivityState:.
For more information, see Foundation/NSUserActivity.h and AppKit/NSUserActivity.h.
New NSTokenField styles
NSTokenField has two new tokenStyles: NSTokenStyleSquared and NSTokenStylePlainSquared. NSTokenStyleSquared is the recommended style for most token fields. NSTokenStylePlainSquared is recommended for instances where tokens appear as labels.The existing NSTokenStyle enum values: NSDefaultTokenStyle, NSPlainTextTokenStyle, NSRoundedTokenStyle are deprecated. You should use NSTokenStyleDefault, NSTokenStyleNone, and NSTokenStyleRounded instead.
NSAppearance
An accessor has been added to get the name of an appearance instance.NSAppearanceNameLightContent has been deprecated. Uses of this appearance will be redirected to the default Aqua appearance.
Vibrant appearances, NSAppearanceNameVibrantDark and NSAppearanceNameVibrantLight, have been added, for use with NSVisualEffectView.
NSTextView
The default insertion point color is now [NSColor controlTextColor]. In the Aqua appearance this color is black and therefore should result in no change in behavior, but in other appearances (e.g. Vibrant Dark) it may be different.Gesture Recognizers
Gesture recognizers are now available in AppKit. The API is nearly identical to the UIKit version. See NSGestureRecognizer.h. There is a NSGestureRecognizer (NSSubclassUse) category that is explicitly meant for subclassers to override and call. Non-subclassers should never directly access these category methods and properties.The following are considered physical gestures:
• A mouse button press, (possibly) drag, and release. Note: each button is an independent gesture stream.
• A gesture which always goes through NSEventPhaseBegan and NSEventPhaseEnded / NSEventPhaseCancelled (aka Rotate and Magnify)
Note: while ScrollWheel events have a phase value, the phase is not required, and thus ScrollWheel events are not capture-able by NSGestureRecognizers.
Gesture recognition begins at the moment any of the above physical gestures begin. At this point, the set of possible gesture recognizers is gathered much like UIKit. That is, the set of all gesture recognizers attached to the hit tested view and its ancestor views. From there, all mouse, keyboard, and gesture events are routed to the set of gesture recognizers even if such events are the start of a different physical gesture. This continues until the complete set of gesture recognizers end (or fail) or all physical gestures that have begun since the first one, have completed.
Gesture Recognizers do not see events that are pulled by event tracking loops. For this reason, it is advised that your gesture recognizer subclass delay events of the type you are processing (see the various delays[type]Event properties on NSGestureRecognizer).
The event passes from NSApplication’s -sendEvent: to any event monitors, then to NSWindow’s -sendEvent: method then to Gesture Recognizers and finally out to the responder chain. At any point in this chain the event may be consumed. If a gesture recognizer is delaying events, the events are consumed once a gesture is recognized. If all gesture recognizers fail then the delayed events are routed to the window’s responder chain. Warning: consuming events that mark the end of a gesture before Gesture Recognizers handle them can cause the gesture recognizers to get confused.
The set of AppKit provided gesture recognizers (and their UIKit analogs) are:
• NSPanGestureRecognizer [UIPanGestureRecognizer] - tracks a mouse drag
• NSClickGestureRecognizer [UITapGestureRecognizer] - tracks a specified number of clicks (button press and release)
• NSPressGestureRecognizer [UILongPressGestureRecognizer] - tracks a press and hold
• NSMagnificationGestureRecognizer [UIMagnificationGestureRecognizer] - tracks a magnification gesture event sequence
• NSRotationGestureRecognizer [UIRotationGestureRecognizer] - tracks a rotation gesture event sequence
Block Based Event Tracking
There is a new block based event tracking method on NSWindow. It will continuously track events using the supplied tracking handler block until the tracking handler block explicitly terminates tracking.- (void)trackEventsMatchingMask:(NSEventMask)maskIf a matching event does not exist in the event queue, then the main thread blocks in the specified runloop mode until an event of the requested type is received or the timeout expires. If the timeout expires, the tracking handler is called with a nil event. A negative timeout is interpreted as 0. Use NSEventDurationForever to never timeout. Tracking continues until *stop is set to YES in the tracking handler. This method returns once tracking is terminated.
timeout:(NSTimeInterval)timeout
mode:(NSString *)mode
handler:(void(^)(NSEvent *event, BOOL *stop))trackingHandler;
Calls to -nextEventMatchingMask:… are allowed inside the trackingHandler block. This is often useful to quickly drain the event queue for coalescing purposes.
NSScrollView
NSScrollView now has a contentInsets property similar to that of UIScrollView in iOS. Typically this is used with NSFullSizeContentViewWindowMask when the scroll view intersects the title / tool bar.“Automatically” sets the scroll view's contentInsets property to account for any overlapping title / tool bar. In order to overlap with the title / tool bar the window style mask must include NSFullSizeContentViewWindowMask and the titlebar must not appear transparent. Normally this is NSEdgeInsetsZero. Defaults to YES.
@property BOOL automaticallyAdjustsContentInsets;NSScrollView manages a number of subviews (contentView, scrollers, find bar, rulers, etc..). This property controls the distance that the subviews are inset from the enclosing scroll view during tiling. When contentInset equal to NSEdgeInsetsZero, traditional tiling is performed. That is, the rulers, headers, etc... are tiled with the contentView frame filling the remaining space. When contentInset is not equal to NSEdgeInsetsZero, the rulers, header, etc... are inset as specified. The contentView is is placed underneath these sibling views and is only inset by scroll view border and non-overlay scrollers. Note: if automaticallyAdjustsContentInsets == YES then any value set here will get overridden during tiling.
@property NSEdgeInsets contentInsets;The distance the scrollers are inset from the edge of the scroll view:
@property NSEdgeInsets scrollerInsets;
NSClipView
Since NSClipView is the view that actually contains the scrollable content (documentView), NSClipView also gains the concept of contentInsets.When YES, and used as the contentView of an NSScrollView, the scroll view will automatically set the appropriate clipView contentInsets considering the scrollView’s contentInsets, boarder and layout of other subviews such as rulers and headers. Defaults to YES.
@property BOOL automaticallyAdjustsContentInsets;The distance that the content view is inset from the enclosing scroll view. Animate with [self animator]. If automaticallyAdjustsContentInsets == YES then any value set here will get overridden during tiling.
@property NSEdgeInsets contentInsets;Generally, the default automatic behavior will do the right thing when using a full content view style window. To achieve a hidden pull down or pull to refresh style UI, set automaticallyAdjustsContentInsets to NO and manually set the contentInsets. Add the hidden content as subViews of the clipView and place arrange them above your documentView’s frame.
NSPrintSaveJob and File Coordination
In Mac OS X 10.9, when printing operations resulted in PDF or PostScript files, NSPrintOperation would use NSFileCoordinator to properly cooperate with other applications that might be presenting or accessing the same file. However, this new behavior caused deadlocks in some applications doing their file coordination, since coordinated writing on the same file from two separate subsystems is not reentrantThe scenario where these would occur always involved an app correctly doing a coordinated write, then inside that write running an NSPrintOperation with NSPrintSaveJob set with an explicit value for NSPrintJobSavingURL (the URL provided by the coordinated write). In order to break this deadlock NSPrintOperation on Mac OS X 10.9.2 or later will not perform a coordinated write if and only if both NSPrintSaveJob and NSPrintJobSavingURL are set.
If NSPrintSaveJob is set but NSPrintJobSavingURL is not, then NSPrintOperation will still perform a coordinated write. The same goes for when a print operation starts off with NSPrintSpoolJob, the print panel is displayed, and the user chooses to Save to PDF instead of sending the job to the printer.
Non-main thread invocations of -[NSDocumentController typeForContentsOfURL:error:]
Prior to Mac OS X 10.10, it was possible for -[NSDocumentController typeForContentsOfURL:error:] to be invoked on a non-main thread. This is no longer the case. However, if your application targets Mac OS X 10.9 or earlier and overrides this method, you should make sure the method is safe to be invoked on a non-main thread.Gradual deprecation of NSCell
Mac OS X 10.10 takes another step towards the eventual deprecation of cells. Direct access to the cell of a control is discouraged, and methods which allow it will be formally deprecated in a subsequent release. A variety of cell-level APIs have been promoted to various Control subclasses in order to provide cell-free access to important functionality. NSLevelIndicator, NSTextField, NSSearchField, NSSlider, and NSPathControl all have new properties for this purpose. Cell-based NSTableViews are now deprecated, and view-based NSTableViews should be used instead. Matrix-based NSBrowsers are also deprecated in favor of the item-based interface.NSPathControl
In order to discourage direct access to cells, Mac OS X 10.10 adds a new item-based API to NSPathControl. This eliminates the need to use NSPathComponentCell to manage the components of the path. Additionally, a variety of properties have been added to NSPathControl which provide access to important functionality previously only available through NSPathControlCell.NSForm
Use of NSForm is now deprecated. Use NSTextField directly, and consider using NSStackView if layout assistance is desired.NSMatrix
Use of NSMatrix is informally deprecated. We expect to add the formal deprecation macros in a subsequent release, but its use is discouraged in the mean time. The primary use of NSMatrix is for radio button groups, so recall that for applications linked on 10.8 or later, radio buttons that share the same parent view and action will operate as a group.Weak targets for NSControl & NSCell
The target properties of control and cell subclasses have been modified to provide proper automatic-zeroing weak reference behavior for applications linked on Mac OS X 10.10 and higher. The implementation attempts to accommodate targets which cannot be safely referenced weakly, but the result is that messages are now sent to targets at the time they are set. If a target is being set to an invalid object reference or an object in the process of deallocation, this can cause crashes in cases that were previously innocuous. One consequence of this change is that NSControl now has a concrete implementation of the target & action properties. This is something to be aware of if you plan to rely on it in 10.10, but also deploy your application to previous releases.New Auto Layout Constraint Management API
Under Mac OS X 10.10, it is now possible to directly activate and deactivate NSLayoutConstraint objects, without having to worry about adding them to an appropriate ancestor view. This is accomplished by manipulating NSLayoutConstraint's new boolean property 'active'. Class methods are available for operating on multiple constraints simultaneously, which can be much faster. The legacy API on NSView for adding & removing constraints is now deprecated.Image positioning in Buttons
NSButtonCell now uses -imageRectForBounds: to position images displayed in buttons, for applications linked against Mac OS X 10.10 or later.NSTableView/NSOutlineView General Updates
NSTableRowView now has properties to determine if the previous and/or next row is selected. This is useful for drawing selection differently based on this state.@property(getter=isPreviousRowSelected) BOOL previousRowSelected;NSTableView now supports the ability to statically design the contents of a table at design time in Xcode. At runtime, it is unarchived and identical to what you designed.
@property(getter=isNextRowSelected) BOOL nextRowSelected;
One can also dynamically create a static table view with a new property: usesStaticContents.
Normally, a table view will only keep around a subset of the total number of rows potentially available (in general, this is limited to the visible region, plus some overdrawn allowance for responsive scrolling). A static table view keeps all views added to the table around when usesStaticContents=YES. Views can dynamically be removed by calling removeRowsAtIndexes:withAnimation:. The datasource does not need to implement numberOfRowsInTableView: when usesStaticContents=YES. Static views are encoded and decoded with the table view. Views can also dynamically be inserted into the table view by using insertRowIndexes:withAnimation:, however, this requires an implementation of tableView:viewForTableColumn:row: to provide the newly inserted view, which is then kept around statically.
NSOutlineView also supports the ability be statically designed, and works similarly to NSTableView. However, to dynamically change the outline view you must use the methods: insertItemsAtIndexes:inParent:withAnimation:, removeItemsAtIndexes:inParent:withAnimation:, and moveItemAtIndex:inParent:toIndex:inParent:. Provide newly inserted items by implementing the datasource method outlineView:child:ofItem:, and indicate they are expandable by implementing outlineView:isItemExpandable:. It is not necessary to implement outlineView:numberOfChildrenOfItem when using a static outline view. Note that all items provided must be encodable (i.e.: implement the NSCoding protocol), and unique (in order to uniquely identify the row for a given item).
Since 10.5, NSTableViews/NSOutlineViews are configured as sidebars (source lists) by setting the selectionHighlightStyle to NSTableViewSelectionHighlightStyleSourceList. This has always had side effects. Specifically, it will control some layout metrics for NSOutlineView and NSTableView. Upon setting the style, it will also set the backgroundColor of the tableView to a special internal color. Prior to 10.10 this color was a light blue gradient. After 10.10, an NSVisualEffectView is internally used to create a blurred background. After setting the style, one could always change the color away from the internal default; doing this will cause the blur background to not be shown (which may be desired). On 10.10, setting the selectionHighlightStyle to NSTableViewSelectionHighlightStyleSourceList will now also set the appearance to NSAppearanceNameVibrantLight. On 10.10, for source lists using the rowSizeStyle of NSTableViewRowSizeStyleDefault, the intercellSpacing.height is now automatically controlled. In general, it may vary based on the user's set row size style in System Preferences, and it is important to test each size when creating a “source list” application.
In 10.10, NSTableHeader view now has a new height. Applications linked on 10.10 and higher will automatically get the new default height. For applications not linked on 10.10, the new height will still be applied, but only in cases where there are no overrides in drawing for NSTableHeaderView or NSTableCellView.
Animations
Most animations on the system can be slowed down by setting the default NSAnimationSlowMotionOnShift to YES and holding down the shift key. NSTableView previously had use a default named NSTableViewSlowMotion; it is no longer used.NSWindow
NSWindow has some API to control the new look of windows in OS X.The first new API is titlebarAppearsTransparent; when set to YES, the titlebar does not draw its background, allowing all content underneath it to “show through.” It is only applicable to use this option when turning on the new NSFullSizeContentViewWindowMask option for the window styleMask. NSFullSizeContentViewWindowMask can be combined with titled windows (using NSTitledWindowMask) to allow a contentView’s frame to be the full size of the window and take up space under the toolbar/titlebar. Doing this may still require certain UI pieces to not appear under the titlebar/toolbar; to find that area, utilize the contentLayoutRect and contentLayoutGuide. Please also refer to the notes about the contentInsets property and related new features in NSScrollView to see how to use NSFullSizeContentViewWindowMask in advanced situations.
Another new property is called titleVisibility and the enum options are:
typedef NS_ENUM(NSInteger, NSWindowTitleVisibility) {The method canStoreColor is now deprecated; it has not been used in some time.
/* The default mode has a normal window title and titlebar buttons. */
NSWindowTitleVisible = 0,
/* The always hidden mode hides the title and moves the toolbar up into the area previously occupied by the title. */
NSWindowTitleHidden = 1,
};
NSWindow has never supported clients adding subviews to anything other than the contentView. Some applications would add subviews to the contentView.superview (also known as the border view of the window). NSWindow will now log when it detects this scenario: "NSWindow warning: adding an unknown subview:". Applications doing this will need to fix this problem, as it prevents new features on 10.10 from working properly. See titlebarAccessoryViewControllers for official API.
NSWindow now has the ability to add officially known subviews to the titlebar/toolbar area. The views are to be wrapped with a new NSViewController subclass called NSTitlebarAccessoryViewController and added to the window with the "titlebarAccessoryViewControllers" API. There are a set of methods to add and insert the titlebarAccessoryViewControllers, such as addTitlebarAccessoryViewController: and removeTitlebarAccessoryViewControllerAtIndex:. However, one can also utilize "removeFromParentViewController" to easily remove a given child view controller. NSTitlebarAccessoryViewController has a property to tell NSWindow where to place the view (layoutAttribute) and a property to determine how it behaves in full screen (fullScreenMinHeight). The NSToolbar fullScreenAccessoryView API is now deprecated, and clients should utilize this new API.
HUD style windows (utilizing the NSHUDWindowMask) now automatically utilize NSVisualEffectView to create a blurred background. Applications should set the NSAppearance with the name NSAppearanceNameVibrantDark on the window to get vibrant and dark controls.
NSView
shouldDrawColor is now deprecated. It has not meant anything meaningful in quite some time, and should not be used.The "gState" class of functions in NSView (and some auxiliary classes) are now deprecated. In many cases, they did not do anything, and their use was not well defined.
NSSegmentedControl
NSSegmentedControl (and the underlying cell) properly respect the isBordered flag for applications linked on 10.10 and higher. The value is saved with the normal encoding routines. By default, the cell will have setBordered:YES done in init.Yosemite's Translucent Backgrounds and Vibrancy / NSVisualEffectView
Translucent backgrounds and associated "vibrancy" in Yosemite are supported by a new view class, NSVisualEffectView. “Vibrancy” describes a compositing mode that does special blending such as “Plus Darker”, “Plus Lighter”, “Color Dodge”, and “Color Burn.” It is automatically used by the system in many places, including pop-overs, “source list” and sidebar table views, and sheets. Some dynamically use vibrancy based on their contents; for example, NSTextField does when certain special colors are used, such as the new colors: [NSColor labelColor] and [NSColor secondaryLabelColor].If you need to support vibrancy in one of your own views, the following conditions must be met:
• The view must be contained inside an NSVisualEffectView.
• The view must return YES from allowsVibrancy.
• The view’s effectiveAppearance must return YES from allowsVibrancy (this means it must be either the NSAppearanceNameVibrantLight or NSAppearanceNameVibrantDark appearance). (Typically you set the appearance on the window, or on the NSVisualEffectView, and the subviews will inherit the appearance.)
NSVisualEffectView’s internal implementation of blurring and vibrancy depends on the blendingMode. If the blendingMode is set to NSVisualEffectBlendingModeBehindWindow, then blurring is achieved by describing a rectangle to the window server that should be blurred. Areas that should be vibrant are also described in a similar way. Once a particular region is described to be vibrant, ANYTHING drawn in that region will be drawn vibrant, even things drawn drawn before the vibrant subview. Therefore, once a view returns YES from allowsVibrancy, both it and all of its subviews will always be vibrant—a subview can not turn off vibrancy by returning NO from allowsVibrancy. For example: consider a large square NSView that returns NO from allowsVibrancy which fills its contents with [NSColor blueColor]. Consider an NSTextField that is on top of this blue view, and the NSTextField returns YES from allowsVibrancy. The square bounding frame for the NSTextField will become vibrant, including the blue color beneath, which will look wrong (the blue rectangle under the text will be a darker blue). The solutions to this problem: The blue view should also be vibrant, and return YES from allowsVibrancy. Or, the NSTextField needs to opt-out of vibrancy. Opt-ing out of vibrancy can be done by one of the following: setting the appearance back to NSAppearanceNameAqua OR using controlTextColor OR overriding allowsVibrancy and returning NO. It is possible to opt-out rectangular 100% opaque views by adding another NSVisualEffectView, and subviews to it that return NO from allowsVibrancy. These overlapping siblings will not be vibrant, but ONLY works for the blending mode NSVisualEffectBlendingModeBehindWindow. Not that this will only respect the rectangular shape.
If the blendingMode is set to NSVisualEffectBlendingModeWithinWindow, then the use of Core Animation layers is required. It is best to call view.wantsLayer = YES on the parent view that contains the NSVisualEffectView, as blurring will occur with it and the children of the NSVisualEffectView. Blurring for NSVisualEffectView is achieved by using special Core Animation filters. Vibrancy for a particular subview that returns YES from allowsVibrancy is achieved by setting the layer.compositingFilter. Applications that have vibrant views should not change the layer.compositingFilter for a view. A layer’s compositingFilter applies to how the layer and all of its children are composited to the parent layer, and it is not possible to have a child view to NOT be vibrant. However, you can add sibling views over the vibrant view; the overlapping sibling views will correctly not be vibrant. Note this ONLY works for the blending mode NSVisualEffectBlendingModeWithinWindow.
It is not recommended to subclass NSVisualEffectView and override -drawRect: or -updateLayer.
NSVisualEffectView with the NSVisualEffectBlendingModeWithinWindow blending mode can not overlap a view with the behindWindow blending mode; doing so will cause drawing artifacts. Similarly, behindWindow can not overlap withinWindow.
Note that a hidden NSVisualEffectView may still have a performance impact; at this time it is better to remove it from the view hierarchy.
NSImage changes to named image lifetime
Beginning in Mac OS X 10.10 named images are retained. Previously, named images had been weakly referenced (and occasionally cached.) This allows applications to register a named image using the -[NSImage setName:] API, and retrieve it later using the +[NSImage imageNamed:] API. To achieve the same pattern on prior releases, it was necessary to hold an extra reference to the image. Applications wishing to ensure named images are deallocated must call -[NSImage setName:] with nil, before releasing their reference to the image. Note that changing the name of system artwork, or bundle artwork loaded through +[NSImage imageNamed:] is not supported.NSImage support for sliced images
The resizingMode property of an image defines how that image is drawn into a rectangle larger or smaller than that image's natural size. When drawn into a larger rectangle NSImageResizingModeTile will replicate the image like a pattern. When drawn into a smaller rectangle the image will be clipped. NSImageResizingModeStretch will resize the image to fit precisely into the specified rectangle. Both modes have no effect when the image is drawn into a rectangle of its native size. NSImageResizingModeStretch is the default, and matches the behavior of previous releases.The capInsets property allows the image to have specific regions that are stretched or tiled independently, based on resizingMode. In both modes the corners defined by the capInsets property are drawn without scaling or tiling. When the resizingMode is NSImageResizingModeTile, the horizontal edges will be replicated vertically, the vertical edges will be replicated horizontally, and the center of the image will be replicated on both axes. When the resizingMode is NSImageResizingModeStretch, the horizontal edges will be stretched vertically, the vertical edges will be stretched horizontally, and the center of the image will be stretched on both axes.
Note that drawing an image into a rectangle smaller than its natural size will cause the edges and corners to be stretched to fit into that rectangle.
NSBitmapImageRep support for image data byte-swapping
In Mac OS X 10.10 NSBitmapImageRep now supports explicitly specifying the endianness of image data. This can be accomplished by using the new NS16BitLittleEndianBitmapFormat, NS32BitLittleEndianBitmapFormat, NS16BitBigEndianBitmapFormat, and NS32BitBigEndianBitmapFormat enumerations. These behave equivalently to the CGBitmapByteOrder enumerations. Setting no byte order option produces behavior identical to MacOSX 10.9. Setting more than one byte order option is illegal.Importantly 8-bit BGRA image data can be expressed by passing bitsPerSample=8, samplesPerPixel=4, bitmapFormat=NSAlphaFirstBitmapFormat|NS32BitLittleEndianBitmapFormat, and bitsPerPixel=32 to -[NSBitmapImageRep initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpace:bitmapFormat:bytesPerRow:bitsPerPixel:].
NSOpenGLContext conformance to NSLocking
NSOpenGLContext now conforms to the NSLocking protocol. -[NSOpenGLContext lock] and -[NSOpenGLContext unlock] should be considered equivalent to CGLLockContext([NSOpenGLContext CGLContextObj]) and CGLUnlockContext([NSOpenGLContext CGLContextObj]).NSOpenGLContext getter for associated pixel format
It is now possible to access the pixel format associated of an OpenGLContext using the -[NSOpenGLContext pixelFormat] method.Changes to -[NSView noteFocusRingMaskChanged]
-[NSView setNeedsDisplay:] and -[NSView setNeedsDisplayInRect:] used to call -[NSView noteFocusRingMaskChanged]. In OS X 10.10 this is no longer the case. Views must manually make the call -[NSView noteFocusRingMaskChanged]. This enables views to redraw portions of themselves without needlessly redrawing their focus rings, which can be expensive.New NSGraphicsContext CGContext methods
In MacOSX 10.10 NSGraphicsContext offers two new methods, +graphicsContextWithCGContext:flipped: and -CGContext. These methods are exactly the same as the existing +graphicsContextWithGraphicsPort:flipped: and -graphicsPort methods, excepting that they are typed to accept and return CGContextRef respectively. The old methods will be deprecated in a future release.NSColor
NSColor exposes new system colors in 10.10 for static text and related elements: labelColor, secondaryLabelColor, tertiaryLabelColor, and quaternaryLabelColor. The first two are for primary and secondary static text elements. These colors are applied to new labels dragged out in Interface Builder. The tertiaryLabelColor is for disabled static text elements, and quaternaryLabelColor is for large disabled static text elements. These colors can also be applied to other related UI elements where related and appropriate; for instance, quaternaryLabelColor is recommended for separator lines as well as large glyphs or icons.These new colors as well as some of the existing system colors now paint differently depending on the current appearance. Please be sure to set colors while the appropriate appearance is in effect and do not cache their individual color component values.
Hyphenation
Hyphenation is now enabled by default for all text rendering and measurement. The hyphenation factor is 0.4.In addition, the handling of the soft hyphen character (U+00AD) has been changed. In prior versions of OS X, we handled the character just as a line break opportunity when the hyphenation is disabled. Now, we always show the hyphen glyph whenever wrapping at the character.
Notes specific to OS X 10.9
Some of the major topics covered in this section include:- Responsive scrolling
- Spaces and multiple screens
- NSStackView
- NSAppearance and light content controls
- Sheet presentation
- Export as PDF
- Layer-backed view updates
- Tagging support in NSSavePanel
- NSTableView/NSOutlineView updates
- Occlusion
- NSSharingService
- NSNibLoading
Sheet Presentation
Sheet API has been moved from NSApplication to NSWindow. In this move, a number of changes to the API have been made. First is the use of blocks as completion handlers, rather than delegation.Rather than presenting a sheet:
[NSApp beginSheet:mySheet modalForWindow:docWindow modalDelegate:nil didEndSelector:nil contextInfo:nil];This will instead be:
[docWindow beginSheet:mySheet completionHandler:nil];Likewise, when handling the ending of a sheet, a separate method was needed to act as the endSelector:
[NSApp beginSheet:mySheet modalForWindow:docWindow modalDelegate:self didEndSelector:@selector(didEndSheet:returnCode:contextInfo:) contextInfo:nil];
- (void)didEndSheet:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {This will instead be handled in the completionHandler block:
[sheet orderOut:self];
NSLog(@“Good bye sheet.”);
}
[docWindow beginSheet:mySheet completionHandler:^(NSModalResponse response) {In this example, you’ll also notice that -orderOut: no longer needs to be called in the completion handler. If you do not dismiss the sheet, it will be done for you after the completion handler finishes.
NSLog(@“Good bye sheet.”);
}];
NSModalResponse is a new typedef in NSApplication created with an enum of modal responses. NSModalResponseStop, NSModalResponseAbort, and NSModalResponseContinue are added in replacement of the deprecated NSRunStoppedResponse, NSRunAbortedResponse, and NSRunContinuesResponse.
NSWindow also adds two more modal responses: NSModalResponseOK and NSModalResponseCancel (which are replacements for NSPanel's NSOKButton and NSCancelButton).
A sheet's parent (the window or sheet it is attached to), can now be retrieved with -[NSWindow sheetParent]. This relationship is maintained from the time the sheet is begun with -beginSheet:… to when it is ordered out.
Another change brought by the moving of this API is queued and critical sheets. Previously, attempting to show a sheet on a window that already has a sheet would result in a 'Beep' to the user, a loss of contextInfo's memory, and no return call to the modalDelegate. The new NSWindow API will queue the second sheet if there is already a sheet present on the window. Once the first sheet is ended and ordered out, the second sheet will be brought down. Queued sheets can be ended ahead of time; so that if a queued sheet becomes unnecessary before the current sheet ends, the queued sheet doesn't have to be presented later. Critical sheets are sheets that are time sensitive and critical to the user; these will skip the queue and be presented on top of an existing sheet, if necessary. Sheets attempted to be presented while a critical sheet is up will be queued like normally; and after the critical sheet is dismissed, the previously presented sheet and queued sheets will be able to be interacted with again.
NSApplication's sheet API will continue to work as it did before, without the ability to queue sheets.
Alert dialogs
NSAlert's -beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo: has been deprecated in favor of -beginSheetModalForWindow:completionHandler:. Through its use of NSWindow's new sheet API, alerts with an alert style of NSCriticalAlertStyle will be presented as critical sheet.NSAlertFirstButtonReturn, etc are marked as additional NSModalResponses used by -beginSheetModalForWindow:completionHandler: and -runModal. -[NSAlert runModal] is declared as returning an NSModalResponse (a typedef of NSInteger).
NSAlert's Functional API and -alertWithMessageText:defaultButton:alternateButton:otherButton:informativeTextWithFormat: has been deprecated. These creation methods have used different return codes from modern alert API; these old return codes (NSAlertDefaultReturn, NSAlertAlternateReturn, etc) are also deprecated. NSAlert and its properties should be used to create an alert instead.
Controls' -stringValue
NSSlider, NSStepper, and NSLevelIndicator use the set NSFormatter to format the result of -stringValue as well as retrieve the value set through -setStringValue:If there is no set formatter, the value is formatted to an NSString with %g.
NSClipView
Before 10.9, overriding -constrainScrollPoint: aided in allowing developers to center (or use any custom positioning) a document within a scroll view. This works for constraining scrolling; however, constraining the changes resulting from an animation of a change in magnification requires knowledge of the size of the bounds. For this, NSClipView now has constrainBoundsRect:. This comes with the deprecation of -constrainScrollPoint:, as it can handle a superset of the problems that -constrainScrollPoint: could.NSMatrix
The order of cells in NSMatrix is right-to-left layout when -[NSApplication userInterfaceLayoutDirection] returns NSUserInterfaceLayoutDirectionRightToLeft. This can be disabled or forced in IB by switching the Control's 'Mirror' value in IB.NSPrinter
PPD access is deprecated through NSPrinter's table methods (-statusForTable:, -stringForKey:inTable:, etc). PMPrinter API should be used instead.NSSlider
NSSliderCell exposes API to support appearance customization:* -drawTickMarks is an override point to customize the drawing of tick marks.
* -barRectFlipped: returns the rectangle in which the slider bar is drawn.
* Overriding -drawBarInside:flipped: will actually override bar drawing.
* NSSliderCell's -knobRectFlipped: can be called directly if needed.
On apps linked against 10.9, the values returned by the dimensional methods (and passed into the drawing methods) are fitted to the size of default slider artwork. Any custom sliders should be sure to either override the dimensional methods with valid results for their custom artwork, or at the very least consider this fitting inside of -drawBarInside:flipped: and -drawKnobInside:flipped:. The dimensions returned by the default slider cell may also change in future releases, so they should not be assumed to remain constant. For binary compatibility, in apps linked before 10.9, NSSliderCell’s methods return and are passed the legacy, non-fitted rects.
Linear NSSliders now support right-to-left configuration when -[NSApplication userInterfaceLayoutDirection] returns NSUserInterfaceLayoutDirectionRightToLeft. This can be disabled or forced in IB by switching the Control's 'Mirror' value in IB.
NSSliderCell no longer receives an additional call to -continueTracking after the -stopTracking call.
NSSlider and NSSliderCell have formal deprecations for previously obsolete methods (-setImage:, -image, -setKnobThickness:, -setTitleCell:, -titleCell, -setTitleColor:, -titleColor, -setTitleFont:, -titleFont, -setTitle:, -title).
NSStackView
NSStackView is a new class in OS X 10.9. It is used to layout horizontal or vertical stacks of views using auto layout. Necessary constraints will automatically be created and modified when adding and removing views from a stack view to maintain a cohesive layout. This behavior is extended by customizing various properties of the stack view and the views it holds. Decreasing the clipping resistance priority allows the stack view to become smaller than the the minimum required to hold the provided views. Depending on the visibility priority associated with the internal views, they may either overflow and clip off the side, or drop from the view hierarchy. These dropped views are still retained by the stack view, and will be reattached if the stack view becomes the necessary size. Views can be forced to be dropped or reattached by setting the necessary visibility priority.Note that NSStackView's hugging and clipping resistance priorities are distinct from NSView's contentHugging and compression resistance priorities. Since stack views have no intrinsic content sizes, contentHugging does not affect it. However, NSStackView's hugging priority is the priority at which the StackView wants its internal spacing to be at a minimum and "hug" its contained views. The clipping resistance priority is used to prevent the clipping of the contained views; a value less than NSLayoutPriorityRequired means the stack view can become less than the minimum size required to hold all of the its views.
Keep in mind that the default hugging priority of NSStackView is NSLayoutPriorityDefaultLow (250). If you add views with hugging priorities of the same value, the constraints can be ambiguous, causing unexpected layouts such as oddly wide or tall views. Fixing these issues can be as simple as tweaking the stack view’s hugging priority, the view’s content hugging priority, or by adding explicit size constraints on the view.
The view hierarchy that NSStackView manages is private. Additional subviews and constraints can be added to the stack view itself, but should not be added to its private views. These views should not be assumed to remain consistent in future releases, nor should they be assumed to be encoded or decoded with NSCoder. Because external constraints can be added to the views you add to a stack view, there should be no need to manipulate or measure these private views.
NSTabView
NSTabView uses the contentCompressionResistancePriority in the orientation of the tabs to prevent clipping of tabs. Top and Bottom tabs will use the horizontal contentCompressionResistancePriority; left and right tabs will use the vertical contentCompressionResistancePriority.NSWindow
NSUnscaledWindowMask is formally deprecated.Application and Window Occlusion
AppKit has a new API to notify an application if its windows are visible to the user on screen. If a window is not visible to the user then it is occluded.There are two sets of methods; one set on NSApplication and one set on NSWindow. Both sets include a delegate method, an NSNotification, and a method to query the current state. To determine if any part of your application is visible, your application delegate can implement the method like this:
- (void)applicationDidChangeOcclusionState:(NSNotification *)notificationWhen the delegate method is called or the notification is posted, use either -[NSApplication occlusionState] or -[NSWindow occlusionState] to retrieve the current occlusion state of the application or window. Note that the result of this method is a bitfield, so the appropriate way to check for visibility is to use the bitwise-AND operator. Do not use the equality operator.
{
if ([NSApp occlusionState] & NSApplicationOcclusionStateVisible) {
// Visible
} else {
// Occluded
}
}
Windows are considered occluded if their entire content, including title bar and tool bar, is 100% covered by another opaque window. Windows are also occluded if they are ordered off screen, minimized to the dock, or on another space. Partial occlusion counts as “visible.” An application is only considered occluded if all windows owned by the application are occluded. That is, if any part of any window is visible, then the application is visible.
If your application has no windows except the menu bar, then your application is considered occluded. However, if your application uses an NSStatusItem, then the window that displays the status item is considered owned by your application and your application will not become occluded while the status item is visible.
This API can be used for increasing the performance of your application, including power, CPU, and network efficiency. For example, when your application is invisible to the user, it might cease expensive operations like retrieving data over the network or drawing an animation. You may also decide to trigger the start of expensive work when notified that the application or window has become visible.
The system reserves the right to delay notification of visibility during certain performance-critical periods. For example, exposing all windows on the system when the user enters Mission Control. In these cases the system will favor responsiveness to the user, and applications will be notified after the performance critical period has finished.
Auto Layout for NSViews in NSMenus
In 10.9, NSMenu has better support for views using auto layout. If the top-level view set on the menu item has translatesAutoresizingMaskIntoConstraints set to NO, the menu item will establish a required LeadingX constraint that position the view in the menu, and another required width constraint that sizes it equal to the menu. The height is the responsibility of the view.If translatesAutoresizingMaskIntoConstraints is YES, the behavior is as it was in previous releases.
Auto Layout and window resize cursors
Window resize cursors now correctly reflect the minimum and maximum window sizes as determined by auto layout.NSPopUpButtonCell cellSize
NSPopUpButton's cellSize method no longer considers the width of the key equivalent, which has never been drawn in OS X anyways.View-based NSTableView scrolling optimized under Auto Layout
In 10.9, View-based NSTableViews have been optimized under auto layout to improve scrolling performance. To take advantage of the optimization, do not establish constraints that relate a view within an NSTableView row to a view outside of the NSTableView. (This would be difficult to do in any case - if you design your table view rows in IB you should be fine.)NSApplication setActivationPolicy:
In 10.9, setActivationPolicy: now supports NSApplicationActivationPolicyAccessory.NSWorkspace openURL:
In 10.9, -[NSWorkspace openURL:] may show an error dialog on failure, for apps linked using the 10.9 SDK or later. The return value of openURL: is unaffected.NSWindow implicit animations
For apps compiled with the 10.9 SDK, -[NSWindow setFrame:display:] and -[NSWindow setFrameOrigin:] now perform animations, if invoked in an implicit animation context. To opt out, invoke -[NSWindow setFrame:display:animate:] and pass NO for the animate parameter.Full screen NSToolbar changes
Before 10.9, NSToolbar would ignore the fullScreenAccessoryViewMinHeight when NSApplicationPresentationAutoHideToolbar is set. In 10.9, setting a fullScreenAccessoryViewMinHeight will cause it to be respected whether AutoHideToolbar is set or not. If you set a value of 0, the accessory view will animate in and out as the menu bar animates in or out.Status Items with multiple menu bars
10.9 introduces multiple menu bars, each of which draws the status items. If your status item has a custom view, this view is positioned in one menu bar, and other menu bars get a “clone”, which looks identical. The clones are not exposed in the API.The clones are drawn by redirecting your custom view’s drawing into another window. This means that your status item should not make assumptions about the drawing destination. For example, it should not assume that a call to drawRect: is destined for the view’s window, or that the resolution of the drawing destination matches the resolution of the status item’s screen. You must also not assume that the status item is on any particular display, except as described below.
The clones are only redrawn in NSDefaultRunLoopMode. This allows the status item to limit highlighting to one display, by driving the run loop in another mode, such as NSEventTrackingRunLoopMode. For example, if you wish to simulate a menu, you would implement mouseDown: to show your window, and run the run loop in NSEventTrackingRunLoopMode until you determine that the window should be dismissed. While the run loop is in this mode, only the true status item will redraw. Clone status items will not redraw, and therefore they will not show any highlight applied to the true status item.
When a clone status item is clicked, the clone exchanges locations with the true status item. This means that the location and screen of the status item window is reliable from within mouseDown:. You can access this information from your custom view, for example, using [[view window] screen] to position a window on the same screen as the status item.
NSSegmentedControl now better respects sendActionOn:
In 10.9, segmented controls that have sendActionOn: set to NSLeftMouseDownMask now work correctly.constrainFrameRect:toScreen: now invoked for borderless windows
Prior to 10.9, the NSWindow method -[NSWindow constraintFrameRect:toScreen:] was invoked only for windows with NSTitledWindowMask set in their styleMask. In 10.9, this method is invoked for all windows. The default implementation does a more limited constraining for non-titled windows, as described in “NSWindows constrained to not intersect the menu bar” below.NSWindows constrained to not intersect the menu bar
In 10.9, in support of the new multi-monitor architecture, windows are now constrained to not intersect the menu bar on their containing space. This restriction was already in place for titled windows, but it has been extended to borderless windows whose level is at least NSNormalWindowLevel but less than NSMainMenuWindowLevel. This behavior is implemented in -[NSWindow constraintFrameRect:toScreen:]. You may override that method in an NSWindow subclass to adjust or prevent this constraining.NSNibLoading
The following nib loading methods on NSBundle and NSNib have been formally deprecated: +[NSBundle loadNibFile:externalNameTable:withZone:], +[NSBundle loadNibNamed:owner:], -[NSBundle loadNibFile:externalNameTable:withZone:], -[NSNib initWithContentsOfURL:], -[NSNib instantiateNibWithExternalNameTable:], and -[NSNib instantiateNibWithOwner:topLevelObjects:].The ARC-compatible methods introduced in 10.8 should be used instead: -[NSBundle loadNibNamed:owner:topLevelObjects:], -[NSNib initWithNibData:bundle:], and -[NSNib instantiateWithOwner:topLevelObjects:].
It is important to note the difference in memory management between the deprecated and new methods. The deprecated methods retained the top level objects of the loaded nib file, requiring the caller to explicitly release them. This is no longer necessary with the new methods because they follow the standard Cocoa memory management rules and autorelease the top level objects. IBOutlet properties to top level objects should be strong (retain) to demonstrate ownership and prevent deallocation. Alternatively, one may hold a strong reference to the top level objects array, available as an out parameter on these methods.
Sharing Service
In 10.9, NSSharingService provides some new built-in services:NSString * const NSSharingServiceNamePostOnTencentWeibo;In addition NSSharingService provides the following new properties:
NSString * const NSSharingServiceNamePostOnLinkedIn;
NSString * const NSSharingServiceNameUseAsFacebookProfileImage;
NSString * const NSSharingServiceNameUseAsLinkedInProfileImage;
@property (copy) NSString *menuItemTitle;
@property (copy) NSArray *recipients;
@property (copy) NSString *subject;
@property (readonly, copy) NSString *messageBody;
@property (readonly, copy) NSURL *permanentLink;
@property (readonly, copy) NSString *accountName;
@property (readonly, copy) NSArray *attachmentFileURLs;
Responsive Scrolling
Mac OS 10.9 has a new feature called Responsive Scrolling. Responsive Scrolling allows the application to continue to rapidly scroll content even when the application’s main thread is busy doing other work. AppKit accomplishes this by having the document view draw more than what is currently visible during idle (see the Overdraw section in these release notes). The responsive scrolling thread is then free to replace what is on the screen with this previously drawn content. Responsive Scrolling requires one to use an NSScrollView, NSClipView and a document view.By default, AppKit tries to turn on this feature where applicable. The following primary conditions, however, are not compatible with Responsive Scrolling. If any of the following conditions are met, the traditional scrolling behavior is performed.
* The application is primarily a Carbon application
* The documentView has an OpenGL context
* The window alpha is not 1.0
* The application links on 10.7 or prior (the application must link on Mountain Lion or higher to support this feature)
There are some secondary requirements that your NSScrollView subclass, NSClipView subclass, or document view must meet for them to be eligible for Responsive Scrolling. These secondary requirement checks may be bypassed by overriding the following new NSView class method on the view that fails the secondary requirements. You should strive to meet the requirements and only use this override as a last resort.
@implementation NSViewThe list of secondary requirements are:
+ (BOOL)isCompatibleWithResponsiveScrolling;
@end
* Do not override -scrollWheel: in an NSScrollView, NSClipView subclass, or in the document view. (See the event modal notes in this section)
* Do not override -drawRect: in an NSScrollView subclass
* Do not override -lockFocus: in an NSScrollView, NSClipView subclass, or in the document view.
Layer backed secondary requirements:
* The root layer must be either the NSScrollView or an ancestor view
Traditional drawing secondary requirements:
* copiesOnScroll must be set to YES on the NSClipView
* The documentView must override -isOpaque and return YES
Event Modal
Traditionally, each scroll wheel event is uniquely hit tested and then handled. For non-gesture scrolling devices, this is still true. For gesture scrolling devices, this has changed dramatically. Once NSScrollView receives a scroll wheel event, it goes into a concurrent tracking loop. That is, future scroll wheel events are captured privately and processed on a background thread until the gesture, and any associated animation, is complete. If the user physically performs a consecutive scroll gesture, for responsive tracking purposes, it is considered a continuation of the current gesture and is therefore not hit tested.
You may be able to replace your NSScrollView, NSClipView subclass, or document view -scrollWheel: override with one or more of the following techniques.
* Register for bounds change notifications on the clip view
* Register for the LiveScrollNotifications (see the the ScrollView section)
If you only need to peek at the initial scroll wheel event, then return yes from +isCompatibleWithResponsiveScrolling to explicitly opt in to Responsive Scrolling.
While the scroll wheel events are being processed on a background thread, the main thread is periodically requested to synchronize. During this synchronization, the clip views bounds are updated and any required notification are posted. If the main thread is free, it is also asked to perform additional overdraw in the direction of the scroll (see NSScrollView - Overdraw). The result is that if the main thread is busy and / or cannot synchronize quickly enough, the visibleRect of the document may differ from what the user sees on screen. Also, if there is no existing overdraw content at the scrolled to location, then concurrently moving the content is paused while waiting for the main thread to catch up.
NSScrollView - LiveScrollNotifications
NSScrollView posts new notifications in response to user initiated scrolling. This may occur due to scroll wheels, scroll gestures, scroller tracking or page up / down animation. These notifications are sent on the main thread.NSString * const NSScrollViewWillStartLiveScrollNotification;If the user action has a known start and end point, the “WillStart” and “DidEnd” notifications are issued with 0 or more “Did” notifications sent in between. If the scroll is an animation (for example rubber banding), the “DidEnd” notification is not sent until the animation completes. If the scroll view is performing Responsive Scrolling, multiple consecutive scrolling gestures are grouped together into a single series of LiveScroll notifications bracketed by a single “WillStart” / “DidEnd” pair.
NSString * const NSScrollViewDidLiveScrollNotification;
NSString * const NSScrollViewDidEndLiveScrollNotification;
NSScrollView - Floating Subviews
Some subviews of the document view do not scroll with the rest of the document. Instead these views appear to float over the document (see NSTableView floating group rows). The following API makes it easier to work with these types of floating views. Also, NSScrollView ensures that any scrolling on the non-floating axis is visually performed synchronously with the document content. Note: You are responsible for keeping track of the floating views and removing them via -removeFromSuperview when they should no longer float. A view may only float on one axis at a time.- (void)addFloatingSubview:(NSView *)view forAxis:(NSEventGestureAxis)axis;
NSScrollView - Overdraw
To facilitate Responsive Scrolling, your document view will be asked to draw portions that are not currently visible to the user. AppKit balances the amount of non-visible content drawn with the amount of memory and power usage such drawing requires. NSScrollView is then free to visually scroll to any previously overdrawn content during Responsive Scrolling.If your document view has multiple subviews or you need more control over the overdraw area, your document view needs to implement the methods to ensure that all the appropriate content exists in the requested overdraw area.
The following method is called by NSView with a 'rect' for a recommended area that should be fully rendered for overdraw. Override this method and bring in additional subviews and pre-cached content for the 'rect' in order to perform responsive scrolling. Calling super may be required for some subclasses (such as NSTableView and NSOutlineView), so in general, super should always be called. To suppress overdraw for a particular view (such as NSTableView), override this method and call [super prepareContentInRect:[self visibleRect]].
- (void)prepareContentInRect:(NSRect)rect;The preparedContentRect is the area of the NSView that has full content coverage. In general, this should be called with the area that is filled in fully with views. It should always include the visibleRect. Set this with a value equal to the visibleRect to have overdraw start from the visibleRect and automatically grow larger on idle, as is needed for optimal system performance.
@property NSRect preparedContentRect;
NSApplication
-[NSApplication stopModal] and -[NSApplication stopModalWithCode:] can now be called from an NSTimer or other runloop source. This lifts the restriction requiring abortModal to be used in these cases.NSWindow styleMask
AppKit reserves the right to use all bits of the styleMask in NSWindow, and some are used for private communication with other Apple frameworks. Please do not use undocumented styleMask bits for your own NSWindow subclasses.Spaces and Multiple Screens
In 10.9, we have added a feature where each screen gets its own set of spaces, and it is possible to switch between spaces on one screen without perturbing the spaces on the other screens. In this mode, a fullscreen window uses one screen, and leaves the contents of any other screens unchanged.Each screen now has its own menu bar, and it is possible to show the Dock on any screen, provided you have the Dock set to “Position on Bottom”.
The menu bar has an active appearance on the active screen, which is typically the screen containing the key window. Menu bars on other screens have an inactive appearance.
In this mode, it is desirable for new windows to open on the active screen. In support of this model, +[NSScreen mainScreen] now returns the active screen, which is slightly different than its prior behavior of returning the screen containing the keyWindow, if any, and the zero screen otherwise.
A window restored at app launch through -restoreStateWithCoder: will return to its previous location, independent of active screen. A window positioned using -setFrameAutosaveName: will prefer the active display.
This feature can be disabled by unchecking the preference named “Displays have Separate Spaces” in the Mission Control preference pane in System Preferences. This setting only takes effect after logging out and back in, or restarting. NSScreen has API to query whether the separate space feature is enabled:
+ (BOOL)screensHaveSeparateSpaces NS_AVAILABLE_MAC(10_9);When this feature is enabled, windows may not visibly span displays. A window will get assigned to the display containing the majority of its geometry if programmatically positioned in a spanning position. A window will get assigned to the display containing the mouse if the window is moved by the user. A window clips to the edge of the display, whether or not there is another adjacent display.
Tagging Support in NSSavePanel
NSSavePanel provides a field that allows users to specify Tags (a new feature in OS X 10.9) that should be applied to the resulting file. However, since NSSavePanel isn't responsible for creating the file, your application should adopt new API to ensure the requested Tags are set correctly.To opt in to NSSavePanel Tagging support, you should invoke -[NSSavePanel setShowsTagField:YES] prior to displaying the panel. When the user click Save in the panel, you can get the Tag names they entered by invoking -[NSSavePanel tagNames]. After creating the file at the requested URL, you should set the requested Tag names on the file by using the NSURLTagNamesKey API.
If your application does not adopt the above API, NSSavePanel will still show the Tags field and will attempt to automatically apply the Tags by listening to file system change notifications for a limited duration to detect when your application creates the requested file. However, this technique is imperfect, so you are strongly encouraged to test your application's support for tags in the save panel and adopt the above API if needed.
Export as PDF
In OS X 10.9, NSDocument and NSPrintOperation provide new API and functionality to aid you in creating an Export as PDF option that is consistent with the rest of the operating system.NSDocument has a new standard IBAction method called -saveDocumentToPDF:. If your NSDocument subclass already implements -printOperationWithSettings:error:, then invoking this method will cause NSDocument to use the resulting NSPrintOperation to prompt the user for a location and save a PDF. The resulting PDF export panel will also include certain standard print panel configuration controls that you have enabled (like paper size and orientation) and the first accessory controller from the NSPrintOperation's NSPrintPanel.
The default implementation of -saveDocumentToPDF: simply invokes [self printDocumentWithSettings: @{ NSPrintJobDisposition : NSPrintSaveJob} showPrintPanel:NO delegate:nil didPrintSelector:NULL contextInfo:NULL]. When invoked with these parameters, the method will invoke -PDFPrintOperation instead of -printOperationWithSettings:error:. The default implementation simply invokes [self printOperationWithSettings:@{ NSPrintJobDisposition : NSPrintSaveJob } error:NULL], but you can override this method if you need to customize the way your application creates PDFs or to provide a different accessory controller.
If your application doesn't use NSDocument, you can still use NSPrintOperation to implement Export as PDF. Prior to OS X 10.9, running an NSPrintOperation that had its job disposition set to NSPrintSaveJob and its NSPrintJobSavingURL set to nil would result in undefined behavior. However, on OS X 10.9, doing this will cause NSPrintOperation to display an NSPDFPanel, prompting the user for a location where it will save the PDF.
Whether or not you use NSDocument, you can modify the PDF export panel used by NSPrintOperation by either creating a new NSPDFPanel with the desired options and accessory controller, or modifying the one that NSPrintOperation creates automatically.
If your application is unable to use NSPrintOperation to generate PDFs, you can still use NSPDFPanel to ensure your application's user interface is consistent with the rest of the operating system. In order to do this, you should first create an NSPDFInfo object, optionally changing the paperSize and orientation properties. Then you should invoke -[NSPDFPanel beginSheetWithPDFInfo:modalForWindow:completionHandler:] passing the NSPDFInfo object you created. When the completion handler is invoked, the given NSPDFInfo object will be modified with the URL, file extension hidden flag, and Tag names, and rendering parameters that should be used when creating the PDF.
Shoebox-like applications may wish to generate multiple separate PDFs when the user has selected multiple items. To support this, NSPDFPanel supports the NSPDFPanelRequestsParentDirectory option. When run with this option, NSPDFPanel will prompt the user to choose a directory instead of a directory and a document name. The URL of the resulting NSPDFInfo object will contain the user's chosen directory. You are responsible for appending a file name to this path.
If your application manually runs an NSPDFPanel but uses NSPrintOperation to create PDFs, NSPrintInfo provides -takeSettingsFromPDFInfo:, which will modify the receiving NSPrintInfo with all the settings from the given NSPDFInfo. But remember, if you use NSPDFPanelRequestsParentDirectory, you must modify the NSPDFPanel's URL by appending a file name before passing it to -takeSettingsFromPDFInfo:. If the URL doesn't looks like it points to a directory, the method will throw an exception.
Additional Use of -performActivityWithSynchronousWaiting:usingBlock: in NSDocument
NSDocument provides the -performActivityWithSynchronousWaiting:usingBlock: to ensure document concurrent operations are performed and their results presented in a properly serialized manner. Presentation of any alert or error sheet on a document is supposed to be done within an 'activity' block to avoid multiple sheets interfering with each other.NSDocument internally presents many alerts on documents and is responsible for making sure this is done within an 'activity' block. However, prior to OS X 10.9, the alert that is displayed when the user edits a locked document was not presented within an 'activity' block. As a result, alerts originating from application-defined 'activities' could potentially interfere with it, often resulting in deadlocks. This has been fixed on OS X 10.9.
Bug Fixes for NSPathControl in Sandboxed Applications
Prior to OS X 10.9, NSPathControl behaved unexpectedly in sandboxed applications when used with a URL within the user's home directory. NSPathControl would fail to recognize that sandboxing redirects NSHomeDirectory() to the application's sandbox container. Additionally, the NSPathControl was given the URL to the user's real home directory, it would fail to recognize that path as home, and would therefore show the entire path up to the root of the volume instead of stopping at the user's home directory. These issues have been fixed on OS X 10.9.NSSavePanel / NSOpenPanel
Key Value Observation compliance was dramatically increased for public and some private properties of actual, non sandboxed NSOpen and Save panels, including keys affecting other values. For example, if the directory value changes on a save panel, this will cause a KVO notification to be emitted for the URL value as well as the directory value. Sandboxed NSOpen and Save Panels do not have the same level of KVO compliance.In the “On my Mac” mode of the App Centric Open Panel (the open panel shown for applications using iCloud), accessory Views are now hidden by default in and can be revealed by hitting the “Options…” button.
Specification of fonts in nib files
The name of the default system font has changed from "LucidaGrande" to ".LucidaGrandeUI". In the vast majority of cases this should not present a problem. However, there is a case where fonts can be unexpectedly decoded from an archive as "LucidaGrande" rather than the new system system font. This can cause a variety of subtle issues, including slight changes to the layout of certain button titles. This will occur if you are still using nib files rather than xib files, built them using Xcode 4 or earlier, and they use system fonts in non-standard point sizes. When you change the point size to be non-standard, older versions of Xcode will change the displayed font type from "System" to "Custom", and then it will not be decoded as the new system font on Mavericks. Xcode 5 will correct this problem when opening your nib file, so the UI will automatically show "System", but note that it's still necessary to save the changes.NSSavePanel / NSOpenPanel
Automatic key loop recalculation is enabled. This not only obviates the need to explicitly recalculate the key loop after modifying a subview of the accessory view but also makes it possible for the accessory view itself to reliably declare itself a key view.NSSound
Channel mapping is deprecated in 10.9, consequently channels should be mapped at a lower level using AudioUnitSetProperty.Event-specific behavior of -isSwipeTrackingFromScrollEventsEnabled
In 10.9, the user may choose to have different preferences for the “Swipe between pages” behavior for mouse and trackpad input devices. It is now possible for this behavior to be enabled for trackpad devices but disabled for mouse devices (or vice versa). By default, swipe between pages is enabled for trackpads and disabled for mice.AppKit provides the -isSwipeTrackingFromScrollEventsEnabled method to determine the current user preference. The behavior of this method is now based on the current event (as returned by [NSApp currentEvent]). If the current event originated from a mouse device, this method returns the user preference for mouse devices; otherwise, it returns the user preference for trackpad devices.
New Accessibility constants
These accessibility constants have been added:— NSAccessibilityMarkedMisspelledTextAttribute
NSAccessibilityMisspelledTextAttribute was the original attribute to indicate misspelled text. In OS X 10.4, the Cocoa text system added support for NSAccessibilityMarkedMisspelledTextAttribute, which was used to indicate a word that was visibly marked as misspelled (for example, with a red squiggle underneath); the original MisspelledText attribute could also be used on text that was not visibly marked as misspelled (for example, a misspelled word that was currently being edited by the user).
Typically, a screen reader only wants to vocalize what a sighted user could see, and so the MarkedMisspelledText attribute was adopted by VoiceOver to provide feedback to the user about misspelled text. In OS X 10.9, VoiceOver has entirely stopped using the original MisspelledText attribute, and now only checks for MarkedMisspelledText.
When implementing accessibility for a custom text-editing engine, you should generally provide the MarkedMisspelledText attribute in order to support VoiceOver, especially in OS X 10.9 and later. You may optionally also support the MisspelledText attribute for compatibility with other accessibility clients.
— NSAccessibilityDescriptionListSubrole
This subrole is similar to the existing NSAccessibilityDefinitionListSubrole, but is preferred to represent HTML5 objects indicated by the <DL> tag.
Accessibility Notification support for custom accessible objects
The NSAccessibility informal protocol now supports a new protocol method, -accessibilityNotifiesWhenDestroyed.Prior to 10.9, the only accessible objects that could post accessibility notifications were those that inherited from NSView, NSWindow, or NSCell. An application's custom accessible object, subclassed from NSObject, could not post notifications.
In 10.9 and later, an application's custom accessible object may post accessibility notifications if it follows the following guidelines:
– the object must implement -accessibilityNotifiesWhenDestroyed to return YES.
– the object must post the NSAccessibilityUIElementDestroyed notification at appropriate times, typically when the corresponding UI element in the application's visual interface is removed from the screen, and certainly when the accessible object is deallocated.
– the lifetime of the NSObject must match the lifetime of the corresponding element in the application's visual interface. It is common for a custom accessible object that acts as a proxy for an onscreen UI element to be autoreleased and deallocated very quickly, immediately after the application responds to a single accessibility request. This is not sufficient to support posting notifications, because any notification observers that are registered on the object will be removed as soon as the object is deallocated. Instead, the application must arrange for an accessible object that refers to a specific UI element to remain allocated as long as that UI element is visible.Accessibility protected content
The Accessibility API is designed to help make OS X more accessible to users with disabilities. To accomplish this, the API is able to vend all of the strings and values that are displayed onscreen to clients such as VoiceOver. This is critical, especially for book-reading applications, because it allows users who are blind to read and interact with the same content as everyone else.
However, some applications need to be able to prevent their string content from being copied by other applications that use the Accessibility API. It is now possible for an application to tell the Accessibility implementation that some of its content is protected.
There are two steps required to indicate that a particular UI element has protected content:
– use the NSAccessibilitySetMayContainProtectedContent API to indicate that this application contains protected content
– handle requests for NSAccessibilityContainsProtectedContentAttribute in your accessible objects’s -accessibilityAttributeValue: implementation, returning an NSNumber containing YES.
Accessibility API for transient UI
Application UI elements can appear, disappear, or change as a result of mouse movement to certain positions in the content, or other types of user input, such as press or release of a modifier key. This presents several problems for clients of the Accessibility API:– there is no equivalent accessible way to simulate such mouse triggered events
– there's no way to know when/what happened as a result of mouse rollover
The Accessibility API now offers new features to support transient UI elements.
NSAccessibilityShowAlternateUIAction
NSAccessibilityShowDefaultUIAction
These accessibility actions should be implemented to present the alternative or default UI.
NSAccessibilityPostNotificationWithUserInfo
This API allows an accessibility notification to be posted with a user info dictionary. When transient UI elements are shown or hidden, an application should use this API to post NSAccessibilityLayoutChangedNotification with a user info dictionary containing a list of the UI elements that have changed.
NSAccessibilityLayoutChangedNotification
This notification lets accessibility clients such as VoiceOver know that some UI layout change has occurred. The client application then has the option to decide if it wants to give feedback, auto jump to a new UI, list the new or changed UIs in a menu, or do nothing. The notification can be used by anything that changes UI on screen. As an example, this could be triggered by an explicit NSAccessibilityShowAlternateUIAction, or by user input such as mouse hovering. Similarly, performing NSAccessibilityShowDefaultUIAction or mouse exiting a UI element to revert some transient UIs should also cause this notification to fire. The notification can contain a user info dictionary with the key NSAccessibilityUIElementsKey and an array of elements that have been added or changed as a result of this action.
NSAccessibilityAnnouncementRequestedNotification
This notification allows an application to request that an announcement be made to the user by an accessibility client such as VoiceOver. The notification requires a user info dictionary with the key NSAccessibilityAnnouncementKey and the announcement as a localized string. In addition, the key NSAccessibilityAnnouncementPriorityKey should also be used to help accessibility clients determine the important of this announcement. This notification should be posted for the application element.
NSAccessibilityUIElementsKey
This key is used in the user info dictionary for notifications. The value is an array of elements that are associated with the notification.
NSAccessibilityPriorityKey
This key can be used in the user info dictionary for any notification. This gives the client an opportunity to determine how to handle this notification based on the priority. For example, a developer can pass the priority for NSAccessibilityAnnouncementRequestedNotification. Clients such as VoiceOver can then decide to speak the announcement immediately or after the current speech is completed. The NSAccessibilityLayoutChangedNotification is another example where priority can help VoiceOver determine if the UI change requires the VO cursor to go to the new UI.
NSAccessibilityAnnouncementKey
This key is used in the user info dictionary for notifications. The value is a localized string. This should generally be used in conjunction with the NSAccessibilityPriorityKey to help accessibility clients determine the important of this announcement.
NSAccessibilitySharedFocusElementsAttribute
Returns an array of elements that also have keyboard focus when a given element has keyboard focus. A common usage of this attribute is to report that both a search text field and a list of resulting suggestions share keyboard focus because keyboard events can be handled by either UI element. In this example, the text field would be the first responder and it would report the list of suggestions as an element in the array returned for NSAccessibilitySharedFocusElementsAttribute.
NSAppearance
NSAppearance is a new class in Mac OS X 10.9. It can be used to access alternate appearances of standard system windows and views. You obtain an NSAppearance by name using +[NSAppearance appearanceNamed:]. A new appearance for controls that is appropriate for light backgrounds (such as popovers) is accessible via the name NSAppearanceNameLightContent. To access the default system NSAppearance, use the name NSAppearanceNameAqua.NSAppearanceCustomization is a new protocol that NSView and NSWindow adopt that allows customization on those objects. To customize the appearance of a window, call setAppearance: on the window with your NSAppearance object. That will cause the window itself to take on the customizations in that NSAppearance, as well as any view in that window. To customize just a view, call setAppearance: on that view. Any specific customization not found in a view's appearance will fall back up to that view's superview, ultimately trying the window's appearance. If a customization is not found at the window level, the default (Aqua) appearance is used. Use the effectiveAppearance method (part of NSAppearanceCustomization) to access what NSAppearance object will be used when drawing that view or window; this takes into account appearances set on superviews and windows.
While drawing views, the currentAppearance is set for the drawing thread. To access the current appearance, use:
NSAppearance *currentAppearance = [NSAppearance currentAppearance];
Automatic Quote and Dash Substitution
Mac OS X 10.9 now contains global user preference settings for the use of automatic quote and dash substitution. Applications that provide for the entry of free-form text in which typographically correct quotation marks and other punctuation would be appropriate may follow these settings by using the new NSSpellChecker methods+ (BOOL)isAutomaticQuoteSubstitutionEnabled;and by listening to the following notifications to be notified of changes
+ (BOOL)isAutomaticDashSubstitutionEnabled;
NSString *NSSpellCheckerDidChangeAutomaticQuoteSubstitutionNotification;For applications compiled on 10.9 and later, NSTextViews by default will automatically follow these settings, unless setAutomaticQuoteSubstitutionEnabled: or setAutomaticDashSubstitutionEnabled: has been called.
NSString *NSSpellCheckerDidChangeAutomaticDashSubstitutionNotification;
NSTableView/NSOutlineView General Updates
Prior to 10.9, deselecting a row with a cmd-click would call tableView:selectionIndexesForProposedSelection: but the resulting selection may not have been used. This has been properly fixed for applications that link on 10.9 and higher, and the resulting selection returned by the delegate will be used.NSOutlineView now fully supports Right To Left language layout. This can be achieved in several ways. One can explicitly set the userInterfaceLayoutDirection in code or in a NIB. Or, if auto-localization is used and the app is linked on 10.9 (or higher), then the userInterfaceLayoutDirection will automatically be flipped if it differs from [NSApp userInterfaceLayoutDirection].
Prior to 10.9, calling moveRowAtIndex:toIndex: when the NSTableRowView being moved (or any subview of the NSTableRowView) was the first responder, would leave the table in a bad state. This has been fixed for all applications in 10.9. For applications that need to run on platforms prior to 10.9, it is recommended to first make the window the firstResponder before calling moveRowAtIndex:toIndex: (if the row being moved is the firstResponder).
Prior to 10.9 there was a visual glitch (specifically, a removed NSTableRowView) when performing a row delete right before a row insertion in the same beginUpdates/endUpdates block, but only when both animations where either NSTableViewAnimationSlideUp or NSTableViewAnimationSlideDown. This has been fixed for 10.9, and applications that need to target prior to 10.9 should ensure they do a different animation for the deletion, or perform the deletion in a separate beginUpdates/endUpdates block.
Using a View Based TableView with a rowSizeStyle of NSTableViewRowSizeStyleDefault will automatically update the rowSizeStyle of each cell view for tables with the NSTableViewHighlightStyleSourceList. However, prior to 10.9 the rowSize style for "header rows / group rows" would always be set to Medium and Large, when they should always be "small" regardless of the user setting in System Preferences. This has been fixed in 10.9 for all applications using a View Based TableView.
Using beginUpdates/endUpdates on a cell based tableview may potentially throw an exception "NSTableView Error: Insert/remove/move only works within a -beginUpdates/-endUpdates block." if the cell based table view is layer-backed. This has been fixed for all applications on 10.9 and higher. For prior applications, it is recommended to use a view based NSTableView when using beginUpdates/endUpdates, or to not layer-back the table view.
Calling [tableView addTableColumn:] after a call to [tableView moveColumn:toColumn:] may through an exception if done in the same call stack level; this has been fixed in 10.9 for all applications. For applications that need to work around this prior to 10.9, be sure to call addTableColumn: before calling moveColumn:toColumn:
Calling [outlineView removeItemsAtIndexes:inParent:withAnimation:] that results in removing an expanded item may have left the item retained by NSOutlineView for an indefinite amount of time. This has been fixed on 10.9. For applications that need to work around this prior to 10.9, call collapseItem: before removing the item.
Hiding and unhiding an NSTableColumn (via setHidden:) will cause the table to automatically resize other table columns to make room for the new table column (or take up slack for the one that was hidden). Previously, this would always attempt to resize columns based on the enclosingScrollView's visible width. On 10.9 this has been changed to prefer the actual width, in the case of a horizontally scrollable table view.
NSTableView has a user default to allow all the default animations it does to be slowed down: NSTableViewSlowMotion YES/NO, which can be set with 'defaults' or a command line parameter.
NSView - General Updates
NSView now exposes a property userInterfaceLayoutDirection, which is backwards available on 10.8 for NSView, and 10.7 for NSOutlineView. userInterfaceLayoutDirection defaults to [NSApp userInterfaceLayoutDirection]. It is up to a particular view's implementation to properly support Right To Left layout. NSOutlineView properly implements the Right To Left layout.NSView - Layer-backed Views
NSView has some new API to ease layer-backed adoption. The new property canDrawSubviewsIntoLayer allows a parent view to draw all of its subviews into a single layer, when that parent view is layer-backed. This is also sometimes referred to as an “inclusive layer”, as all the children views are drawn inclusively into a single parent layer. Normally, calling setWantsLayer:YES on a parent view will create individual layers for that parent view and all individual subviews. However, each individual subview will not have wantsLayer==YES, but will inherit its own unique layer by virtue of being in a layer-tree. When canDrawSubviewsIntoLayer is set to YES, the parent view will draw all subviews into a single layer, and each individual subview will not have an individual layer. The exception is a particular subview which already has wantsLayer set to YES on it. It is recommended to first call setCanDrawSubviewsIntoLayer:YES, and then call setWantsLayer:YES to avoid unnecessary work. The reason to use canDrawSubviewsIntoLayer=YES, is when there is a view hierarchy which can not be refactored to take advantage of the new Mountain Lion 10.8 API of -wantsUpdateLayer=YES and -updateLayer. Another reason to use this feature is to collapse multiple layers into a single layer, in order to gain better application performance. It is generally recommended to turn canDrawSubviewsIntoLayer on only for parent views which are opaque; otherwise text font-smoothing may not look correct. However, it is acceptable to set canDrawSubviewsIntoLayer to YES for non-opaque views if no text is drawn, or if text is known to draw into some opaque portion of the view (or subview). Note that turning on canDrawSubviewsIntoLayer requires the layerContentsRedrawPolicy to not be NSViewLayerContentsRedrawNever; otherwise subview invalidation will not work.Please note that calling setLayer: with a custom layer will implicitly cause the layerContentsRedrawPolicy to be set to NSViewLayerContentsRedrawNever. This side effect is so AppKit will have a "hands off" approach on custom layers assigned to a view.Please be aware that using an [NSAnimationContext beginGrouping / endGrouping] will cause a CATransaction to commit; this will cause layer-backed views to potentially get a -viewWillDraw (and a -drawRect: or -updateLayer) immediately after the [NSAnimationContext endGrouping] is called (or [CATransaction commit]).
Layer-backed NSButtons in 10.8 would previously not up-scale the image property, and would not properly use the alternate title when the state changed. Also, when a button was shown with no border (and just a title), the focus ring would not draw. NSButtonCell has been moved to properly use the NSView and NSCell focusRingMask* API. These bugs have been fixed for all applications on 10.9, and any applications providing (or hiding) the focus ring should use the focusRingMask* methods to provide (or customize) the focus ring.
In 10.8 MountainLion, layer-backed views would not invalidate the layer if the size was empty (0,0). This has been fixed for applications that link on 10.9 and higher.
Returning [NSNull null] from NSView's -animationForKey: or -defaultAnimationForKey: will now correctly be interpreted as not doing an animation. Previously, it would incorrectly cause the default CALayer animation to happen.
NSSplitView now overrides defaultAnimationForKey: and returns [NSNull null] for the "subviews" key. This suppresses the subviews animation, which is generally not desired and would be seen as a crossfade.
Layer backed views created by AppKit will now by default have the CALayer edgeAntialiasingMask property set to 0.
There is a new layerContentsRedrawPolicy of NSViewLayerContentsRedrawCrossfade. This can be used to do a crossfade of layer contents when the view's frame size changes. For some views, it also is applicable to views which have contents. For instance, one can easily cross fade the string of an NSTextField with:
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
context.allowsImplicitAnimation = YES;
textField.stringValue = @"Testing from the treehouse";
} completionHandler:nil];
Changes to layer rendering
Beginning in Mac OS X 10.9 CoreAnimation layer trees are rendered out-of-process. CoreImage filters attached to layers are not supported in this mode. If you are using CoreImage filters on a layer attached to NSView, please set the layerUsesCoreImageFilters property on your view to YES. When set, the layer tree hosting the NSView will be rendered in-process. Failure to set this property will result in an exception being thrown when filters are applied to a layer. Note that if you are setting filters using the NSView properties (backgroundFilters, compositingFilter, or contentsFilters) you do not need to set the layerUsesCoreImageFilters property.Changes to the lifetime of named NSImages
Prior to Mac OS 10.9 images loaded through +[NSImage imageNamed:] were retained for the lifetime of the application. For applications linked on 10.9 and later this is no longer the case. Images loaded through +[NSImage imageNamed:] will still be cached for a brief time.Changes to -[NSView allocateGState] and-[NSView releaseGState]
Beginning in Mac OS X 10.9 -[NSView allocateGState] and -[NSView releaseGState] are no ops. These methods were seldom used. Additionally, methods that relied on the side effects of these methods, specifically -[NSView gState] and -[NSWindow gState] will now always return 0. The method -[NSView renewGState] will continue to be invoked as it has in the past, when the view's location in the window has changed.Changes to the default blend mode for block-based NSImages and NSImageRep subclassers
Beginning in Mac OS X 10.9, NSImage will invoke -[NSImageRep draw] with the current compositing operation set to NSCompositeSourceOver. This matches the behavior of drawing in NSView. Prior to this, -[NSImageRep draw] would be invoked with either the compositing operation passed to NSImage itself, or NSCompositeCopy, depending on the circumstances.If you have subclassed NSImageRep, you may now depend on NSCompositeSourceOver being set when your -draw method is invoked. Likewise, if you are supplying a block to either +[NSImage imageWithSize:flipped:drawingHandler:] or -[NSCustomImageRep initWithSize: flipped:drawingHandler:] you may also depend on NSCompositeSourceOver being the current composite operation when your block is invoked.
Note that if you are deploying to previous OSes (or wish to use a non source-over composite operation) you should explicitly set the drawing operation. This can be done using the -[NSGraphicsContext setCompositeOperation:] API.
Fixes to layer:shouldInheritContentsScale:fromWindow
Prior to Mac OS X 10.9, returning YES from a layer delegate's layer:shouldInheritContentsScale:fromWindow: method would cause AppKit to update the contentsScale property of a CALayer, and then invoke setNeedsDisplay:YES on the NSView that contained that CALayer. In Mac OS X 10.9, the layer itself will be marked needing display. This fix is conditionalized against apps that have been linked on Mac OS X 10.9 or later.Reminder about deprecated NSOpenGL functionality
The following NSOpenGL pixel format creation options should be considered deprecated, and have been deprecated by the underlying OpenGL libraries for some time. Their effects should be considered undefined, and their use avoided.NSOpenGLPFAOffScreenThe OpenGL library option NSOpenGLGOResetLibrary should also be considered deprecated, and its use avoided.
NSOpenGLPFAFullScreen
NSOpenGLPFASingleRenderer
NSOpenGLPFAWindow
NSOpenGLPFACompliant
NSOpenGLPFAPixelBuffer
NSOpenGLPFARemotePixelBuffer
NSOpenGLPFARobust
NSOpenGLPFAMPSafe
NSOpenGLPFAMultiScreen
The following methods on NSOpenGLContext should also be considered deprecated. Note that support for these methods no longer exists for many hardware configurations, and their usage may result in crashes.
- (void)setFullScreen;When trying to create a full-screen context, use a fullscreen NSOpenGLView instead.
- (void)setOffScreen:(void *)baseaddr width:(GLsizei)width height:(GLsizei)height rowbytes:(GLint)rowbytes
- (void)copyAttributesFromContext:(NSOpenGLContext *)context withMask:(GLbitfield)mask
- (void)createTexture:(GLenum)target fromView:(NSView *)view internalFormat:(GLenum)format
To render offscreen, please see the documentation regarding OpenGL FrameBufferObjects (FBOs) and glReadPixels.
Finally note that the entirety of the NSOpenGLPixelBuffer class should be considered deprecated. Use IOSurface in conjunction with GL framebuffer objects as a replacement.
NSColor
NSColor now provides three new methods for easier reuse of code that uses UIColor on iOS:+ (NSColor *)colorWithWhite:(CGFloat)w alpha:(CGFloat)a;These create colors that are compatible with sRGB. However, where you have a choice, it's better to use the methods such as colorWithSRGBRed:green:blue:alpha: that specify the color space explicitly.
+ (NSColor *)colorWithRed:(CGFloat)r green:(CGFloat)g blue:(CGFloat)b alpha:(CGFloat)a;
+ (NSColor *)colorWithHue:(CGFloat)h saturation:(CGFloat)s brightness:(CGFloat)b alpha:(CGFloat)a;
NSTokenField
There is a UI behavior change introduced for NSTokenField in Mac OS X 10.9. Adjacent Tokens are not longer visually connected when selected. As a dragging source, NSTokenField now removes the tokens drag/dropped with NSDragOperationGeneric (moving the selection).NSTextField
Setting attributed placeholder string via -setPlaceholderAttributedString: renders the attributed string value while focused.Notes specific to OS X 10.8
Some of the major topics covered in this section include:- iCloud support for NSDocument
- Layer-backed drawing improvements
- Sharing sheet support
- New class NSPageController
- NSScrollView zooming and smart magnification
- NSImage improvements
- NSImage multi representation support for high resolution recap
- Full screen changes
- Use of NSSavePanel / NSOpenPanel when application sandboxed
- Auto layout changes
- Changes to Resume for applications
- New class NSTextAlternatives
NSDocument Support for iCloud
In 10.8, NSDocument-based applications with a ubiquity-container-identifiers entitlement gain new functionality and UI to facilitate iCloud document management.When iCloud is enabled and an application is first launched or re-activated and no windows are visible or being restored, instead of creating a new Untitled document, NSDocumentController will display a non-modal open panel showing the user's iCloud library. From there an application can either open an existing document from iCloud or the local file system, or create a new document. To support this new non-modal open panel, NSDocumentController has added -beginOpenPanelWithCompletionHandler: and -beginOpenPanel:forTypes:completionHandler:. Note that currently the former method will only run the open panel non-modally when iCloud is enabled.
If your application overrides -[NSDocumentController openDocument:] and invokes -URLsFromRunningOpenPanel, you should switch to calling -beginOpenPanelWithCompletionHandler: instead.
If your application overrides -[NSDocumentController runModalOpenPanel:forTypes:] to customize the open panel, including adding an accessory view, you should also override -beginOpenPanel:forTypes:completionHandler: and do the same customization there. For compatibility NSDocumentController will continue to invoke the former if it is overridden when the latter isn't. As a result, the panel will be run modally instead of non-modally, giving your users a suboptimal experience.
iCloud-enabled applications also gain UI to facilitate moving documents into the cloud via the move and save panels. There is also a shortcut in the document titlebar menu to quickly move the document to iCloud. This menu item uses a new IBAction method on NSDocument: -moveDocumentToUbiquityContainer:. Also, as mentioned in the section on Drafts, new documents can get autosaved directly to the application's iCloud container, depending on the user's preference.
NSDocument also provides a new conflict resolution UI that allows users to inspect the contents of each conflict and save each conflicting version as a separate file, if they wish.
Applications that do not wish to use these features for any or all of their NSDocument subclasses can override +[NSDocument usesUbiquitousStorage] and return NO. If all of the application's declared NSDocument subclasses return NO from this method, then NSDocumentController will never show the new non-modal open panel.
NSDocument Versions Preservation Optimization
In Mac OS 10.7, when NSDocument decided that its file deserved to be preserved as a version prior to saving it would synchronously copy the file to perform the preservation. For significantly large files this could cause long hangs that interrupt the user's workflow. In applications linked on or after 10.8, NSDocument can avoid this hang by leveraging the 'backup' file that gets created during safe-saving.When the current file's contents need to be turned into a version prior to saving, the file will get moved out of the way prior to the newly written file overwriting it. After the new file is successfully saved in the old file's place, a version can be created from the old file without requiring an expensive copy.
NSDocument uses -[NSDocument backupFileURL], a new method in 10.8, to control this behavior. This method only returns a URL when the current file needs to be preserved. When this method returns non-nil, -writeSafelyToURL:ofType:forSaveOperation:error: must ensure that the original contents of the file exist at that location prior to returning. Failing to do so is tantamount to data loss, since the user will be unable to revert to their previous versions. If your NSDocument subclass overrides -writeSafelyToURL:ofType:forSaveOperation:error: and doesn't call super, when you link against 10.8 you must ensure that your implementation obeys this rule. Alternatively, you can override -backupFileURL and return nil to disable this optimization. By doing so, -saveToURL:ofType:forSaveOperation:completionHandler: will fall back to the old synchronous copy technique. Applications that do saving in-place and don't use the safe-saving technique will almost always want to disable this optimization unless the cost of creating the 'backup' file is significantly less than a copy.
You should not override -backupFileURL to return a custom URL. Your override should either call super or return nil.
Sometimes versions are preserved after the new file is written, such as when the user chooses File > Save. In this scenario, the backup file optimization is not possible, so a copy is still necessary. However, for applications linked on 10.8, NSDocument will perform this copy on a background thread, leaving the main thread unblocked. To avoid corrupting these versions by modifying the file during this asynchronous copy, you should make sure that any direct access to your document's file is done with -performSynchronousFileAccessUsingBlock: or -performAsynchronousFileAccessUsingBlock:.
Debugging -performActivityWithSynchronousWaiting:usingBlock:, -performSynchronousFileAccessUsingBlock:, and -performAsynchronousFileAccessUsingBlock:
In Mac OS 10.7, NSDocument added these three APIs to ensure proper serialization of file access and user activity when using asynchronous saving. When an asynchronous activity or file access fails to properly invoke its completion handler, subsequent uses of these APIs wait forever. Unfortunately, due to the asynchronous nature of these APIs, the backtrace of these hangs usually doesn't provide enough information to debug the problem and identify the unterminated operation.In 10.8, NSDocument provides a tool to help you more easily debug these problems. If your application is hanging in -performActivityWithSynchronousWaiting:usingBlock: or -performSynchronousFileAccessUsingBlock:, break in the debugger and execute this command:
po _NSDocumentSerializationInfo()The output of this command will contain a listing of all unterminated invocations to these serialization APIs, including the document instance in question, the backtrace where the API was invoked, the type of serialization (file access vs. activity), and the current status (waiting for exclusive access vs. exclusive access is granted but block hasn't been executed yet vs. block is executing).
This function is intended for debug purposes only. It is not declared in any headers, and its name and output are subject to change at any time.
New and Updated NSDocument Operations
In 10.8, NSDocument has added some new document management operations and has enhanced others.Users can now move and rename documents from within the application by using the Move To… and Rename… commands from the File menu or the document titlebar menu. The Move To… command shows the user a simple panel that allows the user to move the document to a new directory. The Rename… command causes the title in the window titlebar to become editable, allowing the user to change the title of the document. The new APIs controlling these operations are -renameDocument:, -moveDocument:, -moveDocumentWithCompletionHandler:, and -moveToURL:completionHandler:.
The Duplicate command has also been enhanced. Immediately after the duplicate document is created, the window's title will become editable, allowing the user to customize the title. If the user specifies a custom title, then the document will be saved into the same directory as the original document, if possible.
Certain documents that the system decides should not be edited, such as attachments from the Mail application, will be automatically duplicated when the user edits them to avoid modifying the original. This works by immediately causing the document to be autosaved to the ~/Library/Autosave Information directory with NSAutosaveElsewhereOperation. The autosavedContentsFileURL is set to that location and the fileURL is set to nil, so the document starts behaving like an unnamed duplicate document.
There is new API to query and control the "locked" state of the document. -isLocked returns YES when NSDocument thinks its file cannot be edited for reasons including, but not limited to, the "user immutable" flag being set, insufficient permissions, and -checkAutosavingSafetyAndReturnError: returning NO. You can attempt to unlock the document with the -unlockDocumentWithCompletionHandler: and -unlockWithCompletionHandler: APIs. NSDocument will attempt to unlock the file by clearing the "user immutable" flag and restoring write permissions. If that is not sufficient to unlock the file, or the file cannot be unlocked, then the operation will fail. You can attempt to lock the document with the -lockDocumentWithCompletionHandler: and -lockWithCompletionHandler: APIs. NSDocument will attempt to lock the file by setting the "user immutable" flag. If the file cannot be locked, then the operation will fail. There are two separate IBAction methods for unlocking and locking: -unlockDocument: and -lockDocument:. If you provide custom UI for these menu items, at validation time you should use -isLocked to decide which action should be used.
For applications adopting +autosavesInPlace, the Revert command has been broken up into multiple menu items that allow reverting to key versions without requiring the user to enter the Versions browser. These menu items continue to use the -revertDocumentToSaved: action and use the menu item's tag to identify which item the user selects. You should not attempt to modify these menu items in any way.
Finally, -browseDocumentVersions: is a new IBAction method on NSDocument that triggers its entry into the Versions browser.
NSDocument Drafts
In applications linked on or after 10.8, when a user edits an Untitled document in an iCloud-enabled application, NSDocument will automatically save it to the default ubiquity container as defined by [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil], unless the user has specifically disabled this. In this case, the document will be saved to ~/Library/Autosave Information as normal.Once saved to iCloud, these documents behave very much like regular saved documents—they have a proxy icon in the title bar, they can accumulate versions, and so forth. However, when the user chooses to Save or Close the document, a save panel will be presented to allow the user to confirm the automatically chosen name and location, assign a new one, or even delete the file. Documents in this state are called "drafts". If the user does something prior to Save or Close to indicate that they want the document to be kept around, the document will no longer be considered a draft and hence will not show the save panel on Save or Close. Moving, renaming, or locking the file or editing it in another application are some examples of what can cause a document to stop being a draft. You can get and set the draft status of a document with the new -[NSDocument isDraft] and -[NSDocument setDraft:] APIs.
When a newly created document is saved as a draft, NSDocument invokes -setFileURL: without affecting the "Edited" status of the document, as shown in the titlebar. None of the NSSaveOperations in Mac OS 10.7 allow for this, so NSDocument in 10.8 defines a new save operation: NSAutosaveAsOperation. This new NSSaveOperation will be used by -autosaveWithImplicitCancellability:completionHandler: under specific circumstances. For example, a new unsaved document that has been changed with NSChangeDone will use NSAutosaveAsOperation instead of NSAutosaveElsewhereOperation. The URL that is used for the NSAutosaveAsOperation is determined internally by NSDocument and attempts to avoid name conflicts by appending a number to the document's display name that will be incremented until no conflict is found.
Some applications use -updateChangeCount: to cause NSDocument to autosave changes that don't originate directly from the user. For example, when importing a non-native document type, some applications create a new document with the imported contents and call -updateChangeCount: to ensure that the document gets autosaved with those contents. Many applications use NSChangeDone for this purpose. However, since the user did not explicitly cause this change, it is undesirable to turn this document into a draft. Applications should be careful to use the correct NSDocumentChangeType—in this case, NSChangeReadOtherContents—to prevent the conversion into a draft. Using NSChangeDiscardable will also prevent the creation of a draft.
When a document is scheduled to become a draft, the effective autosaving delay is greatly reduced to ensure that an autosave happens very soon after the first change. Once NSDocument has completed the NSAutosaveAsOperation, or if the operation has failed without any expectation of succeeding in the future, that NSDocument instance will no longer use NSAutosaveAsOperation on future autosaves. A document that has failed an NSAutosaveAsOperation will revert to behaving like a traditional Untitled document and autosaving will use NSAutosaveElsewhereOperation until the user gives the document a location via the save panel.
If one or more your application's NSDocument subclasses should not save untitled documents as drafts, you should override +[NSDocument autosavesDrafts] and return NO.
NSDocument Customizing Default Draft Names
In Mac OS 10.7, all newly created documents were called "Untitled" or "Untitled <number>" until the user gave them a title by saving. In 10.8, NSDocument will allow your application to replace "Untitled" with a custom string that is more descriptive for your application. For example, a word processing app may choose to automatically name documents "Résumé" when they’re created from a certain template, or a spreadsheet application may prefer new documents to be called "Spreadsheet" instead of "Untitled". You can do this in your application by overriding -[NSDocument defaultDraftName] and returning your custom localized string. If necessary, NSDocument will append a number to make the name unique.CoreAnimation updates for NSView (i.e.: CALayer backed NSView)
For applications linked on 10.8 and higher, the following changes are in effect:• NSTextField has been updated to allow LCD font smoothing to work when the view is layer-backed. Prior to 10.8, NSTextField would directly draw the text into the layer's contents; this would cause text to render incorrectly due to the LCD font smoothing algorithm not having adjacent pixels to smooth fonts with. Layer-backed applications that manually draw text should move to using NSTextField to get proper LCD font smoothing. One caveat is that NSTextField does require an ancestor which is layer-backed and opaque; it does not have to be the direct parent view, but some view in the ancestor chain must be opaque for this to be turned on and work correctly.
• For applications that link on 10.8, all layer-backed views may turn off LCD font smoothing to have text render better. Specifically, if a view returns NO from -isOpaque, then AppKit will turn off LCD-font smoothing before calling the view's drawRect: implementation. Otherwise, the drawing of text into a transparent layer will look incorrect (generally, it looks bolder and not smooth). However, there may be some specific cases where a layer-backed view knows it is manually drawing text into an opaque area. For these views, it is recommended that font smoothing be explicitly turned back on before drawing text. An example:
CGContextRef ctx = NSGraphicsContext.currentContext.graphicsPort;• Prior to 10.8, calling setLayer: on NSView may not have properly updated the layer's position until a resize or reposition of the view happened; this has been fixed.
CGContextSetShouldSmoothFonts(ctx, true);
[@"Tandem Unicycle" drawInRect: ... ];
• Prior to 10.8, an AppKit managed layer would be destroyed (and removed) when a view was removed from a window or moved to another parent view. This would have the undesired effect of losing any custom properties set on the layer. For any view that explicitly has setWantsLayer:YES, the underlying AppKit managed layer will not be destroyed, and custom set layer properties will not get lost.
• Prior to 10.8, when AppKit removed the layer it managed, it would leave the layer's delegate set to the NSView that had the layer. For applications linked on 10.8 and higher, the delegate will be set to nil when it is removed (only if it is "owned" by AppKit). An application must re-assign the delegate to the view, if they save off and restore an AppKit owned layer (in general, this isn't recommended, and to get a layer to stick around one should call setWantsLayer:YES).
• Prior to 10.8, an AppKit managed layer would always have the layerContentsRedrawPolicy set to NSViewLayerContentsRedrawDuringViewResize when the layer was (lazily) created by AppKit. This prevented the value from being set at an earlier time before the layer was created. In 10.8, this is now done at view initialization time (either in -init or -initWithCoder). For views that host custom layers (via a call to setLayer:), the layerContentsRedrawPolicy will still be set to NSViewLayerContentsRedrawNever when setLayer: is called; it is assumed that the client wants to own the layer and control when it redraws. For all other regular NSView backing layers, it is required to properly set the layerContentsRedrawPolicy (usually in initWithFrame: or initWithCoder:). Prior to 10.8, the layerContentsRedrawPolicy was encoded in the NIB along with other properties of the view, despite there being no way to set it in Interface Builder. On 10.8, the layerContentsRedrawPolicy is no longer encoded in the NIB and should be explicitly set in code.
• Prior to 10.8, a flipped view would explicitly control a layer's position by updating it every call to setFrameSize:. However, this conflicts with allowing CoreAnimation to do frame size animations on a background thread without input from AppKit. To solve this, AppKit now controls the -geometryFlipped on the backing CALayer. Prior to 10.8, the anchorPoint was also set to be either (0,0) or (0,1) -- it varied depending on the superview being isFlipped or not. On 10.8, the layer's frame is now equal to the view's frame. The anchorPoint is also always set to be (0,0), since the positions are now equal. In order to properly control the flipped state of a layer, override -isFlipped on NSView.
• There are now two ways to provide a layer's contents when a view is layer-backed. The new preferred way is to implement -wantsUpdateLayer and return YES. When this returns YES, a new method called -updateLayer will be called. At this point, it is appropriate to directly set the view.layer.contents to an image (NSImage or CGImageRef) which can properly stretch (i.e.: a 3 part or 9 part image). To indicate how it should stretch use the CALayer contentsCenter property. If additional UI is needed to represent a particular control, add subviews (or sublayers) in -layout. -layout will be called for views that respond YES to wantsUpdateLayer, even if auto layout is not used. To get a new call to -updateLayer, invalidate the view with a setNeedsDisplay:YES. To get a new call to -layout, call setNeedsLayout:YES. Utilizing wantsUpdateLayer can lead to better performance when a view is layer-backed. Most of the standard AppKit controls will implement this to return YES if drawing methods are not subclassed and overridden. If a view returns NO from -wantsUpdateLayer, then the layer's contents will be filled in with whatever is drawn in drawRect:.
• As of 10.7, the way layer-backed views animate frame changes varies depending on the value of layerContentsRedrawPolicy. Animations are typically initiated using the animator proxy (i.e.: [[view animator] setFrame:]). If the layerContentsRedrawPolicy is NSViewLayerContentsRedrawDuringViewResize, then AppKit assumes the view needs to redraw on each step of the animation, and it will drive the animation on the main thread by calling setFrame: multiple times. If the layerContentsRedrawPolicy is NSViewLayerContentsRedrawOnSetNeedsDisplay, then AppKit will utilize Core Animation to drive the animation, which is done implicitly on a background thread, and the view will not get a setFrame: call for each step of the animation. Instead, Core Animation will stretch the layer's contents based on how properties are set on the CALayer. Note that using NSViewLayerContentsRedrawOnSetNeedsDisplay makes the view work just like a CALayer would for the animation; this means any property changes (which animate) will instantly appear at their final state as soon as the animation call is made. In other words the following happens:
view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawPolicyOnSetNeedsDisplay; // This should be set in init
NSRect frame = view.frame; // Starts out as 100, 100
frame.size = NSMakeSize(300, 300);
[view.animator setFrame:frame]; // Animates the view frame from 100,100 to 300,300
// However, reading back in the view.frame at this point will indicate it is already at 300,300
• For 10.8 and higher, the default value for the layerContentsRedrawPolicy varies on a control-by-control basis inside of AppKit. For a plain NSView, the value will be NSViewLayerContentsRedrawOnSetNeedsDisplay. For a custom NSView that overrides drawRect:, the default value will be NSViewLayerContentsRedrawDuringViewResize. When using -wantsUpdateLayer, it is important to always set the layerContentsRedrawPolicy (ideally to NSViewLayerContentsRedrawOnSetNeedsDisplay).This will allow the view to animate frame changes with Core Animation, as opposed to AppKit driving the animation on the main thread.
• On 10.8, AppKit will control the following properties on a CALayer (both when "layer-hosted" or "layer-backed"): geometryFlipped, bounds, frame (implied), position, anchorPoint, transform, shadow*, hidden, filters, and compositingFilter. geometryFlipped is only changed for apps that link on 10.8 and higher. Use the appropriate NSView cover methods to change these properties.
• When a layer-backed view is invalidated with setNeedsDisplay: (or setNeedsDisplayInRect:) the window is no longer invalidated. This allows just the view that needs to redraw to be redrawn, and no others. However, applications need to take care that they didn't accidentally depend on side effect drawing that previously happened due to a parent view invalidating its children views.
• When a view had the layerContentsRedrawPolicy set to NSViewLayerContentsRedrawOnSetNeedsDisplay, the view (and layer) may still have accidentally been invalidated on frame changes. This has been fixed for applications linked against 10.8, and when NSViewLayerContentsRedrawOnSetNeedsDisplay is set, the application must explicitly call setNeedsDisplay: on frame changes (if required).
• NSAnimationContext now has a new property, allowsImplicitAnimation, for layer-backed views. The default value is NO, and it is automatically set to YES when the -animator proxy is used. When YES, it allows any CoreAnimation animatable property to be animated. This allows for nested animations. In other words, doing a [[view animator] setFrame:frame] will animate the frame on 'view', but also any subviews also. Typically this is desired, as it allows the autoresizing of subviews to be done animated, and all the animations to be tied together in the same animation block with the same duration. If a subview has some particular property they don't want to animate, a new animation group can easily be created to disable this behavior. The converse is also true; if you want a particular property to always have a CoreAnimation animation, then -allowsImplicitAnimation can explicitly be set to YES. An example use:
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {The use of allowsImplicitAnimation is very similar to the existing properties in UIKit on iOS: [UIView areAnimationsEnabled] and [UIView setAnimationsEnabled:]
context.allowsImplicitAnimations = YES; // Enable implicit CoreAnimation animations
// Any operations done in this block will be animated
} completionHandler:nil];
•NSView's -cacheDisplayInRect:toBitmapImageRep: will now work properly with layer-backed views. For applications linked against 10.8, the layer will be drawn using CALayer's renderInContext:, allowing it to render all sublayers, including ones that may not have an associated view. This allows the use of -wantsUpdateLayer and -updateLayer: to work with -cacheDisplayInRect:toBitmapImageRep:.
• When using layer-backed views, Core Animation will animate a layer from its presentation state to whatever state you set on the layer (or view). However, sometimes animating a layer-backed view may not animate from the desired location or state. This can happen if your view (and implicitly the layer) never had a chance to display. For instance, consider this example:
NSRect frame = view.frame; // Starts at 0,0 -- the view was already drawn on screen at this locationThe desired animation is from point 100,100 to 300,300 - however, what may happen is the view will animate from point 0,0 to 300,300! This is because changes to view and layer trees are coalesced inside of NSAnimationContexts/CATransactions. Because it is impossible to know the outermost scoping of an animation context / transaction, changes may be coalesced even when not expressly desired. To handle these cases, each set of changes/animations you want to be visible should be isolated in their own animation grouping. This will cause the view/layer to render with the requested property change. For instance, this will work to enforce the first rendering to happen before the animation starts by chaining the actual animation to the end of the first animation group:
frame.origin = NSMakePoint(100, 100);
view.frame = frame; // The view should move to 100,100 -- however, it has not drawn yet
frame.origin = NSMakePoint(300, 300);
[[view animator] setFrame:frame]; // The view should animate from point 100,100 to point 300,300
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
context.duration = 0;
NSRect frame = view.frame; // Starts at 0,0 -- the view was already drawn on screen at this location
frame.origin = NSMakePoint(100, 100);
view.frame = frame; // The view should move to 100,100 -- however, it has not drawn yet
} completionHandler:^ {
NSRect frame = view.frame; // Now it has drawn at 100,100
frame.origin = NSMakePoint(300, 300);
[[view animator] setFrame:frame]; // This will correctly animate from 100,100 to 300, 300 using the default animation context duration
}];
NSTableView/NSOutlineView
For a View Based NSTableView (or NSOutlineView), it has always been incorrect to acquire a view from the table inside the delegate method tableView:heightOfRow: (or outlineView:heightOfItem:). Now, the table will detect if either of these methods is called from inside the delegate method implementation and throw an exception (or log an error): viewAtColumn:row:makeIfNecessary: and rowViewAtRow:makeIfNecessary:.NSControl now has new API to show "expansion tooltips". This is primarily available for expansion tooltips to be shown when using a View Based NSTableView. Call setAllowsExpansionToolTips:YES to allow them to be shown.
NSTableView now has a method for registering (or associating) additional NIBs for a View Based NSTableView called -registerNib:forIdentifier:. When one calls -makeViewWithIdentifier:owner: the identifier, first the table will look in its reuse queue for a view to reuse with that same identifier. If one is not found, it will then look for registered NIBs with that identifier. If a NIB is found, it is instantiated with the 'owner' passed to the method. All top-level views in the instantiated NIB are then placed in the reuse queue with their current identifier. For this reason, it is important to always set the identifier for all top-level views in a NIB that is registered with the table. The particular view with the requested 'identifier' is then returned from -makeViewWithIdentifier:owner:. For a particular identifier, if there are no available views for reuse, or no associated/registered NIBs (or none created at design time in IB), then the method -makeViewWithIdentifier:owner: will return nil. A single NIB can be registered multiple times with different identifiers; this should be done for each top-level view that is in the NIB. A corresponding method, -registeredNibsByIdentifier, will return an NSDictionary with all registered NIBs. The key in the dictionary will be the identifier, and the value will be the NIB that has been registered. To unregister a NIB, pass 'nil' in for the NIB in -registerNib:forIdentifier:.
NSOutlineView
The following methods now support being animated via the -animator proxy: -expandItem:, -expandItem:expandChildren:, -collapseItem:, and -collapseItem:collapseChildren:. As an example, to animate the expansion of a particular item: [[outlineView animator] expandItem:item];Prior to 10.8, calling -reloadItem: (or -reloadItem:reloadChildren:) on a NSView Based NSOutlineView may not have reloaded the disclosure triangle button if the row changed from being expandable to not being expandable (or vice-versa).
NSButton / NSButtonCell
For applications linked on 10.8, NSButton and NSButtonCell now use the Lion API drawFocusRingMask and focusRingMaskBounds to do its focus ring drawing. Previously, calling setShowsFirstResponder:YES on NSButtonCell and manually drawing the cell would include the focus ring. On 10.8 this is no longer the case, the cell no longer explicitly draws the focus ring. To customize the focus ring, override NSButtonCell's implementation of drawFocusRingMaskWithFrame:inView:.An NSButton configured as a radio button (with the -buttonType set to NSRadioButton), will now operate in a radio button group for applications linked on 10.8 and later. To have the button work in a radio group, use the same -action for each NSButton instance, and have the same superview for each button. When these conditions are met, checking one button (by changing the -state to 1), will uncheck all other buttons (by setting their -state to 0).
NSInterfaceStyle
NSInterfaceStyle is now deprecated. The methods have not been used in AppKit in a while.All the "mnemonic" set of methods are now deprecated. IE, in NSButtonCell: setTitleWithMnemonic:, setAlternateTitleWithMnemonic:, setAlternateMnemonicLocation:, alternateMnemonicLocation, and alternateMnemonic. The methods that take a title will still call setTitle: with the ampersand stripped from the parameter. Under MacOS, these methods have historically not been used and did nothing.
Zooming ScrollView
NSScrollView now has built-in magnification support. You must opt-in by setting the allowsMagnification property to YES. When the user performs a pinch gesture, NSScrollView will magnify, centering around the mouse location. When the user performs a smart zoom gesture (2-finger double tap on trackpads), NSScrollView will attempt to intelligently magnify the content under the cursor. However, NSScrollView needs help from your document view to determine your content layout. See the Smart Zoom section below for more details./* Allow the user to magnify the scrollview. */
@property BOOL allowsMagnification;
/* This value determines how much the content is currently scaled. To animate the magnification, use the object's animator.
The default value is 1.0.
*/
@property CGFloat magnification;
/* This value determines how large the content can be magnified. It must be greater than or equal to the minimum magnification.
The default value is 4.0.
*/
@property CGFloat maxMagnification;
/* This value determines how small the content can be magnified. The default value is 0.25.
*/
@property CGFloat minMagnification;
/* Magnify content view proportionally such that the entire rect (in content view space) fits centered in the scroll view.
The resulting magnification value is clipped to the minMagnification and maxMagnification values. To animate the magnification,
use the object's animator.
*/
- (void)magnifyToFitRect:(NSRect)rect;
/* Scale the content view such that the passed in point (in content view space) remains at the same screen location once the scalingThe magnification property is observable. There are also notifications that will inform you when the magnification property is being changed due to user action. This may be due to the user performing a pinch gesture or a smart zoom gesture. When animating the magnification value yourself via the object's animator, these notifications are not sent.
is completed. The resulting magnification value is clipped to the minMagnification and maxMagnification values. To animate the
magnification, use the object's animator.
*/
- (void)setMagnification:(CGFloat)magnification centeredAtPoint:(NSPoint)point;
/* This notification is sent at the beginning of a magnify gesture. The notification object is the scroll view performing the magnification.
*/
NSString *NSScrollViewWillStartLiveMagnifyNotification;
/* This notification is sent at the end of magnify gesture. The notification object is the scroll view view performing the magnification.
*/
NSString *NSScrollViewDidEndLiveMagnifyNotification;
Smart Magnification
There is a new NSEvent type for the smart zoom gesture (2-finger double tap on trackpads) along with a corresponding NSResponder method. In response to this event, you should intelligently magnify the content.NSEventTypeSmartMagnify
- (void)smartMagnifyWithEvent:(NSEvent *)event;NSEventTypeSmartMagnify is limited to 64 bit only.
Better yet, let NSScrollView perform the intelligence for you. In your NSScrollView document view, implement the following method to provide NSScrollView with semantic layout information of your content.
/* Return the complete rect of the most appropriate content grouping at the specified location. For example, if your content
is divided into three columns, return the entire rect of the column that contains the location. NSScrollView will attempt
to magnify such that the smaller dimension fits inside the scroll view while remaining within the
minMagnification, maxMagnification range.
If your content layout is sub-divided further than one level deep (for example, two boxes that each contain multiple text
boxes), then use the visibleRect parameter to determine when to provide the rect of a sub-grouping. Always return a rect
for the appropriate grouping. If there is no deeper content grouping, return the rect for the deepest grouping. NSScrollView
will determine when to pan, magnify in, and magnify out.
Return NSZeroRect for the default behavior.
*/
- (NSRect)rectForSmartMagnificationAtPoint:(NSPoint)location inRect:(NSRect)visibleRect;
Quick Look Gesture
There is now API to receive the Quick Look gesture (also known as the dictionary look up gesture). The user can perform this gesture with a three finger single tap, or the appropriate keyboard shortcut (Cmd-Ctl-D by default). There are two new NSResponder methods: an event method and an action method./* Perform a Quick Look on the content at location in the event. If there are no Quick Look items at the location, call super.
Also, see quickLookPreviewItems: further below.
*/
- (void)quickLookWithEvent:(NSEvent *)event;
/* Perform a Quick Look on the text cursor position, selection, or whatever is appropriate for your view. If there are no Quick LookTo support the new event responder method, there is also a new NSEventType, NSEventTypeQuickLook. The only valid properties of and NSEventTypeQuickLook event are: -locationInWindow and -modifiers. A quick look event does not come in through the normal event mechanism, therefore there is no corresponding event mask for it, nor should you attempt to look for it in sendEvent: or with nextEventMatchingMask:… methods.
items, then call [[self nextResponder] tryToPerform:_cmd with:sender]; to pass the request up the responder chain. Eventually
AppKit will attempt to perform a dictionary look up. Also see quickLookWithEvent: above.
*/
- (void)quickLookPreviewItems:(id)sender;
NOTE: NSApplication's default implementation of quickLookPreviewItems:, will perform a dictionary lookup. NOTE: There is a bug that causes the quickLoop responder methods to jump from NSWindow directly to NSApp bypassing NSWindowController, NSDocument, and NSAppDelegate.
NSPageController
NSPageController is a new class in 10.8 used for controlling swipe navigation and animations between views or view content. It is useful for user interfaces which control multiple pages as in a book or a web browser history. NSPageController has a delegate that can be used to manage a predefined array of content via NSViewControllers, or used to manage a history of content snapshots built up due to user navigation.NSPageController inherits from NSViewController. You must assign the view property to a view in your view hierarchy. NSPageController does not vend a view. NSPageController does insert itself into the responder chain.
Conceptually, NSPageController manages swiping between an array of pages (arrangedObjects). Using the selectedIndex, you can determine how many pages forward or backward the user may navigate.
There are two modes that NSPageController may operate in, History and Custom. The main difference between the two modes is that History mode expects pageController.view to be the content and Custom mode expects pageController.view to be be a container for the content that you will supply by returning viewControllers in your delegate methods.
NSPageController History Mode aka Non View Controller Mode
History mode is designed to be the easiest way to create a history UI. NSPageController will manage the history (arrangedObjects), snapshots and user navigation between pages in the history.As the user navigates to new content, add to the history by calling navigateForwardToObject:. NSPageController will remove any arrangedObjects after the selectedIndex and then add object to the end of the arrangedObjects (and update the selectedIndex). Just like navigating in a new direction in a web browser, all forward history is lost once the user starts navigating a new path. After returning from navigateForwardToObject: you are free to update the contents of pageController.view.
During swiping, the following optional delegate methods are called in the following order:
- (void)pageControllerWillStartLiveTransition:(NSPageController *)pageController;This is a good time to stash information that you need to restore, such a scroll positions. After returning from the above delegate method, pageController.view is hidden. In its place NSPageController shows a private view hierarchy to animate previously taken snapshots of the page history. This allows NSPageController to remain responsive to the user without any work on your part.
- (void)pageController:(NSPageController *)pageController didTransitionToObject:(id)object;Called after a physically successful swipe, but before the animation has completed. The supplied object is the page the user navigated to -- the new selectedIndex object in arrangedObjects. If you need to start some background loading tasks, now is the time to do it, however, do not block the main thread or the animation will stutter or pause.
- (void)pageControllerDidEndLiveTransition:(NSPageController *)pageController;The swipe and all animations are completed. Restore any scrolls setting or anything else you need to do. The pageController.view is still hidden at this point and you must call -completeTransition on NSPageController to tell NSPageController to hide the private transition view and show pageController.view. Often you do this immediately, however, if your content is not ready you can call this at a later date. See "Completing the Transition" below.
NSPageController Custom Mode aka View Controller Mode
Custom mode is designed to give you more control over the swiping process and to facilitate more UI designs than just history. You can even use custom mode to create a history UI. In this mode, pageController.view is a container view and the content views are vended by viewControllers supplied by your delegate. (Tip: Draw your background in this view.) To enable custom mode, you must implement the following two methods in your delegate:- (NSString *)pageController:(NSPageController *)pageController identifierForObject:(id)object;NSPageController caches the view controllers supplied for each identifier and only asks its delegate to create more if one does not already exists in its cache (this is very similar to how view based NSTableViews work.) If you have different types of views you want to swipe in, then supply a different identifier for each type.
- (NSViewController *)pageController:(NSPageController *)pageController viewControllerForIdentifier:(NSString *)identifier;
When needed, you will be asked to prepare a viewController with a page via the following optional delegate method. If you do not implement this method, then the representedObject of viewController is set to object.
- (void)pageController:(NSPageController *)pageController prepareViewController:(NSViewController *)viewController withObject:(id)object;You will be asked to prepare a viewController with a nil object for each unique identifier it encounters. NSPageController will use this to generate a default snapshot for that identifier.
Generally, when using custom mode, the set of pages are known and it is your responsibility to set the arrangedObjects and initial selectedIndex.
During swiping, the following optional delegate methods are called int the following order:
- (void)pageControllerWillStartLiveTransition:(NSPageController *)pageController;This is a good time to stash information that you need to restore, such a scroll positions. After returning from the above delegate method, NSPageController takes a snapshot of the selectedViewController.view and then removes it from pageController.view. NSPageController replaces it with a private view hierarchy to animate previously taken snapshots. Unlike when building up a history, snapshots may not yet exist for the page being navigated to. In this case, a previously gathered default snapshot is used for that page's identifier. Regardless if using a default snapshot, or a previously gathered snapshot of actual contents, a viewController is prepared for the page being navigated to. This viewController.view is then asked to draw on a background thread while swiping continues (note, the view is currently windowless). Once the background threaded drawing completes, the initial snapshot is replaced with the newly generated snapshot.
- (void)pageController:(NSPageController *)pageController didTransitionToObject:(id)object;Called after a physically successful swipe, but before the animation has completed. The supplied object is the page the user navigated to -- the new selectedIndex object in arrangedObjects. Important, the pageController.selectedViewController has not been updated yet. In custom mode, this delegate method isn't all that interesting, however, If you need to start some background loading tasks, now is the time to do it. Do not block the main thread or the animation will stutter or pause.
- (void)pageControllerDidEndLiveTransition:(NSPageController *)pageController;The swipe and all swipe animations are completed. The selectedViewController.view is still detached at this point and you must call -completeTransition on NSPageController to tell NSPageController to hide the private transition view and update the selectedViewController. Often you do this immediately, however, if your content is not ready you can call this at a later date. See "Completing the Transition" below.
NSPageController Completing the Transition
As discussed above, NSPageController uses a private view hierarchy during swiping. To create a seamless transition to the new content, it is your responsibility to inform NSPageController when you are ready to draw the new content. Ideally, the new content should match the snapshot and the user is none the wiser. You tell NSPageController to complete the transition by calling -completeTransition. If needed, a viewController is prepared and then the contentView is shown (or added) to the view hierarchy and the private transition view is hidden.During NSPageController initiated animations, -pageControllerWillStartLiveTransition and -pageControllerDidEndLiveTransition are called on the delegate. Generally during pageControllerDidEndLiveTransition you will call -completeTransition. Programatic animations via the animator proxy do not call the delegate methods and you are responsible for calling -completeTransition when the animation completes.This is easily done via a completion handler on an NSAnimationContext grouping. (see below)
To instantly change the selectedIndex:
pageController.selectedIndex = newIndex;To animate a selectedIndex change:
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
[[pageController animator] setSelectedIndex:newIndex];
} completionHandler:^{
[pageController completeTransition];
}];
NSPageController Transition Styles
There are three transition animation styles. These transition styles are independent of the delegate mode. It is perfectly reasonable to create a history style UI using the viewController (custom mode) delegate methods. Simply set the transition style appropriately.NSPageControllerTransitionStyleStackHistory - Pages are stacked on top of each other. Pages animate out to the right to reveal the previous page. Next pages animate in from the right. (See Safari as an example)
NSPageControllerTransitionStyleStackBook - Pages are stacked on top of each other. Pages animate out to the left to reveal the next page. Previous pages animate in from the left. (See Preview as an example)
NSPageControllerTransitionStyleHorizontalStrip - Pages are laid out next to each other in one long horizontal strip
The best way to think about the two stack transition styles is to think about the way the pages are stacked on top of each other. In a Book, page 1 is first. Page 2 and subsequent pages are stacked underneath page one. When navigating forward, the user swipes the current page out to reveal the next page. When navigating backward, the user swipes the previous page in and on top of the current page. In History, the pages are stacked over time in reverse order. That is, page 2 is stacked on top of page 1 and page 3 is stacked on top of page 2, etc… When navigating backward, the user swipes the current page out to reveal the previous page. When navigating forward, the user swipes the next page in and on top of the current page.
NSPageController Layer Backed Mode
When using the Custom mode, if pageController.view is layer backed, live layers are used during transition instead of snapshots.NSPageController Notes
NSTableView is not thread safe! Do not tell NSTableView to reload its data while it is drawing on a second thread or it will crash. When using history mode, do not wire pageController.view to an NSSplitView subview. This will actually cause a 3rd split when the private transition view is put in the view hierarchy. You can work around this by using an empty NSView as the NSSplitView subview and placing the real contents in a subview of the empty view.Smooth Scrolling
Smooth scrolling has been removed from user preferences. A consequence of this is that the default name has changed. Any use of @"AppleScrollAnimationEnabled" should be replaced with @"NSScrollAnimationEnabled". If you need to determine the default state of smooth scrolling, the following code will return the correct BOOL value[[NSUserDefaults standardUserDefaults] boolForKey:@"NSScrollAnimationEnabled"];
Accelerated Scrolling
Similar to iOS, on the third consecutive fast scroll gesture, the scrollingDelta of the scrollWheel event is automatically accelerated until either the user takes too long between physical scrollWheel gestures or the user starts to scroll slowly during another accelerated scrollWheel gesture.Changes in Loading Nib Files
To support new features in localization, some nib loading paths in AppKit no longer use the deprecated loadNibFile:externalNameTable:withZone: method. If your application uses certain alternative localization techniques that depend on this method being called, and is linked with the Mountain Lion SDK or later, then localized resources may not be loaded correctly. You should switch to the new localization features that are part of Mountain Lion.Auto Layout View Priorities
In 10.8, the NSView methods setContentHuggingPriority:forOrientation: and setContentCompressionResistancePriority:forOrientation: now raise an exception if the priority is zero or less, or more than NSLayoutPriorityRequired. In 10.7 and earlier, these values were silently accepted but could’ve later led to a crash.Auto Layout NSSplitView improvements
In 10.8, NSSplitView properly respects constraints applied to its subviews, such as their minimum view widths. There are also new APIs for controlling the holding priorities, which determine both the NSLayoutPriority at which a split view holds its sizes and also which views change size if the split view itself grows or shrinks.- (NSLayoutPriority)holdingPriorityForSubviewAtIndex:(NSInteger)subviewIndex;In order to take advantage of these improvements, you must NOT implement any of the following NSSplitViewDelegate methods:
- (void)setHoldingPriority:(NSLayoutPriority)priority forSubviewAtIndex:(NSInteger)subviewIndex;
splitView:constrainMinCoordinate:ofSubviewAt:These methods are incompatible with auto layout. You can typically achieve their effects and more with auto layout.
splitView:constrainMaxCoordinate:ofSubviewAt:
splitView:resizeSubviewsWithOldSize:
splitView:shouldAdjustSizeOfSubview:
Auto Layout NSTextField wrapping improvements
In 10.8, NSTextField has some new APIs to allow better support for wrapping text fields under auto layout.- (void)setPreferredMaxLayoutWidth:(CGFloat)width;When you ask a wrapping text field for its size, by default it lays out as if it has infinite width available, i.e. in one long line (assuming it has no line break characters). Its intrinsicContentSize is the same: wide and short. However, if you set a preferred maximum layout width, the text field then measures itself as if it were confined to a rect of that width (and infinite height). Under auto layout, assuming no other constraints determine its size, its width will not exceed its preferred max, and its height will be sufficient to show all the text contents.
- (CGFloat)preferredMaxLayoutWidth;
A common pattern is to have a wrapping NSTextField whose width is some constant determined by other constraints, and whose height should be sufficient to show the full text at that width. That can be achieved with the following pattern:
[[textField window] layoutIfNeeded];This works as long as the text field view width is not dynamic.
[textField setPreferredMaxLayoutWidth:[textField alignmentRectForFrame:[textField frame]].width];
Auto Layout NSControl alignment rects
In Lion, the alignment rects, baselines, and intrinsic content sizes reported by many controls were slightly off. In 10.8, these have been corrected. In particular, NSPopUpButton has been modified to report an intrinsicContentSize with significantly less extra padding.AppKit has historically resisted changing a control's reported cellSize for binary compatibility reasons, even if the underlying artwork changes. The intrinsicContentSize is intended to always be correct, and can be used as a "better sizeToFit" even when not using auto layout, like so:
NSRect alignmentFrame = (NSRect){NSZeroPoint, [control intrinsicContentSize]};[control setFrame:[control frameForAlignmentRect:alignmentFrame]];
Be aware that this is only sensible for controls that have both a natural width and height, like NSButtons. Controls that are fully flexible (such as NSSlider) will report NSViewNoIntrinsicMetric for their width and/or height, and you should set those dimensions explicitly.
Auto Layout and Non-Finite Frames
Under auto layout in 10.7, if a view returns YES from translatesAutoresizingMaskToConstraints and that view has a non-finite frame size or origin (such as infinity or NaN), an exception would be raised when creating the corresponding constraints. When not using auto layout, the frame has historically been silently tolerated (though the view would not appear). In 10.8, auto layout now logs a warning and flushes all non-finite frames to zero, for better compatibility with the non-auto layout behavior.Of course, all non-finite view frames are bugs. To assist in tracking these down, a new user default NSViewRaiseOnInvalidFrames is provided. If this is set to YES, then both setFrameSize: and setFrameOrigin: (and by extension, setFrameSize:) will raise an exception if their parameters are non-finite.
NSSharingService
NSSharingService is a new class in 10.8 that can be used to share items to different kinds of local and remote services. Items are objects which respond to the NSPasteboardWriting protocol, like NSURL, NSImage or NSString. Please refer to the interface in <AppKit/NSSharingService.h> for the API.If an NSURL is a file URL (pointing to a video for example), then the content of the file will be shared. If the URL is remote, then the URL itself will be shared.
Sharing service integration for NSTextView
The default NSTextView contextual menu contains sharing service items relevant for the current selection.In addition to the contextual menu support, -[NSTextView orderFrontSharingServicePicker:] action method provides support for the sharing service picker facility provided by NSSharingService. A new NSTextViewDelegate method, -textView:willShowSharingServicePicker:forItems:, can be implemented by the delegate to further customize the picker behavior using NSSharingServicePicker.
ARC Weak References
Starting in 10.8, instances of NSWindow, NSWindowController, and NSViewController can be pointed to by ARC weak references.NSApplication
If an application is launched because a user selected a notification in the Notification Center, NSApplicationLaunchUserNotificationKey will be present in the userInfo dictionary of NSApplicationDidFinishLaunchingNotification. Its value is an NSUserNotification object.- (void)applicationDidFinishLaunching:(NSNotification *)notification {NSApplicationLaunchUserNotificationKey has replaced NSApplicationRemoteNotificationKey, which was introduced in Lion but is deprecated in Mountain Lion.
NSUserNotification *launchNotification = [[notification userInfo]
objectForKey:NSApplicationLaunchUserNotificationKey];
if (launchNotification) {
// application was launched by a user selection from Notification Center
}
}
Full Screen
We now allow a fullscreen window to occupy an external display. This allows users to specify which display should contain the fullscreen window by first moving the non-fullscreen window to the desired display. When a fullscreen window is on an external display, other displays are covered in linen shield windows, as before. The menu bar can be shown by mousing up to the top of the external display. The Dock is autohidden, and remains on whatever display edge is specified in user preferences. If your application implements a custom fullscreen transition, using window:startCustomAnimationToEnterFullScreenWithDuration:, you should choose the destination screen for the fullscreen window by finding the screen containing the majority of the window prior to the transition.Much of the underlying Spaces architecture has changed in 10.8. This should have no effect on your application if you are using the NSWorkspace and NSWindow API, but you may notice changes if you have assumptions about timing of when the current space changes during a fullscreen transition, for example.
Using NSSavePanel / NSOpenPanel API in an App Sandbox
When you enable app sandboxing for your application (by checking "Enable App Sandboxing" for a target in Xcode), any calls to NSSavePanel and NSOpenPanel objects are redirected to a separate process which displays the Save/Open panel. This process is pboxd (aka "Powerbox").Sandboxed NSSavePanel and NSOpenPanels were introduced in Lion. There are no new API changes in Mountain Lion, however the internals were worked on extensively to add support for iCloud and to resolve various usage and performance issues. The notes here are tips for developer usage.
A Note on Deprecated APIs:
Subtle aspects of deprecated methods in NSSavePanel and NSOpenPanel will not work when your app is sandboxed. Replacement methods have been available for a while, Changing your code to use the supported APIs will not prevent you from disabling the app sandboxing in the future. Now would be a good time to heed the warnings the compiler has been giving you.
Class Hierarchy:
As described in : The App Sandbox Design Guide
The declared (compile-time) class hierarchy for NSOpenPanel and NSSavePanel appears the same for sandboxed and non-sandboxed applications, but the actual (run-time) class hierarchy changes. Early versions of the App Sandbox Design Guide documentation were incorrect. Here is a (correct) recap:
The declared hierarchy looks like this:
NSOpenPanel : NSSavePanel : NSPanel : NSWindow : NSResponder : NSObjectThe actual (run-time) hierarchy looks like this:
NSOpenPanel : NSSavePanel : NSObjectif you or anyone to whom you pass an instance of an NSOpenPanel or NSSavePanel assumes that the methods of NSPanel, NSWindow, or NSResponder are available through that instance — your app may encounter a method-not-found exception.
Availability of NSWindow APIs on an instance of NSOpenPanel and NSSavePanel:
In Mountain Lion, in a sandboxed app, you can treat an instance of NSSavePanel or NSOpenPanel as if it were subclassed from NSPanel during three well-defined spans of time: Delegate method calls, panel completion blocks, and accessory view action method calls.
Restricted File Access affects Delegate Method support:
As described in the "Powerbox and File System Access Outside of Your Container" section of the The App Sandbox Design Guide, a sandboxed app has restricted access to the file system unless and until the user specifies otherwise on a file-by-file basis, or your application has extra entitlements to files. The NSOpenPanel and NSSavePanel delegate methods behave the same under App Sandboxing, however what you can do with the URLs supplied by those methods has changed. The URLs passed to these delegate methods cannot be used to obtain information about the objects to which the URLs refer. This means not only that file contents are inaccessible but also object meta-data is inaccessible. In other words, about all you can do with these URLs is analyze them as (standardized, structured) strings.
NSOpenPanel and NSSavePanel are not intended for use within a Sandboxed Application's container directory. The Application container directory is an abstraction which an end user is unlikely to be able to find with Finder. Therefore an Application container directory should not be presented to an end user in the open and save panels either. If your application attempts to set the initial directory of an open or save panel to the Application container directory (or a sub-directory therein), the code in pboxd detects this and redirects the panel to the user's home directory.
Titlebar Renaming
There is no public API for Titlebar Renaming outside of NSDocument. You may note that even if your application is not app sandboxed, an instance of pboxd (aka "Powerbox") is running while a Titlebar Renaming session is in progress. Titlebar Renaming uses pboxd even in non-sandboxed applications.Changes to Resume for Applications
In order to improve system startup performance, applications which were hidden at time of logout will by default only be partially launched when resuming after a reboot. Such partially relaunched applications use much less system resources. When the user activates an application launched this way, it will be fully restored and behave normally.If this behavior is undesirable for your application (for example, a mail application may be checking for mail and displaying a count of unread messages in its dock icon even if the user isn't interacting with it), you can use the Automatic Termination APIs to provide information to the system about what your app is doing, allowing it to make more appropriate choices. If an automatic-termination-aware application disables automatic termination via the -[NSProcessInfo disableAutomaticTermination:] API, the OS will assume that it's in the middle of something that should be resumed, and will fully relaunch it on restart. Once automatic termination is reenabled via -[NSProcessInfo enableAutomaticTermination:], the OS will resume deciding between full, partial, or no relaunch to minimize startup time without violating the user's expectations.
Adopt Automatic Termination
Automatic Termination is a facility introduced in OS X Lion. It allows underlying processes to be terminated by the system automatically, independent of the running state of the application itself. We intend to rely on Automatic Termination even more in the future, and encourage you to adopt it in your applications. You can do this by opt'ing into Automatic Termination via the Info.plist entry NSSupportsAutomaticTermination (or calling -[NSProcessInfo setAutomaticTerminationSupportEnabled:YES] early on), and then calling -[NSProcessInfo disableAutomaticTermination:] and -[NSProcessInfo enableAutomaticTermination:] as needed.NSTextAlternatives
NSTextAlternatives is a new immutable value class that stores a list of alternatives for a piece of text and communicates selection of an alternative via a notification. NSTextAlternatives instances are attached to attributed strings as the value of a new attribute, NSTextAlternativesAttributeName.In 10.8 the use case for NSTextAlternatives is dictation, but this class is not strictly tied to dictation and may be used for other purposes in future releases.
The class has two attributes: primaryString (representing the text that was actually chosen and used in the input string), and alternativeStrings (representing the alternative possible interpretations that the user might select instead). These are separate because the text system needs to deal with them separately, using the primaryString to make sure that the text is still in the same state as when it was entered, and presenting the alternativeStrings via UI similar to that currently used for correction alternatives.
When the user picks one of the alternative strings, the text view calls -noteSelectedAlternativeString: on the NSTextAlternatives object. Subclasses of NSTextAlternatives may do what they like in this method, but the base class implementation sends a notification NSTextAlternativesSelectedAlternativeStringNotification, with the selected alternative string in the user info under the key @"NSAlternativeString". This way arbitrary objects can listen for user selections of alternative strings.
Additional API for Spellchecking
There is an additional method on NSSpellChecker:- (NSString *)languageForWordRange:(NSRange)rangeClients who have an NSOrthography from NSTextCheckingTypeOrthography checking and wish to determine a specific language from it for a particular word, for example to pass in to -guessesForWordRange:inString:language:inSpellDocumentWithTag:, -correctionForWordRange:inString:language:inSpellDocumentWithTag:, or -completionsForPartialWordRange:inString:language:inSpellDocumentWithTag:, should use this method to obtain it. This method is publicly documented in 10.8 but is available back to 10.7.
inString:(NSString *)string
orthography:(NSOrthography *)orthography;
NSImage
In 10.8 we've added API to NSImage and NSCustomImageRep allowing clients to delegate drawing to a block:+[NSImage imageWithSize:flipped:drawingHandler:]These APIs make it easy to create an image (or custom image rep) that delegates its drawing to a block. This block is invoked at draw time, allowing drawing to be tweaked as appropriate for the destination context pixel density, color space, and other properties. This API can replace many (but not all) existing uses of lockFocus/unlockFocus. The existing lockFocus implementation performs all drawing against a single bitmap representation. This causes problems when images are moved between displays of different pixel densities (and to a lesser extent, different colorspaces).
-[NSCustomImageRep initWithSize:flipped:drawingHandler:]
-[NSCustomImageRep drawingHandler]
Like other non-bitmap image rep types, drawing is cached as appropriate for the destination context. Practically speaking, the drawingHandler block will be invoked the first time the image is drawn to a particular type of destination (1x or 2x screen, for example). Subsequent drawing operations to the same type of destination will reuse the previously generated bitmap.
When drawing, NSImage has always tried to use a representation with at least as many pixels as the destination rectangle. Many apps try to implement banners and 3 part / 9 part images by stretching an NSImage over a much larger area (usually only on a single axis). With the addition of 2x assets these apps are finding this policy displays the 2x image rep when they would prefer the 1x rep. This behavior can be changed by using the new matchesOnlyOnBestFittingAxis property on NSImage. It is still preferable to use NSDrawThreePartImage and NSDrawNinePartImage, when possible.
Since SnowLeopard it has been possible to use NSImage as the contents of a CALayer. With the recent proliferation of 2x assets, this technique is increasingly being used to simplify HIDPI adoption. This technique does however face certain limitations:
• The contentsGravity of the layer must be kCAGravityResize, kCAGravityResizeAspect, or kCAGravityResizeAspectFill. For all other contentsGravities behavior is undefined.
• When rasterizing a resolution independent representation, or selecting from multiple representations, only the backingScaleFactor of the view hosting the layer is used. This means that any scales introduced by modifying the bounds or transform of the layer or its ancestors are ignored.
In 10.8, NSImage now offers two new methods to more explicitly deal with a layer's contentsScale. These methods are -recommendedLayerContentsScale: and layerContentsForContentsScale:. These methods can be used to account for scales present in the layer tree (for example due to the layer bounds, or a layer transform). They also work for all contentsGravities.
static void updateLayerWithImageInWindow(NSImage *image, CALayer *layer, NSWindow *window)
{
CGFloat baseScaleFactor = [window backingScaleFactor]; // This is the scale factor of the screen we are displayed on.
NSSize imageSize = [image size];
CGRect layerSize = [layer bounds].size;
CGFloat additionalLayerScaleFactor = fmax(layerSize.width / imageSize.width, layerSize.height / imageSize.height);
CGFloat desiredScaleFactor = baseScaleFactor * additionalLayerScaleFactor; // this scale factor is appropriate
// for the actual pixel bounds of the layer.
CGFloat actualScaleFactor = [image recommendedLayerContentsScale: desiredScaleFactor]; // Don't use a higher scale
// factor if the image can't provide it. If the image is resolution independent
// the return value will be the same as the input.
id layerContents = [image layerContentsForContentsScale:actualScaleFactor]; // Now we have an appropriately
// sized object to use as the contents of a layer.
[layer setContents:layerContents];
[layer setContentsScale:actualScaleFactor];
}
NSWindow
As of 10.7.3, AppKit sends NSWindowDidChangeBackingPropertiesNotification when a window's backingScaleFactor and/or colorSpace changes. When running on a system version where this new notification is available, applications should use it instead of NSWindowDidChangeScreenProfileNotification to watch for changes to either of these backing store properties. There is also a corresponding delegate method.NSString *NSWindowDidChangeBackingPropertiesNotification;The userInfo dictionary contains two keys: NSBackingPropertyOldScaleFactorKey has an NSNumber value that specifies the window's previous backingScaleFactor, and NSBackingPropertyOldColorSpaceKey has an NSColorSpace value that specifies the window's previous colorSpace . You can compare these with the backingScaleFactor and colorSpace at the time of the notification, to determine which of these two properties changed. Note it is possible for both to have changed.
@protocol NSWindowDelegate <NSObject>
@optional
...
- (void)windowDidChangeBackingProperties:(NSNotification *)notification;
...
@end
NSString *NSBackingPropertyOldScaleFactorKey;
NSString *NSBackingPropertyOldColorSpaceKey;
NSView
NSView has a new override point: -viewDidChangeBackingProperties. This method provides a simple override point from which a client may apply changes to their layer hierarchy. It will be invoked when the view is added to a window, or when that window changes colorspace or backing scale factor. This method may be overridden to apply the current contentsScale to their layer tree.Additionally, we have introduced an optional method for CALayer delegates:
- (BOOL)layer:(CALayer *)layer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow *)window;When implemented, NSView will invoke this method on a layer's delegate when the view is added to the window, or the window's resolution changes. NSView does this by enumerating the layer tree attached to it and inspecting each layer delegate in turn. Note that when a layer is added to an already visible layer tree this method will *not* be invoked. If you return YES from this method, NSView will set the layer's contentScale to match the backingScaleFactor of the window it is hosted in; and mark the layer dirty using -setNeedsDisplay.
Managing NSImages with multiple representations
Taking advantage of NSImage's multiple representation capability is important for high resolution support in your apps. NSImage has facility to choose and load the appropriate representation at runtime, depending on the context in which it is being drawn. It will also reload additional representations as needed when asked to draw into different contexts.Although NSImages with multiple representations can be created at runtime, by calling addRepresentation:, it's often much easier to load such images from files. A call such as [NSImage imageNamed:@"foo"] will create an NSImage backed with all image files named "foo" in your app bundle, for instance foo.png, foo.tiff, foo.pdf, etc. In addition, the tiff file format can contain multiple representations within a single file, eliminating the need to have multiple files. This facility has been in place since OS X 10.0.
When multiple representations are providing for a single image, it's very important that the representations all have the same intrinsic size (as returned by -size). For bitmap image files with different pixel counts this is achieved by setting the density of the image properly. Since the default density of images is 72dpi, setting the density of 2x files to 144dpi achieves this constraint. When creating artwork this is often done by specifying a resolution value in an image sizing panel; programmatically this is done by setting the size of the NSBitmapImageRep explicitly to something other than {pixelsWide, pixelsHigh}.
Starting in 10.7, NSImage added support for the iOS naming convention for high resolution images, which means [NSImage imageNamed:@"foo"] will also pick up foo@2x.png. In such cases the pixel dimensions of the 2x image need to be 2x, and NSImage will assume that the density is twice that of the 1x file.
For artwork coming from bundles other than the app bundle, the 10.7 method -[NSBundle imageForResource:] can be used to do the same combination of individual files.
Despite this multi-file support in NSImage, 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 by default is going to roll them together into a multi-representation foo.tiff that contains both during build. We recommend this approach as a way to reduce the number of files in your shipping app bundles.
Command line tool tiffutil can be used to manually combine individual bitmap image files into single tiff files.
Managing application and document icons
Xcode 4.4 provides support for combining application and document icons, which use the .icns image file format. In your project resources you provide "iconset" folders that contain the individual files which will be combined at build time into .icns files. The format for the iconset folder is described in the document "High Resolution Guidelines for OS X."Command line tool iconutil can be used to manually combine individual bitmap files into single icns files.
Note that support for icns files with high resolution representations extends back to OS X 10.6, but not 10.5.
NSOpenGL
The NSOpenGLContext methods -setFullScreen and -setOffScreen:width:height:rowbytes: no longer do anything. This is because the functions on which they are are based (CGLSetFullscreen and CGLSetOffScreen) no longer work. Check the documentation for CGLSetFullscreen and CGLSetOffScreen for replacement functionality.NSColor
There's a new NSColor class method to return the lighter linen color that should be used for backgrounds of pages and revealed view areas:+ (NSColor *)underPageBackgroundColor;There are two new NSColor methods to convert back and forth from CGColorRefs:
+ (NSColor *)colorWithCGColor:(CGColorRef)cgColor;These methods do not guarantee round-trip fidelity. -colorWithCGColor: may return nil in some cases (for instance, it's not implemented for indexed color spaces). -CGColor will never return nil, but the result may be an approximation.
- (CGColorRef)CGColor;
Conversion of colors with -[NSColor colorUsingColorSpace:] using arbitrary spaces has been speeded up greatly to match the speed of conversion using the built-in standard color spaces.
NSProgressIndicator
The bar-style progress indicator now supports progress animation in right-to-left configuration when -[NSApplication userInterfaceLayoutDirection] returns NSUserInterfaceLayoutDirectionRightToLeft.NSRulerView
The icon for right and left tab markers are correctly rendered in the vertical text orientation.New gestures for NSTextView
NSTextView supports the Quick Look gesture by implementing new NSResponder APIs, -quickLookPreviewItems: and -quickLookWithEvent:. Also, the smart magnify gesture is supported through -rectForSmartMagnificationAtPoint:inRect:.NSFont
The default fixed pitch font in Mountain Lion is Menlo 11pt. +[NSFont userFixedPitchFontOfSize:] with 0.0 point size now returns Menlo 11 instead of Monaco 10.Starting from this release, the default use of screen fonts is discontinued. The text rendering and measurement APIs for user interface elements such as NSStringDrawing and NSCell methods no longer substitute font attributes with their screen font counterparts automatically. The default setting for -[NSLayoutManager usesScreenFonts] is now NO for applications linked on this and later releases. You can keep your applications running with Lion-behavior by setting a new NSUserDefaults key, NSFontDefaultScreenFontSubstitutionEnabled. NSFontDefaultScreenFontSubstitutionEnabled controls the overall default screen font substitution behavior. When set to YES, the screen font substitution is performed by all text APIs just as on previous releases. The default value is NO.
Applications can store the screen font setting into documents via NSUsesScreenFontsDocumentAttribute.
Notes specific to Mac OS 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 Mac OS X and iOS, back to Mac OS X 10.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 {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).
return [self bounds];
}
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 Mac 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 Mac 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 {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.
return [_foo respondsToSelector:action] ? _foo : [super supplementalTargetForAction:action sender:sender];
}
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 {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.
NSWindowCloseButton,
NSWindowMiniaturizeButton,
NSWindowZoomButton,
NSWindowToolbarButton,
NSWindowDocumentIconButton,
NSWindowFullScreenButton
};
typedef NSUInteger NSWindowButton;
enum {Notifications are sent for fullscreen changes:
NSFullScreenWindowMask = 1 << 14
};
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 Mac 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 Mac 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 Mac 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 Mac 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
Mac OS X 10.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
Mac OS X 10.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 Mac OS X 10.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 Mac OS X 10.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 Mac 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 Mac OS 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 Mac 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 Mac OS X 10.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 Mac OS X 10.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
Mac OS X 10.6 introduces a new “NSOpenGLLayer” class, that supports fully generalized OpenGL usage in layer-backed mode.On Mac OS X 10.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 Mac OS X 10.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 {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:
return [[[CubeViewOpenGLLayer alloc] init] autorelease];
}
- (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
Mac OS X 10.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 {The semantics of these modes are:
NSViewLayerContentsRedrawNever = 0,
NSViewLayerContentsRedrawOnSetNeedsDisplay = 1,
NSViewLayerContentsRedrawDuringViewResize = 2,
NSViewLayerContentsRedrawBeforeViewResize = 3
};
typedef NSInteger NSViewLayerContentsRedrawPolicy;
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 {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.
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;
NSOpenGLView Minor Method Behavior Changes
On Mac OS X 10.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, Mac OS X 10.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 Mac OS X 10.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 Mac OS X 10.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 Mac OS X 10.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 Mac 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 Mac 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 Mac 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 Mac 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 Mac 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 Mac 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 Mac 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 {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:).
...
NSPrintOperation *op = [NSPrintOperation printOperationWithView:...
NSPrintPanel *printPanel = [op printPanel];
[printPanel setOptions:[printPanel options] | NSPrintPanelShowsPrintSelection];
...
}
- (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 {should produce drawing equivalent to (or at least very close to) this code:
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);
}
- (void)drawRect {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.
NSRect dstRect = NSMakeRect(0.5, 0.5, 4.0, 4.0);
[pdfBackedImage drawInRect:dstRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
}
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 Mac 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 Mac 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 Mac 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 Mac OS X
NSPasteboard no longer promises deprecated pasteboard types that predate Mac OS X, and have never been public in Mac 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 Mac OS X 10.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 Mac OS X 10.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 Mac OS X 10.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 Mac OS X 10.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 Mac OS 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 {to
NSWarningAlertStyle = 0,
NSInformationalAlertStyle = 1,
NSCriticalAlertStyle = 2
} NSAlertStyle;
enum {The latter makes sure that NSAlertStyle remains a fixed size and signedness no matter how the enum contents change.
NSWarningAlertStyle = 0,
NSInformationalAlertStyle = 1,
NSCriticalAlertStyle = 2
};
typedef NSUInteger NSAlertStyle;
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 Mac OS X. The 2.x file format does not support some new features of IB, such as the ability to edit custom cell subclasses and toolbars. The 3.x file format supports all the new features, is also deployable on earlier versions of Mac OS X, but can only be edited with IB 3.0. IB 3.0 also supports a textual, human-readable format called the "xib" format. This format is equivalent to the 3.x format, but it's more SCM-friendly. It is compiled down to a 3.x file at build time.
For editing and building your project on Leopard only, we recommend the xib format, since it provides the best development time experience. If you need to build your project on Tiger, but edit your nibs on Leopard only, then you can use the 3.x format. Finally, if you wish to be able to continue to edit your nib files on Tiger, you can stick to the 2.x format. In all these cases, the files can be deployed on Tiger, but some features supported by IB 3.0 may not work on the earlier systems. IB has facilities to warn you in those cases.
Core Animation Layer Tree Rendering
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 Mac OS X's other Cocoa frameworks provide are able to function in layer-backed mode in Leopard, with the exception of certain specialized views such as WebKit WebViews and Quartz Composer QCViews, whose use in layer-backed mode is not presently supported.
Layer Size Limits and Tiled Layers
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 Mac OS X prior to Leopard, NSMenuItems with excessively long attributed titles would be clipped against the edge of the menu. In Leopard, NSMenuItem attributed titles will be truncated by default using the "NSLineBreakByTruncatingMiddle" truncation style. You may override this by specifying a different line break mode for the NSParagraphStyle attached to the attributed title string.validateItem: no longer called
Historically, AppKit has called the validateItem: method to validate menu items, if validateMenuItem: and validateUserInterfaceItem: were not implemented. This behavior was never documented and it is now disabled for applications built on Leopard. Applications that rely on on validateItem: should switch to validateMenuItem: or validateUserInterfaceItem:.validateMenuItem: only called on matching key equivalents
Before Leopard, typing a key equivalent would cause AppKit to attempt to validate every menu item in the menu (via validateMenuItem:). As an optimization, AppKit will now only validate the item with the matching key equivalent. Applications should use NSMenu's delegate methods to lazily populate a menu.Command key = treated like +
As a special case, if no menu item claims ⌘= as a key equivalent, then typing ⌘= will trigger any ⌘+ key equivalent, if the pressed = key is shared with the + key. For example, on a US keyboard layout, pressing the "=" key adjacent to the "-" key on the number row will trigger any ⌘+ key equivalent, but the "=" key above the keypad will not. If the keyboard layout does not have any "+" and "=" characters on the same key, this does not apply.Key equivalent uniqueing
AppKit tries to ensure that multiple menu items do not have the same key equivalent, because otherwise the user might accidentally invoke one while expecting to invoke the other. This is called key equivalent uniqueing. In Tiger, popup button menus were uniqued with the main menu (and in Leopard, this has been extended to also cover segmented controls). However, menu items that are thought to be aliases of one another can now share the same key equivalent, in Leopard. (Menu items are considered to be an aliases if they have the same action). Thus, in Leopard, you can have "Bold" in a popup button and also in the main menu, and both will show ⌘B as the key equivalent.Disabled key equivalents passed through
Prior to Leopard, key equivalents corresponding to disabled menu items would be ignored. In Leopard, your application now has a chance to handle these. For example, a key equivalent for control-K on a disabled menu item will no longer block the emacs shortcut in an NSTextView, in Leopard.ß support in key equivalents
The German sharfes s character (ß) is now supported as a key equivalent.Menu Item Key Equivalents in non-Roman Scripts
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 Mac OS X prior to Leopard, interface items, including menu items, that had toggleToolbarShown: set as their action would have their titles changed to "Show Toolbar" or "Hide Toolbar." For applications linked on or after Leopard, AppKit will only flip "Show Toolbar" to "Hide Toolbar" (or their localized variations) and vice versa, as appropriate. The empty title will also be modified. Nonempty titles other than the localized variants of these strings must be managed by your application.NSToolbar - Key equivalents can match in toolbars
Before Leopard, only NSPopUpButtons in toolbars could respond to key equivalents. In Leopard, AppKit now respects the key equivalents of all controls in (visible) toolbars, not just popup buttons.NSToolbar - NSToolbarItem default sizes
As of Leopard, if you call setView: on an NSToolbarItem without having called setMinSize: or setMaxSize:, the toolbar item will set its minimum and maximum size equal to the view's frame size.NSToolbar - View archival
In Tiger, NSToolbar would always "copy" your toolbar item's view when displaying it in the customization palette with an NSKeyedArchiver. In Leopard, NSToolbar will only copy your view if you set the view on two different toolbar items at once. For example, say you allocate exactly one view (perhaps in a nib) and always pass it to -[NSToolbarItem setView:] in your toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar: method. If that view is in an active toolbar item, and you display the customization palette, NSToolbar will copy that view once. If that view is not in an active toolbar item and then you display the customization palette, NSToolbar will not copy the view for the customization palette, but will copy that view at the point the user drags the corresponding toolbar item from the palette into the toolbar.If you always ensure that you pass fresh views into setView:, then NSToolbar will never copy the view.
NSToolbar - mouseDownCanMoveWindow
Previous to Leopard, only textured and unified windows were draggable by their toolbar. In Leopard, all windows are draggable by their toolbars. Apps that placed custom views in toolbars and did not override isOpaque or mouseDownCanMoveWindow may find that their views unexpectedly become draggable in addition to responding to clicks. To prevent this, the mouseDownCanMoveWindow is considered to return NO if all of the following are true:- The app was linked on Tiger or earlier.
- The view is in a toolbar item.
- The view does not override mouseDownCanMoveWindow.
- The window is not textured or unified title/toolbar.
NSSound
NSSound has additional features on Leopard and later. These include the ability to get the duration of a sound, change the volume of a sound, set and get the playback location within the sound, and cause a sound to loop. See the documentation or comments within the NSSound.h header for more about these new APIs.In Tiger, NSSound would willingly create a "sound" for any valid Quicktime file, including images. In Leopard, NSSound's init methods will now return nil for files that do not have a sound track.
NSSpeechSynthesizer
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 Mac OS X, get the options from the print panel you've just created, turn on and off the flags you care about, and then set the options.
- (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 Mac OS X there has been no public way to get the Core Printing (previously known as "Printing Manager") objects that an NSPrintInfo is actually built on top of. To make it easy for you do to exactly the same things that the PMPrintSettingsGetValue()/PMPrintSettingsSetValue() functions added in Mac OS 10.4 let you do, a new method has been added to NSPrintInfo in Mac OS 10.5. The most immediate problem that this method solves is that there has been no way for your accessory views to put settings in a place where they can be saved and restored by the printing systems presets mechanism:- (NSMutableDictionary *)printSettings;The print info's print settings. You can put values in this dictionary to store them in any preset that the user creates while editing this print info with a print panel. Such values must be property list objects. You can also use this dictionary to get values that have been set by other parts of the printing system, like a printer driver's print dialog extension (the same sort of values that are returned by the Carbon Printing Manager's PMPrintSettingsGetValue() function). Other parts of the printing system often use key strings like "com.apple.print.PrintSettings.PMColorSyncProfileID" but dots like those in key strings wouldn't work well with KVC, so those dots are replaced with underscores in keys that appear in this dictionary, as in "com_apple_print_PrintSettings_PMColorSyncProfileID". You should use the same convention when adding entries to this dictionary.
Because there is a substantial amount of functionality exposed by Core Printing API that is not generally applicable enough to be exposed by AppKit API, new methods have been added to NSPrintInfo in Mac OS 10.5 to allow access to the underlying Core Printing objects:
- (void * /* PMPrintSession */)PMPrintSession;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 Mac OS X the print job title that the printing system uses has been gotten by invoking -[NSView(NSPrinting) printJobTitle:]. Because it is often not programmatically convenient to make such information available to printed views, new methods have been added to NSPrintOperation in Mac OS 10.5, so that the code that creates the print operation can simply set the print job title right away, and not have to leave it somewhere the printed view can find it:- (void)setJobTitle:(NSString *)jobTitle;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 Mac OS X the -currentPage method has allowed you to find out exactly which page is being printed during the running of a print operation, but there has been no easy way to determine how many pages the document being printed has. In Mac OS 10.5 a new method has been added to let you do that:
- (NSRange)pageRange;The first page number might not be 1, depending on what the printed view returned when sent an -[NSView(NSPrinting) knowsPageRange:] message.
Support for Multiple Page Orientations per Print Operation
In Mac OS 10.5 you can now change the orientation of pages while a job is being printed. To do so your printed view must do its own pagination, and indicate so by returning YES when sent -knowsPageRange: messages. Then, when sent -rectForPage: messages, it may change the orientation of the current print operation's print info, and Cocoa will heed the change. For example: [[[NSPrintOperation currentOperation] printInfo] setOrientation:theNewOrientation].Deprecated NSPrintOperation Methods, and a Bug Fix in -[NSPrintOperation printPanel]
In Mac OS 10.5 the -setAccessoryView: and -accessoryView methods are now also deprecated in NSPrintOperation.Also deprecated are the -setJobStyleHint: and -jobStyleHint: methods. Instead of sending one of those messages to an NSPrintOperation, just invoke -[NSPrintOperation printPanel] and send them to the returned NSPrintPanel instead. -[NSPrintOperation printPanel] has been updated to create and return a new print panel if none has been set yet. (It would usually just return nil before.)
Bug Fixes in -[NSPrintInfo paperName], -[NSPrintInfo setPaperName:], and -[NSPrinter pageSizeForPaper:]
In Mac OS 10.2 through Mac OS 10.4 there was a bug in -[NSPrintInfo paperName] that would cause it to return incorrect paper names. For example, it might return "na-letter" or "Letter" when it should return "LetterSmall" (remember, the paper "names" returned by -paperName are not for showing to users; use -localizedPaperName to get those). Likewise, -[NSPrintInfo setPaperName:] could set a paper that had the same size as the named paper but a different name. These problems were particularly noticeable when a printer supports multiple "papers" that have the same sizes but different imageable areas. The bug has been fixed in Mac OS 10.5.In earlier versions of Mac OS X -[NSPrinter pageSizeForPaper:] only worked when passed strings that are paper names, not paper IDs, in the senses established by the Core Printing API. In Mac OS 10.5 this method now always works when passed paper IDs (the sort of string now always returned by -[NSPrintInfo paperName]). For backward binary compatibility it returns the same values that it would have in Mac OS 10.4 if passed a paper name.
Bug Fixes in NSPrintOperation/NSPrintPanel Cancellation
In earlier versions of Mac OS X there was a bug in which a print job would be sent to the printer even though the user had hit the Cancel button in the print progress panel, if rendering of the last page of the job had already begun. This bug has been fixed in Mac OS 10.5.There was also a bug in which invoking [[[NSPrintOperation currentOperation] printInfo] setJobDisposition:NSPrintCancelJob] from within overrides of NSView(NSPrinting) methods did not prevent the part of the print job that had already been rendered from being sent to the printer. This bug has also been fixed in Mac OS 10.5. You can now programmatically cancel the current print operation in your overrides of any of the NSView(NSPrinting) methods.
In earlier versions of Mac OS X there was no way to distinguish between user cancellation and failure when invoking -[NSPrintOperation runOperationModalForWindow:delegate:didRunSelector:contextInfo:] or -[NSPrintPanel beginSheetWithPrintInfo:modalForWindow:delegate:didEndSelector:contextInfo:]. In Mac OS 10.5 these methods always set the job disposition of the presented print info to NSPrintCancelJob when the user has cancelled the print panel, just as -[NSPrintOperation runOperation] and -[NSPrintPanel runModal] always have. For backward binary compatibility this is only done if the application is linked against Mac OS 10.4 or earlier. In the case of NSPrintOperation the "presented print info" is the one that can be gotten with -[NSPrintOperation printInfo], not the one that was passed in when the NSPrintOperation was created.
There was a bug in which an NSPrintOperation created with a print info whose job disposition was NSPrintCancelJob would never result in output, regardless of what button the user used to dismiss the print panel. This bug has been fixed.
Advice for Developers Working with Printing Margins
The "Printer Margins" in the Custom Page Sizes panel are confusingly named. They are not margins in the NSPrintInfo sense, or in the sense of Microsoft Word, AppleWorks, or any app that lets the user set a document's page margins. They are the widths and heights of the areas of the page that lie outside of the imageable area for the paper size. In other words, the Custom Page Sizes panel is giving you the opportunity to simulate hardware limitations when defining a new paper size. You can use the -[NSPrintInfo imageablePageBounds] method to get the area of page that is surrounded by the printer margins.Support for Uniform Type Identifiers (UTIs) in NSDocument-Based Applications
If your application requires Mac OS 10.5, it may stop using these legacy Info.plist keys in its CFBundleDocumentTypes entries:CFBundleTypeExtensions
CFBundleTypeMIMETypes
CFBUndleTypeOSTypes
NSExportableAs
It may instead use the LSItemContentTypes and (new for Mac OS 10.5) NSExportableTypes keys, and declare the corresponding UTIs as appropriate in its Info.plist. Declaration of UTIs in Info.plist files is described by <http://developer.apple.com/macosx/uniformtypeidentifiers.html>. For each CFBundleDocumentTypes entry that has an LSItemContentTypes subentry, sibling subentries with the keys listed above are ignored. (Actually Cocoa's Info.plist parsing has always ignored CFBundleTypeMIMETypes subentries. In Mac OS 10.5 it continues to. LaunchServices pays attention to them though, except when there's an LSItemContentTypes sibling. So, in Mac OS 10.5 Cocoa and LaunchServices follow the same rules about ignoring LSItemContentTypes siblings.)
If your application uses the LSItemContentTypes key it must also use the NSExportableTypes key where applicable. NSExportableTypes entries serve the same purpose as NSExportableAs entries, and are likewise subentries of CFBundleDocumentTypes entries, but their values are arrays of UTIs instead of arrays of old-style free-form type names.
Use of these keys becomes optional:
CFBundleTypeName
CFBundleTypeIconFile
For each CFBundleDocumentTypes entry that has an LSItemContentTypes subentry you can provide subentries using these keys to override the default icon and kind string that would otherwise result from UTI declarations. As has always been the case, any use of CFBundleTypeName should be accompanied by an entry in the application's InfoPlist.strings files.
The meanings of CFBundleTypeRole and NSDocumentClass subentries have not changed in Mac OS 10.5.
For each CFBundleDocumentTypes entry that has an LSItemContentTypes subentry, the UTIs are used as programmatic type names by NSDocument and NSDocumentController, as described below, instead of the value of any CFBundleTypeName subentry that might also be present.
For backward binary compatibility, none of this new behavior takes affect if the application is linked against Mac OS 10.4 or earlier. A CFBundleDocumentTypes entry that has no LSItemContentTypes subentry at all has the same meaning to Cocoa as in Mac OS 10.4.
Debugging tip: In Mac OS 10.5 you can find out what NSDocumentController thinks it's found in your application's Info.plist by executing 'print-object [[NSClassFromString(@"NSDocumentController") sharedDocumentController] _typeDescriptions]' in gdb (using the Xcode Debugger Console, for instance). Do not attempt to invoke or override -_typeDescriptions in your application. It is there just for this debugging purpose, and may disappear at any time.
Shipping tip: If you want to take advantage of UTIs in your application but have it still run on both Mac OS 10.4 and Mac OS 10.5, you can merely add LSItemContentTypes (and NSExportableTypes, where appropriate) subentries to your Info.plist, and then update your overrides of NSDocumentController and NSDocument methods so that they recognize UTIs as type names in addition to the sort of document types names that were always used in Mac OS 10.4. Be careful to add LSItemContentTypes subentries to all of the app's CFBundleDocumentTypes Info.plist entries though, or things could get more complicated. See the description of -typeForContentsOfURL:error:'s new behavior down below for instance.
Support for UTIs in NSDocumentController
In Mac OS 10.5 NSDocumentController supports UTIs. In the following paragraphs an "app-declared" UTI is one that is declared in a well-formed LSItemContentTypes subentry of a well-formed CFBundleDocumentTypes Info.plist entry, in an application that is linked against Mac OS 10.5 or later. A "recognized" UTI is app-declared or conforms to one of the app-declared UTIs.None of the NSDocumentController methods that were deprecated in Mac OS 10.4 have been updated to handle UTIs properly. You have to stop invoking and overriding deprecated methods to take advantage of UTIs in your application.
-makeUntitledDocumentOfType:error:, -makeDocumentWithContentsOfURL:ofType:error:, and -makeDocumentForURL:withContentsOfURL:ofType:error: all now accept recognized UTIs as type strings, in addition to the sort of document type names that were accepted in Mac OS 10.4. The latter two methods have also been updated to properly handle unrecognized types too. (Because -typeForContentsOfURL:error: might now return an urecognized type.)
-URLsFromRunningOpenPanel now, for each CFBundleDocumentTypes entry that declares an openable document type (as in, has a CFBundleTypeRole of Editor or Viewer, as in Mac OS 10.4) with app-declared UTIs, passes those UTIs into -runModalOpenPanel:forTypes:. For each CFBundleDocumentTypes that declares an openable document without recognized UTIs it still passes file name extensions and encoded HFS file types as in Mac OS 10.4.
-runModalOpenPanel:forTypes: nows accepts all valid UTIs in the array of type strings, in addition to the file name extensions and encoded HFS file types that were accepted in Mac OS 10.4.
-defaultType chooses a CFBundleDocumentTypes entry and considers that document type to be the default type, as in Mac OS 10.4. Once it has made that choice it now returns the first app-declared UTI for that type, if there are any, or a document type name, of the sort returned in Mac OS 10.4, if not. (So the order of elements in the LSItemContentTypes array does matter, just as the order of elements in the CFBundleDocumentTypes array always has.)
-typeForContentsOfURL:error: now uses -[NSWorkspace typeOfFile:error:] to find out the UTI of the file located by the URL. (So it might return a UTI that is not recognized. AppKit's own invocations of it take that fact into account.) For backward binary compatibility it however first tries the same thing that it did in Mac OS 10.4 (invoke -typeFromFileExtension:, possibly twice, passing an HFS file type string for the second invocation) if there are any CFBundleDocumentTypes Info.plist entries that don't have LSItemContentTypes subentries.
-documentClassForType: now accepts any recognized UTI as the type string. As in Mac OS 10.4 it sends a +readableTypes message to each class named in the results of invoking -documentClassNames, and returns the first class whose readable types include one that matches the passed-in type. "Matches" for a UTI means "conforms to," instead of merely "is equal to." (So this method will work even when passed a UTI that is not explicitly declared in the application's Info.plist, but conforms to one that is.)
-displayNameForType: now accepts all valid UTIs as the type string, in addition to the sort of document type names that were accepted in Mac OS 10.4. It returns nil when passed an invalid UTI. For backward binary compatibility it continues to not return nil when passed a non-UTI.
-fileExtensionsFromType: and -typeFromFileExtension: have not changed, but are being deprecated. -fileExtensionsFromType: does not work when passed a UTI. -typeFromFileExtension: only works when passed a file name extension used in a CFBundleDocumentTypes entry that does not have an LSItemContentTypes subentry. In general, if each of the application's CFBundleDocumentTypes Info.plist entries has a valid LSItemContentTypes subentry, and the application doesn't invoke deprecated methods like -fileNamesFromRunningOpenPanel, then these methods will never be invoked from within Cocoa.
Support for UTIs in NSDocument
In Mac OS 10.5 NSDocument's handling of types is consistent with NSDocumentController. Every non-deprecated NSDocument method that takes a type: or ofType: argument now accepts recognized UTIs as type strings, in addition to the sort of document type names that were accepted in Mac OS 10.4. -setFileType: and -fileType: don't actually interpret the file type name in any way, so they work fine with UTIs.None of the NSDocument methods that were deprecated in Mac OS 10.4 have been updated to handle UTIs properly. You have to stop invoking and overriding deprecated methods to take advantage of UTIs in your application.
+readableTypes, +writableTypes, and -writableTypesForSaveOperation: all now return UTIs for CFBundleDocumentTypes entries with app-declared UTIs, and document types of the sort that were returned in Mac OS 10.4 for those that don't.
+isNativeType: now accepts all valid UTIs as the type string, in addition to the sort of document type names that were accepted in Mac OS 10.4. It will return YES if a passed-in UTI conforms to a recognized UTI for the document class in question, NO otherwise.
A new method has been added to NSDocument, because -[NSDocumentController fileExtensionsFromType:] is being deprecated:
- (NSString *)fileNameExtensionForType:(NSString *)typeName saveOperation:(NSSaveOperationType)saveOperation;For a specified type, and a particular kind of save operation, return a file name extension that can be appended to a base file name. The default implementation of this method invokes [[NSWorkspace sharedWorkspace] preferredFilenameExtensionForType:typeName] if the type is a UTI or, for backward binary compatibility with Mac OS 10.4 and earlier, invokes [[NSDocumentController sharedDocumentController] fileExtensionsFromType:typeName] and chooses the first file name extension in the returned array if not.
You can override this method to customize the appending of extensions to file names by NSDocument. In Mac OS 10.5 it's only invoked from two places within Cocoa: 1) -autosaveDocumentWithDelegate:didAutosaveSelector:contextInfo: uses this method when creating a new file name for the autosaved contents. 2) -[NSDocument(NSScripting) handleSaveScriptCommand:] uses this method when adding an extension to the file name specified by a script. In all other cases the name of any file being saved will have been fully specified by the user, with the save panel (whether they know it or not).
Support for UTIs in NSPersistentDocument
In Mac OS 10.5 NSPersistentDocument's handling of types is consistent with NSDocumentController's and NSDocument's. Every NSPersistentDocument method that takes a type string argument now accepts recognized UTIs, in addition to the sort of document type names that were accepted in Mac OS 10.4.Support for UTIs in NSOpenPanel
In Mac OS 10.5 NSOpenPanel supports UTIs. The following methods all now accept all valid UTIs as type strings, in addition to the file name extensions and encoded HFS file types that were accepted in Mac OS 10.4:-beginSheetForDirectory:file:types:modalForWindow:modalDelegate:didEndSelector:contextInfo: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 Mac OS X, these four classes:NSImage
NSImageRep
NSSound
NSAttributedString (in the NSAttributedStringKitAdditions category)
have all had pairs of methods that return either arrays of file type strings (file name extensions and encoded HFS file types) or pasteboard type strings. In Mac OS 10.5, these methods are joined by single new methods that just return arrays of UTIs. Also, NSImageRep's methods that take file type or pasteboard type strings are joined by a new method that just takes UTIs.
It's important to keep in mind when working with UTIs that mere string equality checking is not the correct way to check if the type identified by one UTI "conforms" to the type identified by another. See the description of the -[NSWorkspace type:conformsToType:] in the "Support for UTIs in NSWorkspace" section.
The follow sections contain the details of this change for each class.
Support for UTIs in NSImage
In NSImage, these new methods:+ (NSArray *)imageTypes;join these methods, which might be deprecated in a future release of Mac 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 Mac 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 Mac OS X there has been a bug in which saving a document, undoing changes, and then making an equal number of new changes would cause the document to appear unmodified. If the user closed the document it would simply be closed, with no warning about unsaved changes, which would be lost. This happened in any application in which the document did not send [self updateChangeCount:NSChangeCleared] during document saving (which you're not supposed to have to do). This bug has been fixed in Mac OS 10.5, by virtue of NSDocument now drawing a distinction between doing and redoing of changes. It no longer invokes [self updateChangeCount:NSChangeDone] when it receives an NSUndoManagerDidRedoChangeNotification. Now it invokes [self updateChangeCount:NSChangeRedone] instead. (NSChangeDone is still used for NSUndoManagerWillCloseUndoGroupNotification.) NSChangeRedone is new, and declared in <AppKit/NSDocument.h>.For backward binary compatibility NSDocument only uses NSChangeRedone instead of NSChangeDone in applications linked against Mac OS 10.5 or later, or if -updateChangeCount: is not overridden.
Bug Fix in NSDocument for Nested Undo Manager Groups
In previous versions of Mac OS X there was a bug in which NSDocument's undo support did not take into account nested undo manager groups. Each NSDocument would merely send itself a [self updateChangeCount:NSChangeDone] message whenever it received an NSUndoManagerWillCloseUndoGroupNotification from its undo manager. This would cause it to improperly count how many changes the user had made so that, for example, making one undoable change represented by multiple actions in nested undo groups, and then undoing that change, would still show the document as modified. This problem was particularly noticeable in document-based Core Data applications, because NSManagedObjectContext uses nested undo manager groups. This has been fixed in Mac OS 10.5. NSDocument now checks the undo manager's grouping level when it receives NSUndoManagerWillCloseUndoGroupNotification, and only invokes -updateChangeCount: if the nesting level is less than two.Bug Fix in -[NSDocument writeToURL:ofType:error:]'s Use of NSFileWrapper
In Mac OS 10.4, -[NSDocument writeToURL:ofType:error:] passed NO as the last argument when invoking -[NSFileWrapper writeToFile:atomically:updateFilenames:]. In Mac OS 10.5 it now passes YES so that the file wrapper and any file wrappers it contains have their file names updated during saving. -[NSDocument writeToFile:ofType:], which was deprecated in Mac OS 10.4, has not been updated in a similar way.Bug Fix in NSDocument's Use of -[NSDocument autosavingFileType]
In Mac OS 10.4, -[NSDocument autosaveDocumentWithDelegate:didAutosaveSelector:contextInfo:] would invoke [self autosavingFileType] the first time a document was autosaved, create an autosaved contents file URL using the implied file name extension, and then keep using that URL for all subsequent autosaves. If -autosavingFileType was overridden to return different values at different times, this could result in inconsistencies. For example, the new version of TextEdit in Leopard that uses autosaving could end up autosaving an RTFD file package with an ".rtf" file name extension. (Because it overrides -autosavingFileType to account for attachments being added to the document.) This was a bug, and has been fixed in Mac OS 10.5. NSDocument now consistently uses the result of invoking -autosavingFileType when determining where to autosave.Advice for Overriders of -[NSDocument autosavingFileType]
Even with the bug fixed mentioned above, overriding -autosavingFileType can result in incorrect behavior during reopening of autosaved documents if you're not careful. -[NSDocument initForURL:withContentsOfURL:ofType:error:], which is invoked during reopening of autosaved documents after a crash, takes two URLs, but only the type name of the autosaved contents file. The default implementation invokes [self setFileType:] with that type name, but that is often not the right thing to do, if -autosavingFileType had returned something other than -fileType during document autosaving. If you override -autosavingFile, you probably have to override -initForURL:withContentsOfURL:ofType:error: too, and make the override invoke -setFileType: with the type of the actual document file, after invoking super. See TextEdit's Document class for an example of how to do this.Advice for Overriders of NSDocument Reading and Writing Methods
If you subclass NSDocument and override any of these methods for reading:-readFromData:ofType:error:
-readFromFileWrapper:ofType:error:
-readFromURL:ofType:error:
Or any of these for writing:
-dataOfType:error:
-fileWrapperOfType:error:
-writeToURL:ofType:error:
-writeToURL:ofType:forSaveOperation:originalContentsURL:error:
-fileAttributesToWriteToURL:ofType:forSaveOperation:originalContentsURL:error:
-writeSafelyToURL:ofType:forSaveOperation:error:
Or any of the methods that were deprecated in favor of them, in Mac OS 10.4:
-dataRepresentationOfType:
-fileAttributesToWriteToFile:ofType:saveOperation:
-fileWrapperRepresentationOfType:
-initWithContentsOfFile:ofType:
-initWithContentsOfURL:ofType:
-loadDataRepresentation:ofType:
-loadFileWrapperRepresentation:ofType:
-readFromFile:ofType:
-readFromURL:ofType:
-writeToFile:ofType:
-writeToFile:ofType:originalFile:saveOperation:
-writeToURL:ofType:
-writeWithBackupToFile:ofType:saveOperation:
Don't invoke -fileURL (or -fileName, the method that was deprecated in favor if it, in Mac OS 10.4), -fileType, or -fileModificationDate from within your overrides. During reading, which typically happens during object initialization, there is no guarantee that NSDocument properties like the file's location or type have been set yet. Your overridden method should be able to determine everything it needs to do the reading from the passed-in parameters. During writing, your document may be being asked to write its contents to a different location, or using a different file type. Again, your overridden method should be able to determine everything it needs to do the writing from the passed-in parameters.
If your override cannot determine all of the information it needs from the passed-in parameters, consider overriding another method. For example, if you see the need to invoke -fileURL from within an override of -readFromData:ofType:error:, perhaps you should instead override -readFromURL:ofType:error:. For another example, if you see the need to invoke -fileURL from within an override of -writeToURL:ofType:error:, perhaps you should instead override -writeToURL:ofType:forSaveOperation:originalContentsURL:error:.
Advice for Overriders of -[NSDocument displayName]
Some applications have subclasses of NSDocument that override -displayName to customize the the titles of windows associated with the document. That is usually not the right thing to do. Use a subclass of NSWindowController, and override -[NSWindowController windowTitleForDocumentDisplayName:] instead. If even deeper customization is required override -[NSWindowController synchronizeWindowTitleWithDocumentName]. A document's display name is used in several other places where the custom value that an application might want to use as a window title is typically not appropriate:- In error alerts that may be presented during reverting, saving, or printing of the document.
- In alerts that will be presented during document saving if the document has been moved, renamed, or put in the trash.
- In the alert that will be presented when the user attempts to close the document with unsaved changes.
- As the default value shown in the "Save As:" field of save panels.
It may be used in even more places in future releases of Mac OS X.
Advice for Overriders of -[NSDocument init]
If you subclass NSDocument and override -init, make sure your override never returns nil. When running on Mac OS 10.4 this will likely cause a crash in AppKit. In Mac OS 10.5 it will cause AppKit to present an error alert that is not very useful to the user (for example, "No document could be created."). If, for example, you want to prevent the creation or opening of documents under circumstances unique to your application, override a specific NSDocumentController method instead. Read the next section before doing so.Advice for Overriders of Methods That Return NSErrors by Reference
In Mac OS 10.4 we published many new methods that return NSErrors. For example, in NSDocumentController:- (id)openUntitledDocumentAndDisplay:(BOOL)displayDocument error:(NSError **)outError;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 Mac OS X, each NSWindowController registered for NSWindowWillCloseNotification from its window and then did a number of things when it got the notification if it had a document (like removing itself from the document's window controllers and trying to close document). This way of doing things caused several problems, including:• Other recipients of NSWindowWillCloseNotification would find that the window controller already had no document when they received their notification.
• The NSWindowController would not get the NSWindowWillCloseNotification, and the document would not be closed, if the window controller had been the window's delegate and then the window's delegate was reset (because of the way -[NSWindow setDelegate:] deregisters the previous delegate as an observer of notifications).
In Mac OS 10.5 an NSWindow that has a window controller now sends it a private message when the window did (not will) close, fixing those and other problems. NSWindowControllers no longer register for or depend on receiving NSWindowWillCloseNotification.
Also, a change that's similar to one in -[NSWindowController dealloc] has been made, and the window controller itself is now released instead of autoreleased (to balance a [self retain] that the window controller does before sending -removeWindowController:self to its document). For backward binary compatibility the new behavior is only done in applications linked against Mac OS 10.5 or later.
Bug Fixes in -[NSWindowController dealloc]
-[NSWindowController setWindow:] makes the window control the next responder of the window, which is as designed. However, this could result in the NSWindow messaging the NSWindowController's zombie, depending on which object was deallocated first. In Mac OS 10.5, -[NSWindowController dealloc] fixes this problem by sending the window -setNextResponder:nil if the window controller has a window and is its next responder.In previous versions of Mac OS X, -[NSWindowController dealloc] autoreleased the window and the top level objects from the nib it loaded. This made it needlessly difficult to debug programming mistakes in applications. In Mac OS 10.5 -[NSWindowController dealloc] now releases the window and top level objects instead. For backward binary compatibility the new behavior is only done in applications linked against Mac OS 10.5 or later.
Advice for People Setting up Nibs that Have NSWindowController File's Owners
NSWindowController has never depended on being the controlled window's delegate to do its job. NSWindowController doesn't even implement any NSWindow delegate methods. A subclass of NSWindowController is a fine place to put implementations of NSWindow delegate methods, and if you do so you'll probably need to connect the delegate outlet of a window in a nib to the nib file's owner, an instance of your NSWindowController subclass, but you do not have to do so on NSWindowController's account.New Behavior for NSWindowController Frame Autosave Names
In previous versions of Mac OS X, -[NSWindowController setWindow:], which is typically invoked at nib-loading time, always set the frame autosave name of the window to the window controller's own frame autosave name. This was a significant inconvenience because it meant that the name you set in the window using Interface Builder was always effectively ignored. In Mac OS 10.5 -[NSWindowController setWindow:] now only sets the window's frame autosave name if its own is something other than nil or the empty string. This means that a window's frame autosave name is now useful even when the window is loaded by a window controller. You can still explicitly invoke -[NSWindowController setWindowFrameAutosaveName:] if for some reason you need to override the window's frame autosave name at runtime. For backward binary compatibility the new behavior is only done in applications linked against Mac OS 10.5 or later.NSSplitView Enhancements
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 Mac OS X, NSScrollWheel events were only sent to the window under the mouse if the window had key focus, with the exception of utility windows which received NSScrollWheel events even when inactive.
Key equivalents
We fixed a problem where NSKeyUp events were sent to performKeyEquivalent: if the command-key was not down. This problem could cause a key equivalent to be performed twice if the receiver didn't check the event type for NSKeyDown. The fix applies only to applications built on Leopard or later, to preserve binary compatibility for any application which may have been dependent on the old behavior.The modifier flags are now preserved in NSKeyDown <esc> events. Prior to this change, NSWindow would strip modifier flags from the <esc> key event before invoking -performKeyEquivalent:. We don't expect applications to be dependent on this behavior, but if you need to ignore modifier flags on <esc> keyDown events, you can override -performKeyEquivalent: to do so.
In -[NSWindow sendEvent:] we now send keyDown: to the first responder even if the command-key modifier is set. An NSKeyDown event will only reach this point if it was not recognized as a key equivalent. One effect of this change is to enable custom key binding entries with command-key modifiers. The change applies only to applications built on Leopard or later, to preserve binary compatibility for any application which may have been dependent on the old behavior.
NSApplication now sends a ctrl-key event to performKeyEquivalent: before sending the keyDown: event through the responder chain. This allows ctrl-key events to be used as menu key equivalents more reliably. Prior to this change, a ctrl-key event which had an emacs key binding would not be sent to performKeyEquivalent: if focus was in an NSTextView.
Dragging
-[NSDraggingInfo slideDraggedImageTo:] is now implemented to behave as documented. This change is enabled only for applications built on Leopard or later to avoid changing behavior of older binaries in ways that may be incorrect.-draggingEnded is now implemented for applications linked on Leopard or later.
NSDatePicker
NSDatePicker - Range mode
NSDatePicker now implements the existing date range selection API for the mini-calendar style date picker. This is enabled by calling setDatePickerMode: and passing in NSRangeDateMode. The range is represented by a start date (NSDate; accessible through dateValue) and a time interval (NSTimeInterval; accessible through timeInterval). If the date picker is a textfield style date picker, setDatePickerMode: is ineffectual and timeInterval returns 0.0 as before. If the date picker is in NSSingleDateMode, then timeInterval always returns 0.0.Date ranges can be selected in the mini-calendar interface by clicking and dragging across calendar days or by shift-clicking. The calendar's months will also advance or retreat as the mouse is dragged below or above the area where calendar days are displayed.
NSDatePicker - Small and mini sizes
The text field styles of NSDatePicker now support small and mini control sizes through the -setControlSize: method. However, this only affects the size of the internal stepper cell. The size of the text field is determined by the size of its font and number of displayed date components.NSDatePicker - Text field only style
NSDatePicker now supports a third style: NSTextFieldDatePickerStyle. This is identical to NSTextFieldAndStepperDatePickerStyle in every way, except it does not display a stepper.NSDatePickerCell - Creation performance
The NSDatePickerCell creation speed has been significantly improved by drastically reducing the number of NSDateFormatters created internally.NSDatePicker - Date Arithmetic Improvements
NSDatePickerCell's date arithmetic implementation has changed substantially in Leopard, abandoning use of the obsolete NSCalendarDate class (which only supports the Gregorian calendar, and yields imprecise results for dates in the distant past -- e.g. for years circa 1500) in favor of a fully modern NSCalendar-based implementation underpinned by ICU library routines. This fixes significant editing issues for Gregorian calendar dates, while providing substantial localization improvements for non-Gregorian calendars.Text
New Text Document Formats
The text system now has support for reading and writing both the OASIS Open Document text document format and the ECMA Office Open XML text document format. AppKit/NSAttributedString.h has two new constants for this, NSOfficeOpenXMLTextDocumentType and NSOpenDocumentTextDocumentType. The /usr/bin/textutil command has been updated to support both formats.NSAttributedString HTML Import
Since Tiger, NSAttributedString has used WebKit for all import of HTML documents (but not for export). Since WebKit document loading is not threadsafe, this has not been safe to use on background threads. For applications linked on Leopard and later, if NSAttributedString is used to import HTML documents on a secondary thread, the usage of WebKit will be transferred to the main thread via performSelectorOnMainThread:withObject:waitUntilDone:. This makes such usage threadsafe, but it requires that the main thread be running the run loop in one of the common modes. This behavior can be overridden by setting the default NSRunWebKitOnAppKitThread to either YES (to obtain the new behavior regardless of linkage) or NO (to obtain the old behavior regardless of linkage).In versions prior to Leopard, NSAttributedString HTML import would set the NSBackgroundColorDocumentAttribute to [NSColor whiteColor] in cases in which the HTML did not specify a background color. This will continue to be true for applications linked on system versions prior to Leopard, but on Leopard and later, for applications linked on Leopard and later, no NSBackgroundColorDocumentAttribute will be set in these cases.
NSMutableAttributedString Note
The method -[NSMutableAttributedString readFromURL:options:documentAttributes:error:] is expected to replace the text of the receiver with the contents of the desired file. (The documentation states: "Sets the contents of receiver from the file at url.")It works as expected for plaintext formatted files. However, for RTF formatted files, the contents of the file are appended to the previous string instead of replacing the previous string. This inconsistency is a bug; when using this method with existing content it's best to clear the content away explicitly.
NSTextStorage Scripting
For applications linked on Leopard or later, we now check for nil font in scripting calls and treat it as Helvetica 12. This impacts the calls font, fontName, fontSize, setFontName:, and setFontSize:.Non-Contiguous Layout
NSLayoutManager now has a new option, referred to as non-contiguous layout. Previously, both glyph generation and layout have always been performed in order from the beginning to the end of the document. When non-contiguous layout is turned on, however, the layout manager gains the option of performing glyph generation or layout for one portion of the document without having done so for previous sections. This can provide significant performance improvements for large documents.Non-contiguous layout is not turned on automatically because direct clients of NSLayoutManager typically have relied on the previous behavior--for example, by forcing layout for a given glyph range, and then assuming that previous glyphs would therefore be laid out. Clients who use NSLayoutManager only indirectly--for example, those who use NSTextView without directly calling the underlying layout manager--can usually turn on non-contiguous layout without difficulty. Clients using NSLayoutManager directly will need to examine their usage before turning on non-contiguous layout.
The methods directly concerned with non-contiguous layout are as follows:
- (void)setAllowsNonContiguousLayout:(BOOL)flag;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 Mac OS X 10.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 Mac OS X 10.2, but its interpretation has changed. Previously, any non-zero value would cause the spelling indicator to be displayed. For Mac OS X 10.5, the (integer) value will be treated as being composed of the following flags:
enum {There is a new method on NSTextView for setting the spelling state, which may be called by clients or overridden by subclassers.
NSSpellingStateSpellingFlag = (1 << 0),
NSSpellingStateGrammarFlag = (1 << 1)
};
- (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.