The editing process involves careful synchronization of the complex interaction of various objects. The text system coordinates event processing, data modification, responder chain management, glyph generation, and layout to maintain consistency in the text data model.
The system provides a rich set of notifications to delegates and observers to enable your code to interact with this logic, as described in “Delegate Messages and Notifications.”
Batch-Editing Mode
Forcing the End of Editing
If your code needs to modify the text backing store directly, you should bracket the changes between the NSTextStorage
methods beginEditing
and endEditing
. Although this bracketing is not strictly necessary, it’s good practice, and it’s important for efficiency if you’re making multiple changes in succession. NSTextView
uses the beginEditing
and endEditing
methods to synchronize its editing activity, and you can use the methods directly to control the timing of notifications to delegates, observers, and associated layout managers. When the NSTextStorage
object is in batch-editing mode, it refrains from informing its layout managers of any editing changes until it receives the endEditing
message.
The “beginning of editing” means that a series of modifications to the text backing store (NSTextStorage
for text views and cell values for cells) is about to occur. Bracketing editing between beginEditing
and endEditing
locks down the text storage to ensure that text modifications are atomic transactions.
The “end of editing” means that the backing store is in a consistent state after modification. In cells (such as NSTextFieldCell
objects, which control text editing in text fields), the end of editing coincides with the field editor resigning first responder status, which triggers synchronization of the contents of the field editor and its parent cell.
In addition, the text view sends NSTextDidEndEditingNotification
when it completes modifying its backing store, regardless of its first responder status. For example, it sends out this notification when the Replace All button is clicked in the Find window, even if the text view is not the first responder.
Important: Calling any of the layout manager’s layout-causing methods between beginEditing
and endEditing
messages raises an exception. The NSLayoutManager
reference documentation and the NSLayoutManager.h
header file indicate which methods cause layout.
Listing 1 illustrates a situation in which the NSTextView
method scrollRangeToVisible:
forces layout to occur and raises an exception.
[[myTextView textStorage] beginEditing]; |
[[myTextView textStorage] replaceCharactersInRange:NSMakeRange(0,0) |
withString:@"Hello to you!"]; |
[myTextView scrollrangeToVisible:NSMakeRange(0,13)]; //BOOM |
[[myTextView textStorage] endEditing]; |
Scrolling a character range into visibility requires layout to be complete through that range so the text view can know where the range is located. But in Listing 1, the text storage is in batch-editing mode. It is in an inconsistent state, so the layout manager has no way to do layout at this time. Moving the scrollRangeToVisible:
call after endEditing
would solve the problem.
There are additional actions that you should take if you implement new user actions in a text view, such as a menu action or key binding method that changes the text. For example, you can modify the selected range of characters using the NSText
method setSelectedRange
, depending on the type of change performed by the command, using the results of the NSTextView
methods rangeForUserTextChange
, rangeForUserCharacterAttributeChange
, or rangeForUserParagraphAttributeChange
. For example, rangeForUserParagraphAttributeChange
returns the entire paragraph containing the original selection—that is the range affected if your action modifies paragraph attributes. Also, you should call textView:shouldChangeTextInRange:replacementString:
before you make the change and didChangeText
afterwards. These actions ensure that the correct text gets changed and the system sends the correct notifications and delegate messages to the text view’s delegate. See “Subclassing NSTextView” for more information.
There may be situations in which you need to force the text system to end editing programmatically so you can take some action dependent on notifications being sent. In such a case, you don’t need to modify the editing mechanism but simply stimulate its normal behavior.
To force the end of editing in a text view, which subsequently sends a textDidEndEditing:
notification message to its delegate, you can observe the window’s NSWindowDidResignKey
notification. Then, in the observer method, send makeFirstResponder:
to the window to finish any editing in progress while the window was active. Otherwise, the control that is currently being edited remains the first responder of the window and does not end editing.
Listing 2 presents an implementation of the textDidEndEditing:
delegate method that ends editing in an NSTableView
subclass. By default, when the user is editing a cell in a table view and presses Tab or Return, the field editor ends editing in the current cell and begins editing the next cell. In this case, you want to end editing altogether if the user presses Return. This method distinguishes which key the user pressed; for a Tab it does the normal behavior, and for Return it forces the end of editing completely by making the window first responder.
Listing 2 Forcing the end of editing
- (void)textDidEndEditing:(NSNotification *)notification { |
if([[[notification userInfo] valueForKey:@"NSTextMovement"] intValue] == |
NSReturnTextMovement) { |
NSMutableDictionary *newUserInfo; |
newUserInfo = [[NSMutableDictionary alloc] |
initWithDictionary:[notification userInfo]]; |
[newUserInfo setObject:[NSNumber numberWithInt:NSIllegalTextMovement] |
forKey:@"NSTextMovement"]; |
notification = [NSNotification notificationWithName:[notification name] |
object:[notification object] |
userInfo:newUserInfo]; |
[super textDidEndEditing:notification]; |
[newUserInfo release]; |
[[self window] makeFirstResponder:self]; |
} else { |
[super textDidEndEditing:notification]; |
} |
} |
© 2003, 2008 Apple Inc. All Rights Reserved. (Last updated: 2008-02-08)